From 9a8e6d11d6fdbfed907de2c999129ba8105d6aff Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Wed, 8 Feb 2023 17:39:25 +0200 Subject: [PATCH 1/1] Don't advertise channel binding to client when we cannot support it. With certain kinds of TLS server certificates, we cannot do SCRAM channel binding (tls-server-end-point), because the hash algorithm to use is not well defined, or is not supported by OpenSSL. If you try to use channel binding with such a certificate, you get an obscure error in the client during the authentication: could not find digest for NID UNDEF To fix, don't advertise SCRAM-SHA-256-PLUS if we cannot calculate the hash. Print a LOG message once on server startup, and at config reload, if the server is using such a certificate. To do that, calculate the certificate hash when the certificate is loaded, instead of when a connection is established. The availability of channel binding now depends on the installed certificate, so this removes HAVE_BE_TLS_GET_CERTIFICATE_HASH and replaces it with a new function, scram_channel_binding_possible(). If the server is compiled with a version of OpenSSL that doesn't have the X509_get_signature_nid() function, and hence cannot support our channel binding at all, it will just always return false. The libqq client code has a similar issue. It will choose SCRAM-SHA-256-PLUS if the server advertises it, even if it doesn't support the server certificate's signature algorithm, only to fail later in the authentication. Unfortunately, there isn't much we can do in the client: it cannot safely fall back to not using channel binding if it doesn't support the server's certificate's hashing algorithm, because that could lead to a downgrade attack. So in the client, just make the error message more clear. We could try harder to determine a hash algorithm if the certificate doesn't specify one unambiguously. For example, we could fall back to SHA-256. However, I'm not comfortable inventing new rules especially in back-branches. OpenSSL also has a function X509_digest_sig() that contains some fallback rules like that, but AFAICS it wasn't written with channel binding in mind, so I'm not confident switching to that either. In the future, we probably should switch to the tls-exporter channel binding type as specified in RFC 9266. Fixes bug #17760. We still don't support channel binding with RSA-PSS certificates, but at least we fall back more gracefully now. Reported-by: Gunnar "Nick" Bluth Discussion: https://www.postgresql.org/message-id/17760-b6c61e752ec07060@postgresql.org Backpatch: 11- (all supported versions) --- src/backend/libpq/auth-scram.c | 54 +++++++----- src/backend/libpq/be-secure-openssl.c | 102 ++++++++++++++++++++--- src/backend/libpq/be-secure.c | 1 + src/include/libpq/libpq-be.h | 9 +- src/interfaces/libpq/fe-auth-scram.c | 17 +++- src/interfaces/libpq/fe-secure-openssl.c | 5 +- src/test/ssl/conf/server-rsapss.config | 14 ++++ src/test/ssl/ssl/server-rsapss.crt | 21 +++++ src/test/ssl/ssl/server-rsapss.key | 28 +++++++ src/test/ssl/sslfiles.mk | 22 ++++- src/test/ssl/t/002_scram.pl | 14 ++++ 11 files changed, 242 insertions(+), 45 deletions(-) create mode 100644 src/test/ssl/conf/server-rsapss.config create mode 100644 src/test/ssl/ssl/server-rsapss.crt create mode 100644 src/test/ssl/ssl/server-rsapss.key diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index 4441e0d7745..fb39051bfa6 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -191,6 +191,36 @@ static char *scram_mock_salt(const char *username, pg_cryptohash_type hash_type, int key_length); +/* + * Is channel binding possible? + * + * SCRAM-SHA-256-PLUS in PostgreSQL implies the tls-server-end-point channel + * binding type, which is based on the hash of the server's certificate. Try + * to get the hash. It might not be available, if the certificate uses an + * unsupported or ambiguous digest algorithm. + * + * XXX: By supporting only tls-server-end-point, we are violating RFC 5802, + * which states that the server MUST support tls-unique channel binding type + * if channel binding is supported at all. We choose to violate the RFC + * because tls-unique was not supported by all versions of OpenSSL when this + * was implemented, and also requires Extended Master Key Extension [RFC7627] + * or TLS v1.3 to be secure. In the future, we should implement the + * tls-exporter channel binding type as defined in RFC9266. + */ +static bool +scram_channel_binding_possible(Port *port) +{ + size_t dummy_len; + + if (!port->ssl_in_use) + return false; + + if (be_tls_get_certificate_hash(port, &dummy_len) != NULL) + return true; + else + return false; +} + /* * Get a list of SASL mechanisms that this module supports. * @@ -203,17 +233,13 @@ scram_get_mechanisms(Port *port, StringInfo buf) { /* * Advertise the mechanisms in decreasing order of importance. So the - * channel-binding variants go first, if they are supported. Channel - * binding is only supported with SSL, and only if the SSL implementation - * has a function to get the certificate's hash. + * channel-binding variants go first, if they are supported. */ -#ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH - if (port->ssl_in_use) + if (scram_channel_binding_possible(port)) { appendStringInfoString(buf, SCRAM_SHA_256_PLUS_NAME); appendStringInfoChar(buf, '\0'); } -#endif appendStringInfoString(buf, SCRAM_SHA_256_NAME); appendStringInfoChar(buf, '\0'); } @@ -252,12 +278,9 @@ scram_init(Port *port, const char *selected_mech, const char *shadow_pass) * client nevertheless tries to select it, it's a protocol violation like * selecting any other SASL mechanism we don't support. */ -#ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH - if (strcmp(selected_mech, SCRAM_SHA_256_PLUS_NAME) == 0 && port->ssl_in_use) + if (strcmp(selected_mech, SCRAM_SHA_256_PLUS_NAME) == 0 && scram_channel_binding_possible(port)) state->channel_binding_in_use = true; - else -#endif - if (strcmp(selected_mech, SCRAM_SHA_256_NAME) == 0) + else if (strcmp(selected_mech, SCRAM_SHA_256_NAME) == 0) state->channel_binding_in_use = false; else ereport(ERROR, @@ -1005,14 +1028,12 @@ read_client_first_message(scram_state *state, const char *input) errmsg("malformed SCRAM message"), errdetail("The client selected SCRAM-SHA-256-PLUS, but the SCRAM message does not include channel binding data."))); -#ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH - if (state->port->ssl_in_use) + if (scram_channel_binding_possible(state->port)) ereport(ERROR, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("SCRAM channel binding negotiation error"), errdetail("The client supports SCRAM channel binding but thinks the server does not. " "However, this server does support channel binding."))); -#endif p++; if (*p != ',') ereport(ERROR, @@ -1301,7 +1322,6 @@ read_client_final_message(scram_state *state, const char *input) channel_binding = read_attr_value(&p, 'c'); if (state->channel_binding_in_use) { -#ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH const char *cbind_data = NULL; size_t cbind_data_len = 0; size_t cbind_header_len; @@ -1343,10 +1363,6 @@ read_client_final_message(scram_state *state, const char *input) ereport(ERROR, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("SCRAM channel binding check failed"))); -#else - /* shouldn't happen, because we checked this earlier already */ - elog(ERROR, "channel binding not supported by this build"); -#endif } else { diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index e3c7c12aa0e..751189600f8 100644 --- a/src/backend/libpq/be-secure-openssl.c +++ b/src/backend/libpq/be-secure-openssl.c @@ -61,6 +61,7 @@ static int my_sock_write(BIO *h, const char *buf, int size); static BIO_METHOD *my_BIO_s_socket(void); static int my_SSL_set_fd(Port *port, int fd); +static char *calculate_certificate_hash(X509 *cert, size_t *len, bool isServerStart); static DH *load_dh_file(char *filename, bool isServerStart); static DH *load_dh_buffer(const char *buffer, size_t len); static int ssl_external_passwd_cb(char *buf, int size, int rwflag, void *userdata); @@ -73,7 +74,11 @@ static const char *SSLerrmessage(unsigned long ecode); static char *X509_NAME_to_cstring(X509_NAME *name); +/* current SSL context, and hash of the server certificate in use in the context */ static SSL_CTX *SSL_context = NULL; +static char *ssl_cert_hash = NULL; +static int ssl_cert_hash_len = 0; + static bool SSL_initialized = false; static bool dummy_ssl_passwd_cb_called = false; static bool ssl_is_server_start; @@ -94,6 +99,9 @@ be_tls_init(bool isServerStart) SSL_CTX *context; int ssl_ver_min = -1; int ssl_ver_max = -1; + X509 *new_cert; + char *new_hash = NULL; + size_t new_hash_len = 0; /* This stuff need be done only once. */ if (!SSL_initialized) @@ -156,6 +164,30 @@ be_tls_init(bool isServerStart) if (!check_ssl_key_file_permissions(ssl_key_file, isServerStart)) goto error; + /* + * Pre-calculate the hash of the certificate, for use in channel binding. + */ + new_cert = SSL_CTX_get0_certificate(context); + if (new_cert == NULL) + { + /* + * The SSL_CTX_use_certificate_chain_file() call above succeeded, but + * there is no certificate in the context. Not clear if this can + * happen, but we certainly can't do tls-server-end-point channel + * binding without a certificate. + */ + new_hash = NULL; + new_hash_len = 0; + } + else + { + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(TopMemoryContext); + new_hash = calculate_certificate_hash(new_cert, &new_hash_len, isServerStart); + MemoryContextSwitchTo(oldcxt); + } + /* * OK, try to load the private key file. */ @@ -380,8 +412,12 @@ be_tls_init(bool isServerStart) */ if (SSL_context) SSL_CTX_free(SSL_context); + if (ssl_cert_hash) + pfree(ssl_cert_hash); SSL_context = context; + ssl_cert_hash = new_hash; + ssl_cert_hash_len = new_hash_len; /* * Set flag to remember whether CA store has been loaded into SSL_context. @@ -397,6 +433,8 @@ be_tls_init(bool isServerStart) error: if (context) SSL_CTX_free(context); + if (new_hash) + pfree(new_hash); return -1; } @@ -407,6 +445,9 @@ be_tls_destroy(void) SSL_CTX_free(SSL_context); SSL_context = NULL; ssl_loaded_verify_locations = false; + if (ssl_cert_hash) + pfree(ssl_cert_hash); + ssl_cert_hash = NULL; } int @@ -1429,11 +1470,29 @@ be_tls_get_peer_serial(Port *port, char *ptr, size_t len) ptr[0] = '\0'; } -#ifdef HAVE_X509_GET_SIGNATURE_NID char * be_tls_get_certificate_hash(Port *port, size_t *len) { - X509 *server_cert; + *len = ssl_cert_hash_len; + return ssl_cert_hash; +} + +/* + * Calculate hash of the given certificate, for use in tls-server-end-point + * channel binding. + * + * The returned hash is palloc'd, and its length is returned in *len. + * + * If the hash could not be calculated because the hash algorithm specified in + * the certificate's signature is not supported, or it doesn't unambiguously + * specify an algorithm to use, returns NULL and logs the error. On other + * less expected errors, ereports the error with FATAL or LOG depending on + * whether 'isServerStart' is true. + */ +static char * +calculate_certificate_hash(X509 *cert, size_t *len, bool isServerStart) +{ +#ifdef HAVE_X509_GET_SIGNATURE_NID char *cert_hash; const EVP_MD *algo_type = NULL; unsigned char hash[EVP_MAX_MD_SIZE]; /* size for SHA-512 */ @@ -1441,17 +1500,18 @@ be_tls_get_certificate_hash(Port *port, size_t *len) int algo_nid; *len = 0; - server_cert = SSL_get_certificate(port->ssl); - if (server_cert == NULL) - return NULL; /* * Get the signature algorithm of the certificate to determine the hash * algorithm to use for the result. */ - if (!OBJ_find_sigid_algs(X509_get_signature_nid(server_cert), - &algo_nid, NULL)) - elog(ERROR, "could not determine server certificate signature algorithm"); + if (OBJ_find_sigid_algs(X509_get_signature_nid(cert), + &algo_nid, NULL) == 0) + { + ereport(LOG, + (errmsg("channel binding is disabled; server certificate has unknown signature algorithm"))); + return NULL; + } /* * The TLS server's certificate bytes need to be hashed with SHA-256 if @@ -1461,6 +1521,10 @@ be_tls_get_certificate_hash(Port *port, size_t *len) */ switch (algo_nid) { + case NID_undef: + ereport(LOG, + (errmsg("channel binding is disabled; server certificate does not unambiguously specify a signature algorithm"))); + return NULL; case NID_md5: case NID_sha1: algo_type = EVP_sha256(); @@ -1468,22 +1532,34 @@ be_tls_get_certificate_hash(Port *port, size_t *len) default: algo_type = EVP_get_digestbynid(algo_nid); if (algo_type == NULL) - elog(ERROR, "could not find digest for NID %s", - OBJ_nid2sn(algo_nid)); + { + ereport(isServerStart ? FATAL : LOG, + errmsg("could not find digest for NID %s", + OBJ_nid2sn(algo_nid))); + return NULL; + } break; } /* generate and save the certificate hash */ - if (!X509_digest(server_cert, algo_type, hash, &hash_size)) - elog(ERROR, "could not generate server certificate hash"); + if (!X509_digest(cert, algo_type, hash, &hash_size)) + { + ereport(isServerStart ? FATAL : LOG, + (errmsg("channel binding is disabled; could not generate server certificate hash"))); + return NULL; + } + Assert(hash_size <= sizeof(hash)); cert_hash = palloc(hash_size); memcpy(cert_hash, hash, hash_size); *len = hash_size; return cert_hash; -} +#else + *len = 0; + return NULL; #endif +} /* * Convert an X509 subject name to a cstring. diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c index a0f70840183..8c991099784 100644 --- a/src/backend/libpq/be-secure.c +++ b/src/backend/libpq/be-secure.c @@ -47,6 +47,7 @@ bool ssl_passphrase_command_supports_reload; #ifdef USE_SSL bool ssl_loaded_verify_locations = false; +char *ssl_cert_hash = NULL; #endif /* GUC variable controlling SSL cipher list */ diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 8c70b2fd5be..dda299a241f 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -303,15 +303,10 @@ extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len); * tls-server-end-point. * * The result is a palloc'd hash of the server certificate with its - * size, and NULL if there is no certificate available. - * - * This is not supported with old versions of OpenSSL that don't have - * the X509_get_signature_nid() function. + * size, or NULL if there is no certificate available or we could not + * unambiguously calculate a hash for it. */ -#if defined(USE_OPENSSL) && defined(HAVE_X509_GET_SIGNATURE_NID) -#define HAVE_BE_TLS_GET_CERTIFICATE_HASH extern char *be_tls_get_certificate_hash(Port *port, size_t *len); -#endif /* init hook for SSL, the default sets the password callback if appropriate */ #ifdef USE_OPENSSL diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c index 9c42ea4f819..fc0a5cd264a 100644 --- a/src/interfaces/libpq/fe-auth-scram.c +++ b/src/interfaces/libpq/fe-auth-scram.c @@ -487,7 +487,22 @@ build_client_final_message(fe_scram_state *state) &cbind_data_len); if (cbind_data == NULL) { - /* error message is already set on error */ + /* + * Could not calculate hash of the server's certificate. Error + * message is already set by pgtls_get_peer_certificate_hash(). + * + * This is known to happen with certificates using 'rsassaPss', + * 'ED25519' and 'ED448' signatures, because they don't specify an + * unambiguous digest algorithm to use. The server should not + * offer PLUS-variants with such certificates, but older versions + * incorrectly did. If channel binding is required, we naturally + * must fail, but even if channel binding is merely preferred, we + * cannot safely fall back to disabling channel binding, because + * that could lead to a downgrade attack: A man-in-the-middle + * could intentionally offer us a certificate with an unrecognized + * signature algorithm, to force the client to disable channel + * binding. + */ termPQExpBuffer(&buf); return NULL; } diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index 983536de251..96168a8bd7e 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -389,7 +389,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) if (!OBJ_find_sigid_algs(X509_get_signature_nid(peer_cert), &algo_nid, NULL)) { - libpq_append_conn_error(conn, "could not determine server certificate signature algorithm"); + libpq_append_conn_error(conn, "could not determine signature algorithm to use with server's certificate; channel binding not possible"); return NULL; } @@ -401,6 +401,9 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) */ switch (algo_nid) { + case NID_undef: + libpq_append_conn_error(conn, "could not determine signature algorithm to use with server's certificate; channel binding not possible"); + return NULL; case NID_md5: case NID_sha1: algo_type = EVP_sha256(); diff --git a/src/test/ssl/conf/server-rsapss.config b/src/test/ssl/conf/server-rsapss.config new file mode 100644 index 00000000000..d4701bdb36b --- /dev/null +++ b/src/test/ssl/conf/server-rsapss.config @@ -0,0 +1,14 @@ +# An OpenSSL format CSR config file for creating a server certificate. +# +# This is identical to server-cn-only certificate, but we specify +# RSA-PSS as the algorithm on the command line. + +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] +CN = common-name.pg-ssltest.test +OU = PostgreSQL test suite + +# No Subject Alternative Names diff --git a/src/test/ssl/ssl/server-rsapss.crt b/src/test/ssl/ssl/server-rsapss.crt new file mode 100644 index 00000000000..bef29adb1c3 --- /dev/null +++ b/src/test/ssl/ssl/server-rsapss.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDczCCAioCFB6p3YqR83DVMn6wTP1AI9E3akswMD4GCSqGSIb3DQEBCjAxoA0w +CwYJYIZIAWUDBAIBoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFlAwQCAaIEAgIA3jBG +MSQwIgYDVQQDDBtjb21tb24tbmFtZS5wZy1zc2x0ZXN0LnRlc3QxHjAcBgNVBAsM +FVBvc3RncmVTUUwgdGVzdCBzdWl0ZTAeFw0yMzAyMDgxNDIzMzRaFw0yMzAzMTAx +NDIzMzRaMEYxJDAiBgNVBAMMG2NvbW1vbi1uYW1lLnBnLXNzbHRlc3QudGVzdDEe +MBwGA1UECwwVUG9zdGdyZVNRTCB0ZXN0IHN1aXRlMIIBIDALBgkqhkiG9w0BAQoD +ggEPADCCAQoCggEBALaoRpce0lzkRKERq4cUANEoiVtOPfKnG4n4cifMJYto+DlE +TIB5PfeNeuLoLdKnohYlICQDLuV7W3r93MoimZ8tbYt78OQ06G/6qtyc6FjlGTKP +6+ULV8AVBbhcyQPtKhpzq+WbX/6Lz4hVtms/aeElB60ZbAs6wzSt8XBTmfjnOrty +5w/k2/+/X0DzRE1aYTnVlFTlVyuF1Th7Psm1EOfvAdjbVLCso4Ttpsixq0ILarAm +xQfc5cO6/dXRDsjCnly+5CVNWYCE1+AB4G9NvklORLApkdc4UBjxFpDOoh5pNHZa +ofExUthbGwyUfYOwGDPTPNYeYVshUZlAvgTJIrMCAwEAATA+BgkqhkiG9w0BAQow +MaANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgGiBAIC +AN4DggEBAIyTGMj5Z+Rl/lhvPkLM5TAtl/pGP23wHa6hsf+SpgwPGFQe5qC5Tbrd ++w/5KK5mh40+QMWGNo+kaDGJY3N+WV69Vk4aMvKvxVIGgH5itwvBE/emYFThbMmw +sGlW9f33+Edb2pEAEGEeb/AcesCYCF0tptyD1FphGVJaVFxo9uwwI0R3t/Ogok5H +576WHz6L56/Bqx5qpF+6h3cSDom3uGS0BYJlKBuDG3F89gNSBsixhfPHaIZxy+Jc +x5Tedp1sAqvxOQ1lez9LMPxdYAo3Qubw7c8H7bzyauiZnTt0xuTwOPJGFxwp5LMN +7+KYwyAzsHaglC/1ySPHAfsg0Xkq4Q0= +-----END CERTIFICATE----- diff --git a/src/test/ssl/ssl/server-rsapss.key b/src/test/ssl/ssl/server-rsapss.key new file mode 100644 index 00000000000..71a1dcebd33 --- /dev/null +++ b/src/test/ssl/ssl/server-rsapss.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADALBgkqhkiG9w0BAQoEggSqMIIEpgIBAAKCAQEAtqhGlx7SXOREoRGr +hxQA0SiJW0498qcbifhyJ8wli2j4OURMgHk994164ugt0qeiFiUgJAMu5Xtbev3c +yiKZny1ti3vw5DTob/qq3JzoWOUZMo/r5QtXwBUFuFzJA+0qGnOr5Ztf/ovPiFW2 +az9p4SUHrRlsCzrDNK3xcFOZ+Oc6u3LnD+Tb/79fQPNETVphOdWUVOVXK4XVOHs+ +ybUQ5+8B2NtUsKyjhO2myLGrQgtqsCbFB9zlw7r91dEOyMKeXL7kJU1ZgITX4AHg +b02+SU5EsCmR1zhQGPEWkM6iHmk0dlqh8TFS2FsbDJR9g7AYM9M81h5hWyFRmUC+ +BMkiswIDAQABAoIBAQCA4h3lFf9zQjJWwKQajPfCnerFvWCirl0VLnRGNpsrUmcL +6XBdmq8p1O+n2hIzOMt6+rlCD+jwTemP0D8RdfaVxbUtHcgtDlQQZ3xWbHTZ/NWZ +wsm9dRJ2256MpxPDrH3v6jyZp9fFQZuvZGNyK22nSryu3GhLSCxjKHVbP/I+GKE3 +XUqN8coC9w0IznlURzxOCtN1MQff7ZAriXRtAMX0aWSzdkw/h2MkWsWGyB6ZpevA +iWKqGSzBuQirYcwvk3Ne6IqOU83QRbLO4O7y2IBiwEz+BDB1l0/oIV3mwx2+4qLJ +8HlFeAhPoCYFD/U3w+ZeQHevU8Y6+lFt6YqcGXfZAoGBANwP6+4gQkO+fRg13ncd +7plWuebeEi/GZNpIk36/+U14Z88sZ0apB8szmkYrebDw1aDAsMdg313LlRQdbjzS +9q3Xwx6Zso0CGE+qZ53BByImIcpcOqyuy6XSjd6buAK1mIGtUOvMEQAhCKaKzGZH +mASN6aQazEPP+bLL43GhTU/9AoGBANR8lFHxMFf0uUX0F9Q0yxcmUGkW2bmHQXQl +jnSCGdvMZzi21VLN4MjoLnKUsfo63THFZ0IshNeZbcMfA8wts26cPwJPE/EUppwJ +Okdatp4zb0Pm5rRudhnpMvNELNjIxKSQ1Gpf3eXvpegE97nsApAvO7NzEA0yfNJ9 +hO7dqYRvAoGBAM2adpUqQJ8op5nqIqrqJVXQyKniC93lH68uJdhDprpx55OR6gAD +x0tcMCSlU/I0YXPq9H+ji64HmoaLpMZhWKY9s4iwkzjZnzs3e83RXkfSlrmJiCx0 +t8J7QZ+dn8OwRQfLlZJpO+0B8CIiBiz9cRO4P3Xar8Qf2Szq/9rXmPNFAoGBALlu +HmGEAZRAVI7ffQwLJlCFdxa+gjKN/mjFfZPfFYHi1xF/PJqOX+Pz+tSzPr4IMJAG +nR36i9M6abclkcLU/wlAARyateRlCSCSTPGIEGXurOKs1hgPbbEe+P+iNyDX1ANp +AHX0Q0kt4bKg4y6072e0UH1BpHhf3t4x+5gYiJ6xAoGBAJdD0GxLpe3AyG577Oo6 +x30W0/l4A4fPPkdRA+9zukt9Ybgs/KnoHzydBAqbuNBlj9DzqRuXlZA+zktJdyY1 +HC2oQ8qem7HbzbD10wLMN56VZWEohhEgw5A4EWdIcwYRKmIAliGTwyuJ+TQ4wl0x ++AVn5Ma2QzxTvU1Rrw0ok0w5 +-----END PRIVATE KEY----- diff --git a/src/test/ssl/sslfiles.mk b/src/test/ssl/sslfiles.mk index 5d9dc09a4b0..a565e90e4f1 100644 --- a/src/test/ssl/sslfiles.mk +++ b/src/test/ssl/sslfiles.mk @@ -37,13 +37,17 @@ CLIENTS := client client-dn client-revoked client_ext client-long \ client-revoked-utf8 # -# To add a new non-standard key, add it to SPECIAL_KEYS and then add a recipe -# for creating it to the "Special-case keys" section below. +# To add a new non-standard certificate, add it to SPECIAL_CERTS and then add +# a recipe for creating it to the "Special-case certificates" section below. # +SPECIAL_CERTS := ssl/server-rsapss.crt + +# Likewise for non-standard keys SPECIAL_KEYS := ssl/server-password.key \ ssl/client-der.key \ ssl/client-encrypted-pem.key \ - ssl/client-encrypted-der.key + ssl/client-encrypted-der.key \ + ssl/server-rsapss.key # # These files are just concatenations of other files. You can add new ones to @@ -66,7 +70,9 @@ CRLS := ssl/root.crl \ ssl/client.crl \ ssl/server.crl -SSLFILES := $(STANDARD_CERTS) $(STANDARD_KEYS) $(SPECIAL_KEYS) $(COMBINATIONS) $(CRLS) +SSLFILES := $(STANDARD_CERTS) $(STANDARD_KEYS) \ + $(SPECIAL_CERTS) $(SPECIAL_KEYS) \ + $(COMBINATIONS) $(CRLS) SSLDIRS := ssl/client-crldir \ ssl/server-crldir \ ssl/root+client-crldir \ @@ -86,6 +92,10 @@ sslfiles: $(SSLFILES) $(SSLDIRS) ssl/root_ca.crt: ssl/root_ca.key conf/root_ca.config $(OPENSSL) req -new -x509 -config conf/root_ca.config -days 10000 -key $< -out $@ +# Certificate using RSA-PSS algorithm. Also self-signed. +ssl/server-rsapss.crt: ssl/server-rsapss.key conf/server-rsapss.config + $(OPENSSL) req -new -x509 -config conf/server-rsapss.config -key $< -out $@ + # # Special-case keys # @@ -96,6 +106,10 @@ ssl/root_ca.crt: ssl/root_ca.key conf/root_ca.config ssl/server-password.key: ssl/server-cn-only.key $(OPENSSL) rsa -aes256 -in $< -out $@ -passout 'pass:secret1' +# Key that uses the RSA-PSS algorithm +ssl/server-rsapss.key: + $(OPENSSL) genpkey -algorithm rsa-pss -out $@ + # DER-encoded version of client.key ssl/client-der.key: ssl/client.key $(OPENSSL) rsa -in $< -outform DER -out $@ diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl index 0f3d180cfa9..a14fa3cb09b 100644 --- a/src/test/ssl/t/002_scram.pl +++ b/src/test/ssl/t/002_scram.pl @@ -136,4 +136,18 @@ $node->connect_ok( qr/connection authenticated: identity="ssltestuser" method=scram-sha-256/ ]); +# Now test with a server certificate that uses the RSA-PSS algorithm. It +# cannot be used for channel binding currently, but check that connecting +# without channel binding is still possible. (see bug #17760) +switch_server_cert($node, certfile => 'server-rsapss'); +$node->connect_fails( + "$common_connstr user=ssltestuser channel_binding=require", + "SCRAM with SSL and channel_binding=require, server certificate uses 'rsassaPss'", + expected_stderr => + qr/channel binding is required, but server did not offer an authentication method that supports channel binding/ + ); +$node->connect_ok( + "$common_connstr user=ssltestuser channel_binding=prefer", + "SCRAM with SSL and channel_binding=prefer, server certificate uses 'rsassaPss'"); + done_testing(); -- 2.30.2