commit 140c590ca86a0ba4a6b422e4b618cd459b84175f
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date:   Wed Aug 6 18:43:39 2014 +0300

    Refactor cert file stuff in client

diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index f950fc3..cee7b2e 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -780,57 +780,21 @@ destroy_ssl_system(void)
 static int
 initialize_SSL(PGconn *conn)
 {
-	struct stat buf;
-	char		homedir[MAXPGPATH];
-	char		fnbuf[MAXPGPATH];
-	char		sebuf[256];
-	bool		have_homedir;
-	bool		have_cert;
 	EVP_PKEY   *pkey = NULL;
-
-	/*
-	 * We'll need the home directory if any of the relevant parameters are
-	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
-	 * files could be found.
-	 */
-	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))
-		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
-	else	/* won't need it */
-		have_homedir = false;
-
-	/* Read the client certificate file */
-	if (conn->sslcert && strlen(conn->sslcert) > 0)
-		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] == '\0')
-	{
-		/* no home directory, proceed without a client cert */
-		have_cert = false;
-	}
-	else 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 && errno != ENOTDIR)
-		{
-			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
+	char	   *sslcertfile = NULL;
+	char	   *engine = NULL;
+	char	   *keyname = NULL;
+	char	   *sslkeyfile = NULL;
+	char	   *sslrootcert = NULL;
+	char	   *sslcrl = NULL;
+	int			ret = -1;
+
+	if (!pqsecure_get_ssl_files(conn,
+								&sslcertfile, &sslkeyfile, &engine, &keyname,
+								&sslrootcert, &sslcrl))
+		return PGRES_POLLING_READING;
+
+	if (sslcertfile)
 	{
 		/*
 		 * Cert file exists, so load it.  Since OpenSSL doesn't provide the
@@ -855,216 +819,146 @@ initialize_SSL(PGconn *conn)
 		{
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
+			goto fail;
 		}
 #endif
-		if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+		if (SSL_CTX_use_certificate_chain_file(SSL_context, sslcertfile) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslcertfile, err);
 			SSLerrfree(err);
 
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
-		if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+		if (SSL_use_certificate_file(conn->ssl, sslcertfile, SSL_FILETYPE_PEM) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslcertfile, err);
 			SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
-		/* need to load the associated private key, too */
-		have_cert = true;
-
 #ifdef ENABLE_THREAD_SAFETY
 		pthread_mutex_unlock(&ssl_config_mutex);
 #endif
 	}
 
 	/*
-	 * 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 an engine:key specification was given, load that.
 	 */
-	if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+	if (engine)
 	{
 #ifdef USE_SSL_ENGINE
-		if (strchr(conn->sslkey, ':')
-#ifdef WIN32
-			&& conn->sslkey[1] != ':'
-#endif
-			)
+		conn->engine = ENGINE_by_id(engine);
+		if (conn->engine == NULL)
 		{
-			/* Colon, but not in second character, treat as engine:key */
-			char	   *engine_str = strdup(conn->sslkey);
-			char	   *engine_colon;
-
-			if (engine_str == NULL)
-			{
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("out of memory\n"));
-				return -1;
-			}
-
-			/* cannot return NULL because we already checked before strdup */
-			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();
+			char	   *err = SSLerrmessage();
 
-				printfPQExpBuffer(&conn->errorMessage,
+			printfPQExpBuffer(&conn->errorMessage,
 					 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				free(engine_str);
-				return -1;
-			}
+							  engine, err);
+			SSLerrfree(err);
+			goto fail;
+		}
 
-			if (ENGINE_init(conn->engine) == 0)
-			{
-				char	   *err = SSLerrmessage();
+		if (ENGINE_init(conn->engine) == 0)
+		{
+			char	   *err = SSLerrmessage();
 
-				printfPQExpBuffer(&conn->errorMessage,
+			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);
-				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));
+							  engine, err);
+			SSLerrfree(err);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-	}
-	else if (have_homedir)
-	{
-		/* No PGSSLKEY specified, load default file */
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
-	}
-	else
-		fnbuf[0] = '\0';
 
