]> granicus.if.org Git - postgresql/commitdiff
Don't share SSL_CTX between libpq connections.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 7 Oct 2016 09:20:39 +0000 (12:20 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 7 Oct 2016 09:20:39 +0000 (12:20 +0300)
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>

src/interfaces/libpq/fe-secure-openssl.c
src/test/ssl/Makefile
src/test/ssl/README
src/test/ssl/ServerSetup.pm
src/test/ssl/ssl/client+client_ca.crt [new file with mode: 0644]
src/test/ssl/t/001_ssltests.pl

index 2dcdce7265afbbec65ef4407ef02484167d80f5f..c83c1acae4aa17687d71e1cbf378e3458ebe1156 100644 (file)
@@ -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
index 3d992babff0cfe124169375b3a0e165c241706e7..2b04d825285ce1beeb0ce6f178d2b3eeaae5426a 100644 (file)
@@ -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
index 52bd68f49fade2a44098271d588a88059b7c06b7..50fa14e287eaaf56af733b96c40bb5ab62713100 100644 (file)
@@ -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
index 4e93184eb0357f747c50d229c6e07dbfe63e93b4..d312880f8b1a5ed6763ff54f55e7f62afe6e728e 100644 (file)
@@ -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 (file)
index 0000000..3caada6
--- /dev/null
@@ -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-----
index 80e8ea1fe79840327e5f538965f2989f3ddaa3d0..dc8e064b257d46a51360042a8c2c0f874503d2ac 100644 (file)
@@ -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");