*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.133 2010/05/25 22:03:27 tgl Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.134 2010/05/26 21:39:27 tgl Exp $
*
* NOTES
*
* We don't provide informational callbacks here (like
- * info_cb() in be-secure.c), since there's mechanism to
- * display that information to the client.
+ * info_cb() in be-secure.c), since there's no good mechanism to
+ * display such information to the user.
*
*-------------------------------------------------------------------------
*/
#ifdef USE_SSL
#include <openssl/ssl.h>
-#include <openssl/bio.h>
#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
#include <openssl/conf.h>
#endif
#define ROOT_CRL_FILE "root.crl"
#endif
-#ifndef HAVE_ERR_SET_MARK
-/* These don't exist in OpenSSL before 0.9.8 */
-#define ERR_set_mark() ((void) 0)
-#define ERR_pop_to_mark() ((void) 0)
-#endif
-
static bool verify_peer_name_matches_certificate(PGconn *);
static int verify_cb(int ok, X509_STORE_CTX *ctx);
-static int client_cert_cb(SSL *, X509 **, EVP_PKEY **);
static int init_ssl_system(PGconn *conn);
static void destroy_ssl_system(void);
-static int initialize_SSL(PGconn *);
+static int initialize_SSL(PGconn *conn);
static void destroySSL(void);
static PostgresPollingStatusType open_client_SSL(PGconn *);
static void close_SSL(PGconn *);
}
/*
- * Initialize global context
+ * Initialize global SSL context
*/
int
pqsecure_initialize(PGconn *conn)
int r = 0;
#ifdef USE_SSL
- r = initialize_SSL(conn);
+ r = init_ssl_system(conn);
#endif
return r;
}
/*
- * Attempt to negotiate secure session.
+ * Begin or continue negotiating a secure session.
*/
PostgresPollingStatusType
pqsecure_open_client(PGconn *conn)
/* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
conn->sigpipe_flag = false;
+ /* Create a connection-specific SSL object */
if (!(conn->ssl = SSL_new(SSL_context)) ||
!SSL_set_app_data(conn->ssl, conn) ||
!SSL_set_fd(conn->ssl, conn->sock))
}
/*
- * Initialize errorMessage to empty. This allows open_client_SSL() to
- * detect whether client_cert_cb() has stored a message.
+ * Load client certificate, private key, and trusted CA certs.
*/
- resetPQExpBuffer(&conn->errorMessage);
+ if (initialize_SSL(conn) != 0)
+ {
+ /* initialize_SSL already put a message in conn->errorMessage */
+ close_SSL(conn);
+ return PGRES_POLLING_FAILED;
+ }
}
+
/* Begin or continue the actual handshake */
return open_client_SSL(conn);
#else
* This is roughly in line with RFC2818, but contrary to what most browsers
* appear to be implementing (point 3 being the difference)
*
- * Matching is always cone case-insensitive, since DNS is case insensitive.
+ * Matching is always case-insensitive, since DNS is case insensitive.
*/
static int
wildcard_certificate_match(const char *pattern, const char *string)
else
{
/*
- * Connect by hostname.
+ * Compare CN to originally given hostname.
*
* XXX: Should support alternate names here
*/
}
}
-/*
- * Callback used by SSL to load client cert and key.
- * This callback is only called when the server wants a
- * client cert.
- *
- * Since BIO functions can set OpenSSL error codes, we must
- * reset the OpenSSL error stack on *every* exit from this
- * function once we've started using BIO.
- *
- * Must return 1 on success, 0 on no data or error.
- */
-static int
-client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
-{
- char homedir[MAXPGPATH];
- struct stat buf;
-
-#ifndef WIN32
- struct stat buf2;
- FILE *fp;
-#endif
- char fnbuf[MAXPGPATH];
- BIO *bio;
- PGconn *conn = (PGconn *) SSL_get_app_data(ssl);
- char sebuf[256];
-
- /*
- * If conn->sslcert or conn->sslkey is not set, we don't need the home
- * directory to find the required files.
- */
- if (!conn->sslcert || !conn->sslkey)
- {
- if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get home directory to locate client certificate files\n"));
- return 0;
- }
- }
-
- /* read the user certificate */
- if (conn->sslcert)
- strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
- else
- snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
-
- /*
- * OpenSSL <= 0.9.8 lacks error stack handling, which means it's likely to
- * report wrong error messages if access to the cert file fails. Do our
- * own check for the readability of the file to catch the majority of such
- * problems before OpenSSL gets involved.
- */
-#ifndef HAVE_ERR_SET_MARK
- {
- FILE *fp2;
-
- if ((fp2 = fopen(fnbuf, "r")) == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not open certificate file \"%s\": %s\n"),
- fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
- return 0;
- }
- fclose(fp2);
- }
-#endif
-
- /* save OpenSSL error stack */
- ERR_set_mark();
-
- if ((bio = BIO_new_file(fnbuf, "r")) == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not open certificate file \"%s\": %s\n"),
- fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
- ERR_pop_to_mark();
- return 0;
- }
-
- if (PEM_read_bio_X509(bio, x509, NULL, NULL) == NULL)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not read certificate file \"%s\": %s\n"),
- fnbuf, err);
- SSLerrfree(err);
- BIO_free(bio);
- ERR_pop_to_mark();
- return 0;
- }
-
- BIO_free(bio);
-
- /*
- * 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
- * colon in the name. The exception is if the second character is a colon,
- * in which case it can be a Windows filename with drive specification.
- */
- if (conn->sslkey && strlen(conn->sslkey) > 0)
- {
-#ifdef USE_SSL_ENGINE
- if (strchr(conn->sslkey, ':')
-#ifdef WIN32
- && conn->sslkey[1] != ':'
-#endif
- )
- {
- /* Colon, but not in second character, treat as engine:key */
- char *engine_str = strdup(conn->sslkey);
- char *engine_colon = strchr(engine_str, ':');
-
- *engine_colon = '\0'; /* engine_str now has engine name */
- engine_colon++; /* engine_colon now has key name */
-
- conn->engine = ENGINE_by_id(engine_str);
- if (conn->engine == NULL)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not load SSL engine \"%s\": %s\n"),
- engine_str, err);
- SSLerrfree(err);
- free(engine_str);
- ERR_pop_to_mark();
- return 0;
- }
-
- if (ENGINE_init(conn->engine) == 0)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
- engine_str, err);
- SSLerrfree(err);
- ENGINE_free(conn->engine);
- conn->engine = NULL;
- free(engine_str);
- ERR_pop_to_mark();
- return 0;
- }
-
- *pkey = ENGINE_load_private_key(conn->engine, engine_colon,
- NULL, NULL);
- if (*pkey == NULL)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
- engine_colon, engine_str, err);
- SSLerrfree(err);
- ENGINE_finish(conn->engine);
- ENGINE_free(conn->engine);
- conn->engine = NULL;
- free(engine_str);
- ERR_pop_to_mark();
- return 0;
- }
- free(engine_str);
-
- fnbuf[0] = '\0'; /* indicate we're not going to load from a
- * file */
- }
- else
-#endif /* support for SSL engines */
- {
- /* PGSSLKEY is not an engine, treat it as a filename */
- strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
- }
- }
- else
- {
- /* No PGSSLKEY specified, load default file */
- snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
- }
-
- if (fnbuf[0] != '\0')
- {
- /* read the user key from file */
-
- if (stat(fnbuf, &buf) != 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("certificate present, but not private key file \"%s\"\n"),
- fnbuf);
- ERR_pop_to_mark();
- return 0;
- }
-#ifndef WIN32
- if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
- fnbuf);
- ERR_pop_to_mark();
- return 0;
- }
-#endif
-
- if ((bio = BIO_new_file(fnbuf, "r")) == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not open private key file \"%s\": %s\n"),
- fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
- ERR_pop_to_mark();
- return 0;
- }
-#ifndef WIN32
- BIO_get_fp(bio, &fp);
- if (fstat(fileno(fp), &buf2) == -1 ||
- buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("private key file \"%s\" changed during execution\n"), fnbuf);
- ERR_pop_to_mark();
- return 0;
- }
-#endif
-
- if (PEM_read_bio_PrivateKey(bio, pkey, NULL, NULL) == NULL)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not read private key file \"%s\": %s\n"),
- fnbuf, err);
- SSLerrfree(err);
-
- BIO_free(bio);
- ERR_pop_to_mark();
- return 0;
- }
-
- BIO_free(bio);
- }
-
- /* verify that the cert and key go together */
- if (X509_check_private_key(*x509, *pkey) != 1)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
- fnbuf, err);
- SSLerrfree(err);
- ERR_pop_to_mark();
- return 0;
- }
-
- ERR_pop_to_mark();
-
- return 1;
-}
-
#ifdef ENABLE_THREAD_SAFETY
/*
* Callback functions for OpenSSL internal locking
#endif /* ENABLE_THREAD_SAFETY */
/*
- * Initialize SSL system. In threadsafe mode, this includes setting
- * up libcrypto callback functions to do thread locking.
+ * Initialize SSL system, in particular creating the SSL_context object
+ * that will be shared by all SSL-using connections in this process.
+ *
+ * In threadsafe mode, this includes setting up libcrypto callback functions
+ * to do thread locking.
*
* If the caller has told us (through PQinitOpenSSL) that he's taking care
* of libcrypto, we expect that callbacks are already set, and won't try to
* override it.
*
* The conn parameter is only used to be able to pass back an error
- * message - no connection local setup is made.
+ * message - no connection-local setup is made here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
*/
static int
init_ssl_system(PGconn *conn)
SSL_library_init();
SSL_load_error_strings();
}
+
SSL_context = SSL_CTX_new(TLSv1_method());
if (!SSL_context)
{
pthread_mutex_unlock(&ssl_config_mutex);
#endif
- return;
}
/*
- * Initialize SSL context.
+ * 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.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
*/
static int
initialize_SSL(PGconn *conn)
struct stat buf;
char homedir[MAXPGPATH];
char fnbuf[MAXPGPATH];
-
- if (init_ssl_system(conn))
- return -1;
+ char sebuf[256];
+ bool have_cert;
+ EVP_PKEY *pkey = NULL;
/*
- * If sslmode is set to one of the verify options, perform certificate
- * verification. If set to "verify-full" we will also do further
- * verification after the connection has been completed.
- *
- * If we are going to look for either root certificate or CRL in the home
- * directory, we need pqGetHomeDirectory() to succeed. In other cases, we
- * don't need to get the home directory explicitly.
+ * We'll need the home directory if any of the relevant parameters are
+ * defaulted.
*/
- if (!conn->sslrootcert || !conn->sslcrl)
+ if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+ !(conn->sslkey && strlen(conn->sslkey) > 0) ||
+ !(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
+ !(conn->sslcrl && strlen(conn->sslcrl) > 0))
{
if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
{
- if (conn->sslmode[0] == 'v') /* "verify-ca" or
- * "verify-full" */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not get home directory to locate client certificate files\n"));
+ return -1;
+ }
+ }
+ else
+ {
+ homedir[0] = '\0';
+ }
+
+ /* Read the client certificate file */
+ if (conn->sslcert && strlen(conn->sslcert) > 0)
+ strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+ else
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+
+ if (stat(fnbuf, &buf) != 0)
+ {
+ /*
+ * If file is not present, just go on without a client cert; server
+ * might or might not accept the connection. Any other error, however,
+ * is grounds for complaint.
+ */
+ if (errno != ENOENT)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not open certificate file \"%s\": %s\n"),
+ fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+ return -1;
+ }
+ have_cert = false;
+ }
+ 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.
+ */
+ if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read certificate file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ return -1;
+ }
+ if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read certificate file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ return -1;
+ }
+ /* need to load the associated private key, too */
+ have_cert = true;
+ }
+
+ /*
+ * 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
+ * colon in the name. The exception is if the second character is a colon,
+ * in which case it can be a Windows filename with drive specification.
+ */
+ if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+ {
+#ifdef USE_SSL_ENGINE
+ if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+ && conn->sslkey[1] != ':'
+#endif
+ )
+ {
+ /* Colon, but not in second character, treat as engine:key */
+ char *engine_str = strdup(conn->sslkey);
+ char *engine_colon = strchr(engine_str, ':');
+
+ *engine_colon = '\0'; /* engine_str now has engine name */
+ engine_colon++; /* engine_colon now has key name */
+
+ conn->engine = ENGINE_by_id(engine_str);
+ if (conn->engine == NULL)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not load SSL engine \"%s\": %s\n"),
+ engine_str, err);
+ SSLerrfree(err);
+ free(engine_str);
+ return -1;
+ }
+
+ if (ENGINE_init(conn->engine) == 0)
{
+ char *err = SSLerrmessage();
+
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get home directory to locate root certificate file\n"));
+ libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
+ engine_str, err);
+ SSLerrfree(err);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ free(engine_str);
return -1;
}
+
+ pkey = ENGINE_load_private_key(conn->engine, engine_colon,
+ NULL, NULL);
+ if (pkey == NULL)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+ engine_colon, engine_str, err);
+ SSLerrfree(err);
+ ENGINE_finish(conn->engine);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ free(engine_str);
+ return -1;
+ }
+ if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+ engine_colon, engine_str, err);
+ SSLerrfree(err);
+ ENGINE_finish(conn->engine);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ free(engine_str);
+ return -1;
+ }
+
+ free(engine_str);
+
+ fnbuf[0] = '\0'; /* indicate we're not going to load from a
+ * file */
+ }
+ else
+#endif /* USE_SSL_ENGINE */
+ {
+ /* PGSSLKEY is not an engine, treat it as a filename */
+ strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
}
}
else
{
- homedir[0] = '\0';
+ /* No PGSSLKEY specified, load default file */
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
}
- if (conn->sslrootcert)
+ if (have_cert && fnbuf[0] != '\0')
+ {
+ /* read the client key from file */
+
+ if (stat(fnbuf, &buf) != 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+ fnbuf);
+ return -1;
+ }
+#ifndef WIN32
+ if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+ fnbuf);
+ return -1;
+ }
+#endif
+
+ if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not load private key file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ return -1;
+ }
+ }
+
+ /* verify that the cert and key go together */
+ if (have_cert &&
+ SSL_check_private_key(conn->ssl) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ return -1;
+ }
+
+ /*
+ * 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)
strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
else
snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
{
- if (conn->sslcrl)
+ if (conn->sslcrl && strlen(conn->sslcrl) > 0)
strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
else
snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
- /* setting the flags to check against the complete CRL chain */
+ /* Set the flags to check against the complete CRL chain */
if (X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1)
-/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
+ {
+ /* 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);
- /* if not found, silently ignore; we do not require CRL */
#else
- {
char *err = SSLerrmessage();
printfPQExpBuffer(&conn->errorMessage,
fnbuf);
SSLerrfree(err);
return -1;
- }
#endif
+ }
+ /* if not found, silently ignore; we do not require CRL */
}
- SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER, verify_cb);
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
}
else
{
- /* stat() failed; assume cert file doesn't exist */
+ /*
+ * 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" */
{
printfPQExpBuffer(&conn->errorMessage,
}
}
- /* set up mechanism to provide client certificate, if available */
- SSL_CTX_set_client_cert_cb(SSL_context, client_cert_cb);
-
return 0;
}
}
case SSL_ERROR_SSL:
{
- /*
- * If there are problems with the local certificate files,
- * these will be detected by client_cert_cb() which is
- * called from SSL_connect(). We want to return that
- * error message and not the rather unhelpful error that
- * OpenSSL itself returns. So check to see if an error
- * message was already stored.
- */
- if (conn->errorMessage.len == 0)
- {
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage();
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL error: %s\n"),
- err);
- SSLerrfree(err);
- }
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL error: %s\n"),
+ err);
+ SSLerrfree(err);
close_SSL(conn);
return PGRES_POLLING_FAILED;
}
/*
* We already checked the server certificate in initialize_SSL() using
- * SSL_CTX_set_verify() if root.crt exists.
+ * SSL_CTX_set_verify(), if root.crt exists.
*/
/* pull out server distinguished and common names */