-	if (have_cert && fnbuf[0] != '\0')
-	{
-		/* read the client key from file */
-
-		if (stat(fnbuf, &buf) != 0)
+		pkey = ENGINE_load_private_key(conn->engine, keyname, NULL, NULL);
+		if (pkey == NULL)
 		{
+			char	   *err = SSLerrmessage();
+
 			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
-							  fnbuf);
-			return -1;
+							  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+							  keyname, engine, err);
+			SSLerrfree(err);
+			ENGINE_finish(conn->engine);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-#ifndef WIN32
-		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+		if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
 		{
+			char	   *err = SSLerrmessage();
+
 			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;
+							  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+							  keyname, engine, err);
+			SSLerrfree(err);
+			ENGINE_finish(conn->engine);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-#endif
+#else
+		/*
+		 * should not happen; pqsecure_get_ssl_files doesn't return an
+		 * engine spec if not compiled with USE_SSL_ENGINE.
+		 */
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("engine not supported\n"));
+		goto fail;
+#endif   /* USE_SSL_ENGINE */
+	}
 
-		if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+	/* Read the client private key file */
+	if (sslkeyfile)
+	{
+		if (SSL_use_PrivateKey_file(conn->ssl, sslkeyfile, SSL_FILETYPE_PEM) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not load private key file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslkeyfile, err);
 			SSLerrfree(err);
-			return -1;
+			goto fail;
 		}
 	}
 
-	/* verify that the cert and key go together */
-	if (have_cert &&
+	/* Verify that the cert and key go together */
+	if (sslcertfile &&
 		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);
+						  sslkeyfile, err);
 		SSLerrfree(err);
-		return -1;
+		goto fail;
 	}
 
 	/*
-	 * 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.
+	 * Load root cert, if exists. (pqsecure_get_ssl_files already complained
+	 * if no root cert was configured but sslmode requires verification
+	 * of server certificates.)
 	 */
-	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
-		strncpy(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)
+	if (sslrootcert)
 	{
 		X509_STORE *cvstore;
 
@@ -1075,35 +969,28 @@ initialize_SSL(PGconn *conn)
 		{
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
+			goto fail;
 		}
 #endif
-		if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+		if (SSL_CTX_load_verify_locations(SSL_context, sslrootcert, NULL) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslrootcert, err);
 			SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
 		if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
 		{
-			if (conn->sslcrl && strlen(conn->sslcrl) > 0)
-				strncpy(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)
+			if (sslcrl &&
+				X509_STORE_load_locations(cvstore, sslcrl, NULL) == 1)
 			{
 				/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
 #ifdef X509_V_FLAG_CRL_CHECK
@@ -1114,12 +1001,12 @@ initialize_SSL(PGconn *conn)
 
 				printfPQExpBuffer(&conn->errorMessage,
 								  libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
-								  fnbuf);
+								  sslcrl);
 				SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 				pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-				return -1;
+				goto fail;
 #endif
 			}
 			/* if not found, silently ignore;  we do not require CRL */
@@ -1130,31 +1017,6 @@ initialize_SSL(PGconn *conn)
 
 		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
@@ -1167,9 +1029,27 @@ initialize_SSL(PGconn *conn)
 	}
 #endif
 
-	return 0;
+	/* success */
+	ret = 0;
+
+fail:
+	if (sslcertfile)
+		free(sslcertfile);
+	if (engine)
+		free(engine);
+	if (keyname)
+		free(keyname);
+	if (sslkeyfile)
+		free(sslkeyfile);
+	if (sslrootcert)
+		free(sslrootcert);
+	if (sslcrl)
+		free(sslcrl);
+
+	return ret;
 }
 
+
 /*
  *	Attempt to negotiate SSL connection.
  */
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 66778b2..e19fc61 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -475,3 +475,297 @@ pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
 }
 
 #endif   /* ENABLE_THREAD_SAFETY && !WIN32 */
