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>
This commit is contained in:
Heikki Linnakangas 2016-10-07 12:20:39 +03:00
parent d7eb76b908
commit 8bb14cdd33
6 changed files with 212 additions and 238 deletions

View 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,35 +1072,43 @@ 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
* combination if there is colon present - we don't support files with
@ -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

View 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

View 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

View 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";

View File

@ -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-----

View 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");