From: Heikki Linnakangas Date: Fri, 7 Oct 2016 09:20:39 +0000 (+0300) Subject: Don't share SSL_CTX between libpq connections. X-Git-Tag: REL_10_BETA1~1602 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8bb14cdd33deecc6977cf5638f73e80f76ab658b;p=postgresql Don't share SSL_CTX between libpq connections. There were several issues with the old coding: 1. There was a race condition, if two threads opened a connection at the same time. We used a mutex around SSL_CTX_* calls, but that was not enough, e.g. if one thread SSL_CTX_load_verify_locations() with one path, and another thread set it with a different path, before the first thread got to establish the connection. 2. Opening two different connections, with different sslrootcert settings, seemed to fail outright with "SSL error: block type is not 01". Not sure why. 3. We created the SSL object, before calling SSL_CTX_load_verify_locations and SSL_CTX_use_certificate_chain_file on the SSL context. That was wrong, because the options set on the SSL context are propagated to the SSL object, when the SSL object is created. If they are set after the SSL object has already been created, they won't take effect until the next connection. (This is bug #14329) At least some of these could've been fixed while still using a shared context, but it would've been more complicated and error-prone. To keep things simple, let's just use a separate SSL context for each connection, and accept the overhead. Backpatch to all supported versions. Report, analysis and test case by Kacper Zuk. Discussion: <20160920101051.1355.79453@wrigleys.postgresql.org> --- diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index 2dcdce7265..c83c1acae4 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -80,12 +80,7 @@ static int my_SSL_set_fd(PGconn *conn, int fd); static bool pq_init_ssl_lib = true; static bool pq_init_crypto_lib = true; -/* - * SSL_context is currently shared between threads and therefore we need to be - * careful to lock around any usage of it when providing thread safety. - * ssl_config_mutex is the mutex that we use to protect it. - */ -static SSL_CTX *SSL_context = NULL; +static bool ssl_lib_initialized = false; #ifdef ENABLE_THREAD_SAFETY static long ssl_open_connections = 0; @@ -133,44 +128,9 @@ pgtls_open_client(PGconn *conn) /* First time through? */ if (conn->ssl == NULL) { -#ifdef ENABLE_THREAD_SAFETY - int rc; -#endif - -#ifdef ENABLE_THREAD_SAFETY - if ((rc = pthread_mutex_lock(&ssl_config_mutex))) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not acquire mutex: %s\n"), strerror(rc)); - return PGRES_POLLING_FAILED; - } -#endif - /* Create a connection-specific SSL object */ - if (!(conn->ssl = SSL_new(SSL_context)) || - !SSL_set_app_data(conn->ssl, conn) || - !my_SSL_set_fd(conn, conn->sock)) - { - char *err = SSLerrmessage(ERR_get_error()); - - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not establish SSL connection: %s\n"), - err); - SSLerrfree(err); -#ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&ssl_config_mutex); -#endif - pgtls_close(conn); - - return PGRES_POLLING_FAILED; - } - conn->ssl_in_use = true; - -#ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&ssl_config_mutex); -#endif - /* - * Load client certificate, private key, and trusted CA certs. + * Create a connection-specific SSL object, and load client certificate, + * private key, and trusted CA certs. */ if (initialize_SSL(conn) != 0) { @@ -771,8 +731,7 @@ pq_lockingcallback(int mode, int n, const char *file, int line) #endif /* ENABLE_THREAD_SAFETY && HAVE_CRYPTO_LOCK */ /* - * Initialize SSL system, in particular creating the SSL_context object - * that will be shared by all SSL-using connections in this process. + * Initialize SSL library. * * In threadsafe mode, this includes setting up libcrypto callback functions * to do thread locking. @@ -851,7 +810,7 @@ pgtls_init(PGconn *conn) #endif /* HAVE_CRYPTO_LOCK */ #endif /* ENABLE_THREAD_SAFETY */ - if (!SSL_context) + if (!ssl_lib_initialized) { if (pq_init_ssl_lib) { @@ -863,36 +822,7 @@ pgtls_init(PGconn *conn) SSL_load_error_strings(); #endif } - - /* - * We use SSLv23_method() because it can negotiate use of the highest - * mutually supported protocol version, while alternatives like - * TLSv1_2_method() permit only one specific version. Note that we - * don't actually allow SSL v2 or v3, only TLS protocols (see below). - */ - SSL_context = SSL_CTX_new(SSLv23_method()); - if (!SSL_context) - { - char *err = SSLerrmessage(ERR_get_error()); - - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not create SSL context: %s\n"), - err); - SSLerrfree(err); -#ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&ssl_config_mutex); -#endif - return -1; - } - - /* Disable old protocol versions */ - SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); - - /* - * Disable OpenSSL's moving-write-buffer sanity check, because it - * causes unnecessary failures in nonblocking send cases. - */ - SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + ssl_lib_initialized = true; } #ifdef ENABLE_THREAD_SAFETY @@ -936,9 +866,8 @@ destroy_ssl_system(void) CRYPTO_set_id_callback(NULL); /* - * We don't free the lock array or the SSL_context. If we get another - * connection in this process, we will just re-use them with the - * existing mutexes. + * We don't free the lock array. If we get another connection in + * this process, we will just re-use them with the existing mutexes. * * This means we leak a little memory on repeated load/unload of the * library. @@ -950,26 +879,22 @@ destroy_ssl_system(void) } /* - * Initialize (potentially) per-connection SSL data, namely the - * client certificate, private key, and trusted CA certs. - * - * conn->ssl must already be created. It receives the connection's client - * certificate and private key. Note however that certificates also get - * loaded into the SSL_context object, and are therefore accessible to all - * connections in this process. This should be OK as long as there aren't - * any hash collisions among the certs. + * Create per-connection SSL object, and load the client certificate, + * private key, and trusted CA certs. * * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage). */ static int initialize_SSL(PGconn *conn) { + SSL_CTX *SSL_context; struct stat buf; char homedir[MAXPGPATH]; char fnbuf[MAXPGPATH]; char sebuf[256]; bool have_homedir; bool have_cert; + bool have_rootcert; EVP_PKEY *pkey = NULL; /* @@ -985,6 +910,123 @@ initialize_SSL(PGconn *conn) else /* won't need it */ have_homedir = false; + /* + * Create a new SSL_CTX object. + * + * We used to share a single SSL_CTX between all connections, but it was + * complicated if connections used different certificates. So now we create + * a separate context for each connection, and accept the overhead. + */ + SSL_context = SSL_CTX_new(SSLv23_method()); + if (!SSL_context) + { + char *err = SSLerrmessage(ERR_get_error()); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not create SSL context: %s\n"), + err); + SSLerrfree(err); + return -1; + } + + /* Disable old protocol versions */ + SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + + /* + * Disable OpenSSL's moving-write-buffer sanity check, because it + * causes unnecessary failures in nonblocking send cases. + */ + SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + /* + * If the root cert file exists, load it so we can perform certificate + * verification. If sslmode is "verify-full" we will also do further + * verification after the connection has been completed. + */ + if (conn->sslrootcert && strlen(conn->sslrootcert) > 0) + strlcpy(fnbuf, conn->sslrootcert, sizeof(fnbuf)); + else if (have_homedir) + snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE); + else + fnbuf[0] = '\0'; + + if (fnbuf[0] != '\0' && + stat(fnbuf, &buf) == 0) + { + X509_STORE *cvstore; + + if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1) + { + char *err = SSLerrmessage(ERR_get_error()); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not read root certificate file \"%s\": %s\n"), + fnbuf, err); + SSLerrfree(err); + SSL_CTX_free(SSL_context); + return -1; + } + + if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL) + { + if (conn->sslcrl && strlen(conn->sslcrl) > 0) + strlcpy(fnbuf, conn->sslcrl, sizeof(fnbuf)); + else if (have_homedir) + snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE); + else + fnbuf[0] = '\0'; + + /* Set the flags to check against the complete CRL chain */ + if (fnbuf[0] != '\0' && + X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1) + { + /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */ +#ifdef X509_V_FLAG_CRL_CHECK + X509_STORE_set_flags(cvstore, + X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); +#else + char *err = SSLerrmessage(ERR_get_error()); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"), + fnbuf); + SSLerrfree(err); + SSL_CTX_free(SSL_context); + return -1; +#endif + } + /* if not found, silently ignore; we do not require CRL */ + } + have_rootcert = true; + } + else + { + /* + * stat() failed; assume root file doesn't exist. If sslmode is + * verify-ca or verify-full, this is an error. Otherwise, continue + * without performing any server cert verification. + */ + if (conn->sslmode[0] == 'v') /* "verify-ca" or "verify-full" */ + { + /* + * The only way to reach here with an empty filename is if + * pqGetHomeDirectory failed. That's a sufficiently unusual case + * that it seems worth having a specialized error message for it. + */ + if (fnbuf[0] == '\0') + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not get home directory to locate root certificate file\n" + "Either provide the file or change sslmode to disable server certificate verification.\n")); + else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("root certificate file \"%s\" does not exist\n" + "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf); + SSL_CTX_free(SSL_context); + return -1; + } + have_rootcert = false; + } + /* Read the client certificate file */ if (conn->sslcert && strlen(conn->sslcert) > 0) strlcpy(fnbuf, conn->sslcert, sizeof(fnbuf)); @@ -1010,6 +1052,7 @@ initialize_SSL(PGconn *conn) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not open certificate file \"%s\": %s\n"), fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf))); + SSL_CTX_free(SSL_context); return -1; } have_cert = false; @@ -1017,31 +1060,10 @@ initialize_SSL(PGconn *conn) else { /* - * Cert file exists, so load it. Since OpenSSL doesn't provide the - * equivalent of "SSL_use_certificate_chain_file", we actually have to - * load the file twice. The first call loads any extra certs after - * the first one into chain-cert storage associated with the - * SSL_context. The second call loads the first cert (only) into the - * SSL object, where it will be correctly paired with the private key - * we load below. We do it this way so that each connection - * understands which subject cert to present, in case different - * sslcert settings are used for different connections in the same - * process. - * - * NOTE: This function may also modify our SSL_context and therefore - * we have to lock around this call and any places where we use the - * SSL_context struct. + * Cert file exists, so load it. Since OpenSSL doesn't provide the + * equivalent of "SSL_use_certificate_chain_file", we have to load + * it into the SSL context, rather than the SSL object. */ -#ifdef ENABLE_THREAD_SAFETY - int rc; - - if ((rc = pthread_mutex_lock(&ssl_config_mutex))) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not acquire mutex: %s\n"), strerror(rc)); - return -1; - } -#endif if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1) { char *err = SSLerrmessage(ERR_get_error()); @@ -1050,34 +1072,42 @@ initialize_SSL(PGconn *conn) libpq_gettext("could not read certificate file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); - -#ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&ssl_config_mutex); -#endif - return -1; - } - - if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1) - { - char *err = SSLerrmessage(ERR_get_error()); - - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not read certificate file \"%s\": %s\n"), - fnbuf, err); - SSLerrfree(err); -#ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&ssl_config_mutex); -#endif + SSL_CTX_free(SSL_context); return -1; } /* need to load the associated private key, too */ have_cert = true; + } -#ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&ssl_config_mutex); -#endif + /* + * The SSL context is now loaded with the correct root and client certificates. + * Create a connection-specific SSL object. The private key is loaded directly + * into the SSL object. (We could load the private key into the context, too, but + * we have done it this way historically, and it doesn't really matter.) + */ + if (!(conn->ssl = SSL_new(SSL_context)) || + !SSL_set_app_data(conn->ssl, conn) || + !my_SSL_set_fd(conn, conn->sock)) + { + char *err = SSLerrmessage(ERR_get_error()); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not establish SSL connection: %s\n"), + err); + SSLerrfree(err); + SSL_CTX_free(SSL_context); + return -1; } + conn->ssl_in_use = true; + + /* + * SSL contexts are reference counted by OpenSSL. We can free it as soon as we + * have created the SSL object, and it will stick around for as long as it's + * actually needed. + */ + SSL_CTX_free(SSL_context); + SSL_context = NULL; /* * Read the SSL key. If a key is specified, treat it as an engine:key @@ -1236,109 +1266,10 @@ initialize_SSL(PGconn *conn) } /* - * If the root cert file exists, load it so we can perform certificate - * verification. If sslmode is "verify-full" we will also do further - * verification after the connection has been completed. + * If a root cert was loaded, also set our certificate verification callback. */ - if (conn->sslrootcert && strlen(conn->sslrootcert) > 0) - strlcpy(fnbuf, conn->sslrootcert, sizeof(fnbuf)); - else if (have_homedir) - snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE); - else - fnbuf[0] = '\0'; - - if (fnbuf[0] != '\0' && - stat(fnbuf, &buf) == 0) - { - X509_STORE *cvstore; - -#ifdef ENABLE_THREAD_SAFETY - int rc; - - if ((rc = pthread_mutex_lock(&ssl_config_mutex))) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not acquire mutex: %s\n"), strerror(rc)); - return -1; - } -#endif - if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1) - { - char *err = SSLerrmessage(ERR_get_error()); - - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not read root certificate file \"%s\": %s\n"), - fnbuf, err); - SSLerrfree(err); -#ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&ssl_config_mutex); -#endif - return -1; - } - - if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL) - { - if (conn->sslcrl && strlen(conn->sslcrl) > 0) - strlcpy(fnbuf, conn->sslcrl, sizeof(fnbuf)); - else if (have_homedir) - snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE); - else - fnbuf[0] = '\0'; - - /* Set the flags to check against the complete CRL chain */ - if (fnbuf[0] != '\0' && - X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1) - { - /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */ -#ifdef X509_V_FLAG_CRL_CHECK - X509_STORE_set_flags(cvstore, - X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); -#else - char *err = SSLerrmessage(ERR_get_error()); - - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"), - fnbuf); - SSLerrfree(err); -#ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&ssl_config_mutex); -#endif - return -1; -#endif - } - /* if not found, silently ignore; we do not require CRL */ - } -#ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&ssl_config_mutex); -#endif - + if (have_rootcert) SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb); - } - else - { - /* - * stat() failed; assume root file doesn't exist. If sslmode is - * verify-ca or verify-full, this is an error. Otherwise, continue - * without performing any server cert verification. - */ - if (conn->sslmode[0] == 'v') /* "verify-ca" or "verify-full" */ - { - /* - * The only way to reach here with an empty filename is if - * pqGetHomeDirectory failed. That's a sufficiently unusual case - * that it seems worth having a specialized error message for it. - */ - if (fnbuf[0] == '\0') - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not get home directory to locate root certificate file\n" - "Either provide the file or change sslmode to disable server certificate verification.\n")); - else - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("root certificate file \"%s\" does not exist\n" - "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf); - return -1; - } - } /* * If the OpenSSL version used supports it (from 1.0.0 on) and the user diff --git a/src/test/ssl/Makefile b/src/test/ssl/Makefile index 3d992babff..2b04d82528 100644 --- a/src/test/ssl/Makefile +++ b/src/test/ssl/Makefile @@ -23,7 +23,8 @@ SSLFILES := $(CERTIFICATES:%=ssl/%.key) $(CERTIFICATES:%=ssl/%.crt) \ ssl/client.crl ssl/server.crl ssl/root.crl \ ssl/both-cas-1.crt ssl/both-cas-2.crt \ ssl/root+server_ca.crt ssl/root+server.crl \ - ssl/root+client_ca.crt ssl/root+client.crl + ssl/root+client_ca.crt ssl/root+client.crl \ + ssl/client+client_ca.crt # This target generates all the key and certificate files. sslfiles: $(SSLFILES) @@ -99,6 +100,9 @@ ssl/root+server_ca.crt: ssl/root_ca.crt ssl/server_ca.crt ssl/root+client_ca.crt: ssl/root_ca.crt ssl/client_ca.crt cat $^ > $@ +ssl/client+client_ca.crt: ssl/client.crt ssl/client_ca.crt + cat $^ > $@ + #### CRLs ssl/client.crl: ssl/client-revoked.crt diff --git a/src/test/ssl/README b/src/test/ssl/README index 52bd68f49f..50fa14e287 100644 --- a/src/test/ssl/README +++ b/src/test/ssl/README @@ -65,6 +65,10 @@ root+server_ca root+client_ca Contains root_crt and client_ca.crt. For use as server's "ssl_ca_file". +client+client_ca + Contains client.crt and client_ca.crt in that order. For use as client's + certificate chain. + There are also CRLs for each of the CAs: root.crl, server.crl and client.crl. For convenience, all of these keypairs and certificates are included in the diff --git a/src/test/ssl/ServerSetup.pm b/src/test/ssl/ServerSetup.pm index 4e93184eb0..d312880f8b 100644 --- a/src/test/ssl/ServerSetup.pm +++ b/src/test/ssl/ServerSetup.pm @@ -75,6 +75,7 @@ sub configure_test_server_for_ssl copy_files("ssl/server-*.key", $pgdata); chmod(0600, glob "$pgdata/server-*.key") or die $!; copy_files("ssl/root+client_ca.crt", $pgdata); + copy_files("ssl/root_ca.crt", $pgdata); copy_files("ssl/root+client.crl", $pgdata); # Only accept SSL connections from localhost. Our tests don't depend on this @@ -101,13 +102,14 @@ sub switch_server_cert { my $node = $_[0]; my $certfile = $_[1]; + my $cafile = $_[2] || "root+client_ca"; my $pgdata = $node->data_dir; - diag "Restarting server with certfile \"$certfile\"..."; + diag "Restarting server with certfile \"$certfile\" and cafile \"$cafile\"..."; open SSLCONF, ">$pgdata/sslconfig.conf"; print SSLCONF "ssl=on\n"; - print SSLCONF "ssl_ca_file='root+client_ca.crt'\n"; + print SSLCONF "ssl_ca_file='$cafile.crt'\n"; print SSLCONF "ssl_cert_file='$certfile.crt'\n"; print SSLCONF "ssl_key_file='$certfile.key'\n"; print SSLCONF "ssl_crl_file='root+client.crl'\n"; diff --git a/src/test/ssl/ssl/client+client_ca.crt b/src/test/ssl/ssl/client+client_ca.crt new file mode 100644 index 0000000000..3caada693d --- /dev/null +++ b/src/test/ssl/ssl/client+client_ca.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIBxzCCATACAQEwDQYJKoZIhvcNAQEFBQAwQjFAMD4GA1UEAww3VGVzdCBDQSBm +b3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IGNsaWVudCBjZXJ0czAe +Fw0xNjA5MTIxNjMwMDFaFw00NDAxMjkxNjMwMDFaMBYxFDASBgNVBAMMC3NzbHRl +c3R1c2VyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN3RFl8VWMEBN1Qas0 +w1CFcXdDEbKVNSPsqWHzHIEPoGJv+eUIBK2lQ/Ce8nRCdelO50RsmlbcXBIrjVl6 +BN0RmEeEVclgCdiamYN53LBdc5KWKpKCKn45lCtlZodWt0hNNx1pAmh85jDKpoO9 +ErbCnSU1wODPqnOzdkLU7jBu5QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABUz+vnu +dD1Q1N/Ezs5DzJeQDtiJb9PNzBHAUPQoXeLvuITcDdyYWc18Yi4fX7gwyD42q2iu +1I0hmm2bNJfujsGbvGYFLuQ4hC2ucAAj2Gm681GhhaNYtfsfHYm9R8GRZFvp40oj +qXpkDkYsPdyVxUyoxJ+M0Ub5VC/k1pQNtIaq +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICCDCCAXGgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBAMT4wPAYDVQQDDDVUZXN0 +IHJvb3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBzdWl0 +ZTAeFw0xNjA5MTIxNjMwMDFaFw00NDAxMjkxNjMwMDFaMEIxQDA+BgNVBAMMN1Rl +c3QgQ0EgZm9yIFBvc3RncmVTUUwgU1NMIHJlZ3Jlc3Npb24gdGVzdCBjbGllbnQg +Y2VydHMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMI2MXWSb8TZnCLVNYJ+ +19b4noxRmaR1W2zUxl4aTMfiPt9cK06lNY39EPBfjmb7hjxD76w8fLoV/aZ0gOgd +JXFRZvIg7SyM7QVFma0AJAIZayes+ba1odEmBEi378g0mLrjCLqZtBVHfvJxL/6x +6/flSTAn/+09vtELvvLWBePZAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZI +hvcNAQEFBQADgYEAlGC24V2TsiSlo9RIboBZTZqd0raUpKkmVbkwKyqcmecoFfCI +TCmoyJLYyUL5/e3dtn/cGDcaqxaO3qxnstxVEMSrlCGfZdZJ2oouXZMpDy9CkeOM +ypCCx9pc4EmP3mvu64f21+dNCXlhM36pZ1IokeS5jk2FIHUda+m5jlk5o6I= +-----END CERTIFICATE----- diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl index 80e8ea1fe7..dc8e064b25 100644 --- a/src/test/ssl/t/001_ssltests.pl +++ b/src/test/ssl/t/001_ssltests.pl @@ -2,7 +2,7 @@ use strict; use warnings; use PostgresNode; use TestLib; -use Test::More tests => 38; +use Test::More tests => 40; use ServerSetup; use File::Copy; @@ -239,3 +239,11 @@ test_connect_fails( test_connect_fails( "user=ssltestuser sslcert=ssl/client-revoked.crt sslkey=ssl/client-revoked.key" ); + +# intermediate client_ca.crt is provided by client, and isn't in server's ssl_ca_file +switch_server_cert($node, 'server-cn-only', 'root_ca'); +$common_connstr = +"user=ssltestuser dbname=certdb sslkey=ssl/client.key sslrootcert=ssl/root+server_ca.crt hostaddr=$SERVERHOSTADDR"; + +test_connect_ok("sslmode=require sslcert=ssl/client+client_ca.crt"); +test_connect_fails("sslmode=require sslcert=ssl/client.crt");