+
+/*
+ *	Determine the filenames of SSL related files.
+ *
+ * The filenames returned are checked to exist; the caller should throw an
+ * error if one of the returned files can not be opened. This function
+ * performs some preliminariry sanity checks, e.g. if a certificate file
+ * exists, we must also find the corresponding private key file.
+ *
+ * *sslcert is set to the path of the client's certificate, and *sslkey to
+ * the path to the corresponding private key.  If compiled with
+ * USE_SSL_ENGINE, a pathname containing a colon is interpreted as a
+ * a two-part "engin:key" string, for specifying a hardware key.
+ *
+ * *sslrootcert and *sslcrl are set to the paths to CA certificate and
+ * CRL files, used to validate the server certificate.
+ *
+ * The returned strings are malloc'd, and the caller is responsible for
+ * freeing them. On error, returns false and sets the libpq error message.
+ */
+bool
+pqsecure_get_ssl_files(PGconn *conn, char **sslcert, char **sslkey,
+					   char **engine, char **keyname,
+					   char **sslrootcert, char **sslcrl)
+{
+	struct stat buf;
+	char		homedir[MAXPGPATH];
+	char		fnbuf[MAXPGPATH];
+	char		sebuf[256];
+	bool		have_homedir;
+
+	*sslcert = NULL;
+	*sslkey = NULL;
+	*engine = NULL;
+	*keyname = NULL;
+	*sslrootcert = NULL;
+	*sslcrl = NULL;
+
+	/*
+	 * We'll need the home directory if any of the relevant parameters are
+	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
+	 * files could be found.
+	 */
+	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))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else	/* won't need it */
+		have_homedir = false;
+
+	/* Find the client certificate file */
+	if (conn->sslcert && strlen(conn->sslcert) > 0)
+		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] == '\0')
+	{
+		/* no home directory, proceed without a client cert */
+	}
+	else 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 && errno != ENOTDIR)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+			goto fail;
+		}
+	}
+	else
+	{
+		*sslcert = strdup(fnbuf);
+		if (*sslcert == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+
+	/*
+	 * 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 (*sslcert && 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;
+
+			if (engine_str == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+
+			/* cannot return NULL because we already checked before strdup */
+			engine_colon = strchr(engine_str, ':');
+
+			*engine_colon = '\0';		/* engine_str now has engine name */
+			engine_colon++;		/* engine_colon now has key name */
+
+			*engine = strdup(engine_str);
+			if (*engine)
+				*keyname = strdup(engine_colon);
+
+			free(engine_str);
+
+			if (*engine == NULL || *keyname == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+
+			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 if (have_homedir)
+	{
+		/* No PGSSLKEY specified, load default file */
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+	}
+	else
+		fnbuf[0] = '\0';
+
+	if (*sslcert && 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);
+			goto fail;
+		}
+#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);
+			goto fail;
+		}
+#endif
+
+		*sslkey = strdup(fnbuf);
+		if (*sslkey == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+
+	/*
+	 * 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 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)
+	{
+		*sslrootcert = strdup(fnbuf);
+		if (*sslrootcert == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+	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);
+			goto fail;
+		}
+	}
+
+	/* If we have a root certificate, also try to load a CRL */
+	if (*sslrootcert)
+	{
+		if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+			strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+		else if (have_homedir)
+			snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+		else
+			fnbuf[0] = '\0';
+
+		if (fnbuf[0] != '\0' &&
+			stat(fnbuf, &buf) != 0)
+		{
+			/*
+			 * If not found, silently ignore;  we do not require CRL.
+			 * Any other error, however, is grounds for complaint.
+			 */
+			if (errno != ENOENT && errno != ENOTDIR)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+				goto fail;
+			}
+		}
+		else
+		{
+			*sslcrl = strdup(fnbuf);
+			if (*sslcrl == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+		}
+	}
+
+	/* all done! */
+	return true;
+
+fail:
+	if (*sslcert)
+		free(*sslcert);
+	if (*engine)
+		free(*engine);
+	if (*keyname)
+		free(*keyname);
+	if (*sslkey)
+		free(*sslkey);
+	if (*sslrootcert)
+		free(*sslrootcert);
+	if (*sslcrl)
+		free(*sslcrl);
+
+	*sslcert = NULL;
+	*engine = NULL;
+	*keyname = NULL;
+	*sslkey = NULL;
+	*sslrootcert = NULL;
+	*sslcrl = NULL;
+
+	return false;
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 6032904..caca480 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -627,6 +627,8 @@ extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
 extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
 
+extern bool pqsecure_get_ssl_files(PGconn *, char **sslcert, char **sslkey, char **engine, char **keyname, char **sslrootcert, char **sslcrl);
+
 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
 extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
