* message integrity and endpoint authentication.
*
*
- * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.88 2006/10/04 00:30:13 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.110 2008/12/02 10:39:30 mha Exp $
*
* NOTES
- * [ Most of these notes are wrong/obsolete, but perhaps not all ]
- *
- * The client *requires* a valid server certificate. Since
- * SSH tunnels provide anonymous confidentiality, the presumption
- * is that sites that want endpoint authentication will use the
- * direct SSL support, while sites that are comfortable with
- * anonymous connections will use SSH tunnels.
- *
- * This code verifies the server certificate, to detect simple
- * "man-in-the-middle" and "impersonation" attacks. The
- * server certificate, or better yet the CA certificate used
- * to sign the server certificate, should be present in the
- * "~/.postgresql/root.crt" file. If this file isn't
- * readable, or the server certificate can't be validated,
- * pqsecure_open_client() will return an error code.
- *
- * Additionally, the server certificate's "common name" must
- * resolve to the other end of the socket. This makes it
- * substantially harder to pull off a "man-in-the-middle" or
- * "impersonation" attack even if the server's private key
- * has been stolen. This check limits acceptable network
- * layers to Unix sockets (weird, but legal), TCPv4 and TCPv6.
- *
- * Unfortunately neither the current front- or back-end handle
- * failure gracefully, resulting in the backend hiccupping.
- * This points out problems in each (the frontend shouldn't even
- * try to do SSL if pqsecure_initialize() fails, and the backend
- * shouldn't crash/recover if an SSH negotiation fails. The
- * backend definitely needs to be fixed, to prevent a "denial
- * of service" attack, but I don't know enough about how the
- * backend works (especially that pre-SSL negotiation) to identify
- * a fix.
- *
- * ...
- *
- * Unlike the server's static private key, the client's
- * static private key (~/.postgresql/postgresql.key)
- * should normally be stored encrypted. However we still
- * support EPH since it's useful for other reasons.
- *
- * ...
- *
- * Client certificates are supported, if the server requests
- * or requires them. Client certificates can be used for
- * authentication, to prevent sessions from being hijacked,
- * or to allow "road warriors" to access the database while
- * keeping it closed to everyone else.
- *
- * The user's certificate and private key are located in
- * ~/.postgresql/postgresql.crt
- * and
- * ~/.postgresql/postgresql.key
- * respectively.
- *
- * ...
*
* We don't provide informational callbacks here (like
* info_cb() in be-secure.c), since there's mechanism to
#endif
#ifdef USE_SSL
-#include <openssl/ssl.h>
-#endif /* USE_SSL */
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
+#include <openssl/conf.h>
+#endif
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
+#include <openssl/engine.h>
+#endif
-#ifdef USE_SSL
#ifndef WIN32
#define USER_CERT_FILE ".postgresql/postgresql.crt"
#define ROOT_CRL_FILE "root.crl"
#endif
-#ifdef NOT_USED
-static int verify_peer(PGconn *);
+#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 SSL_CTX *SSL_context = NULL;
#endif
+/*
+ * Macros to handle disabling and then restoring the state of SIGPIPE handling.
+ * Note that DISABLE_SIGPIPE() must appear at the start of a block.
+ */
+
+#ifndef WIN32
+#ifdef ENABLE_THREAD_SAFETY
+
+#define DISABLE_SIGPIPE(failaction) \
+ sigset_t osigmask; \
+ bool sigpipe_pending; \
+ bool got_epipe = false; \
+\
+ if (pq_block_sigpipe(&osigmask, &sigpipe_pending) < 0) \
+ failaction
+
+#define REMEMBER_EPIPE(cond) \
+ do { \
+ if (cond) \
+ got_epipe = true; \
+ } while (0)
+
+#define RESTORE_SIGPIPE() \
+ pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe)
+
+#else /* !ENABLE_THREAD_SAFETY */
+
+#define DISABLE_SIGPIPE(failaction) \
+ pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN)
+
+#define REMEMBER_EPIPE(cond)
+
+#define RESTORE_SIGPIPE() \
+ pqsignal(SIGPIPE, oldsighandler)
+
+#endif /* ENABLE_THREAD_SAFETY */
+#else /* WIN32 */
+
+#define DISABLE_SIGPIPE(failaction)
+#define REMEMBER_EPIPE(cond)
+#define RESTORE_SIGPIPE()
+
+#endif /* WIN32 */
+
/* ------------------------------------------------------------ */
/* Procedures common to all secure sessions */
/* ------------------------------------------------------------ */
{
int err;
+ /* SSL_read can write to the socket, so we need to disable SIGPIPE */
+ DISABLE_SIGPIPE(return -1);
+
rloop:
n = SSL_read(conn->ssl, ptr, len);
err = SSL_get_error(conn->ssl, n);
char sebuf[256];
if (n == -1)
+ {
+ REMEMBER_EPIPE(SOCK_ERRNO == EPIPE);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL SYSCALL error: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ }
else
{
printfPQExpBuffer(&conn->errorMessage,
n = -1;
break;
}
+
+ RESTORE_SIGPIPE();
}
else
#endif
{
ssize_t n;
-#ifndef WIN32
-#ifdef ENABLE_THREAD_SAFETY
- sigset_t osigmask;
- bool sigpipe_pending;
- bool got_epipe = false;
-
-
- if (pq_block_sigpipe(&osigmask, &sigpipe_pending) < 0)
- return -1;
-#else
- pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
-#endif /* ENABLE_THREAD_SAFETY */
-#endif /* WIN32 */
+ DISABLE_SIGPIPE(return -1);
#ifdef USE_SSL
if (conn->ssl)
if (n == -1)
{
-#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
- if (SOCK_ERRNO == EPIPE)
- got_epipe = true;
-#endif
+ REMEMBER_EPIPE(SOCK_ERRNO == EPIPE);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL SYSCALL error: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
#endif
{
n = send(conn->sock, ptr, len, 0);
-#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
- if (n < 0 && SOCK_ERRNO == EPIPE)
- got_epipe = true;
-#endif
+ REMEMBER_EPIPE(n < 0 && SOCK_ERRNO == EPIPE);
}
-#ifndef WIN32
-#ifdef ENABLE_THREAD_SAFETY
- pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe);
-#else
- pqsignal(SIGPIPE, oldsighandler);
-#endif /* ENABLE_THREAD_SAFETY */
-#endif /* WIN32 */
+ RESTORE_SIGPIPE();
return n;
}
return ok;
}
-#ifdef NOT_USED
+
/*
- * Verify that common name resolves to peer.
+ * Check if a wildcard certificate matches the server hostname.
+ *
+ * The rule for this is:
+ * 1. We only match the '*' character as wildcard
+ * 2. We match only wildcards at the start of the string
+ * 3. The '*' character does *not* match '.', meaning that we match only
+ * a single pathname component.
+ * 4. We don't support more than one '*' in a single pattern.
+ *
+ * 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.
*/
static int
-verify_peer(PGconn *conn)
+wildcard_certificate_match(const char *pattern, const char *string)
{
- struct hostent *h = NULL;
- struct sockaddr addr;
- struct sockaddr_in *sin;
- ACCEPT_TYPE_ARG3 len;
- char **s;
- unsigned long l;
-
- /* get the address on the other side of the socket */
- len = sizeof(addr);
- if (getpeername(conn->sock, &addr, &len) == -1)
- {
- char sebuf[256];
+ int lenpat = strlen(pattern);
+ int lenstr = strlen(string);
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("error querying socket: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return -1;
- }
-
- /* weird, but legal case */
- if (addr.sa_family == AF_UNIX)
+ /* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
+ if (lenpat < 3 ||
+ pattern[0] != '*' ||
+ pattern[1] != '.')
return 0;
- {
- struct hostent hpstr;
- char buf[BUFSIZ];
- int herrno = 0;
+ if (lenpat > lenstr)
+ /* If pattern is longer than the string, we can never match */
+ return 0;
- /*
- * Currently, pqGethostbyname() is used only on platforms that don't
- * have getaddrinfo(). If you enable this function, you should
- * convert the pqGethostbyname() function call to use getaddrinfo().
- */
- pqGethostbyname(conn->peer_cn, &hpstr, buf, sizeof(buf),
- &h, &herrno);
- }
+ if (pg_strcasecmp(pattern+1, string+lenstr-lenpat+1) != 0)
+ /* If string does not end in pattern (minus the wildcard), we don't match */
+ return 0;
- /* what do we know about the peer's common name? */
- if (h == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get information about host \"%s\": %s\n"),
- conn->peer_cn, hstrerror(h_errno));
- return -1;
- }
+ if (strchr(string, '.') < string+lenstr-lenpat)
+ /* If there is a dot left of where the pattern started to match, we don't match (rule 3) */
+ return 0;
- /* does the address match? */
- switch (addr.sa_family)
- {
- case AF_INET:
- sin = (struct sockaddr_in *) & addr;
- for (s = h->h_addr_list; *s != NULL; s++)
- {
- if (!memcmp(&sin->sin_addr.s_addr, *s, h->h_length))
- return 0;
- }
- break;
+ /* String ended with pattern, and didn't have a dot before, so we match */
+ return 1;
+}
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("unsupported protocol\n"));
- return -1;
- }
+/*
+ * Verify that common name resolves to peer.
+ */
+static bool
+verify_peer_name_matches_certificate(PGconn *conn)
+{
/*
- * the prior test should be definitive, but in practice it sometimes
- * fails. So we also check the aliases.
+ * If told not to verify the peer name, don't do it. Return
+ * 0 indicating that the verification was successful.
*/
- for (s = h->h_aliases; *s != NULL; s++)
+ if(strcmp(conn->sslverify, "cn") != 0)
+ return true;
+
+ if (conn->pghostaddr)
{
- if (pg_strcasecmp(conn->peer_cn, *s) == 0)
- return 0;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("verified SSL connections are only supported when connecting to a hostname"));
+ return false;
}
-
- /* generate protocol-aware error message */
- switch (addr.sa_family)
+ else
{
- case AF_INET:
- sin = (struct sockaddr_in *) & addr;
- l = ntohl(sin->sin_addr.s_addr);
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "server common name \"%s\" does not resolve to %ld.%ld.%ld.%ld\n"),
- conn->peer_cn, (l >> 24) % 0x100, (l >> 16) % 0x100,
- (l >> 8) % 0x100, l % 0x100);
- break;
- default:
+ /*
+ * Connect by hostname.
+ *
+ * XXX: Should support alternate names here
+ */
+ if (pg_strcasecmp(conn->peer_cn, conn->pghost) == 0)
+ /* Exact name match */
+ return true;
+ else if (wildcard_certificate_match(conn->peer_cn, conn->pghost))
+ /* Matched wildcard certificate */
+ return true;
+ else
+ {
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "server common name \"%s\" does not resolve to peer address\n"),
- conn->peer_cn);
+ libpq_gettext("server common name '%s' does not match hostname '%s'"),
+ conn->peer_cn, conn->pghost);
+ return false;
+ }
}
-
- return -1;
}
-#endif /* NOT_USED */
/*
* 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
#ifndef WIN32
struct stat buf2;
+ FILE *fp;
#endif
char fnbuf[MAXPGPATH];
- FILE *fp;
+ BIO *bio;
PGconn *conn = (PGconn *) SSL_get_app_data(ssl);
- int (*cb) () = NULL; /* how to read user password */
char sebuf[256];
if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
/* read the user certificate */
snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
- if ((fp = fopen(fnbuf, "r")) == NULL)
+
+ /*
+ * 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_X509(fp, x509, NULL, NULL) == NULL)
+
+ if (PEM_read_bio_X509(bio, x509, NULL, NULL) == NULL)
{
char *err = SSLerrmessage();
libpq_gettext("could not read certificate file \"%s\": %s\n"),
fnbuf, err);
SSLerrfree(err);
- fclose(fp);
+ BIO_free(bio);
+ ERR_pop_to_mark();
return 0;
}
- fclose(fp);
- /* read the user key */
- snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
- if (stat(fnbuf, &buf) == -1)
+ BIO_free(bio);
+
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
+ if (getenv("PGSSLKEY"))
{
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("certificate present, but not private key file \"%s\"\n"),
- fnbuf);
- return 0;
+ /* read the user key from engine */
+ char *engine_env = getenv("PGSSLKEY");
+ char *engine_colon = strchr(engine_env, ':');
+ char *engine_str;
+ ENGINE *engine_ptr;
+
+ if (!engine_colon)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid value of PGSSLKEY environment variable\n"));
+ ERR_pop_to_mark();
+ return 0;
+ }
+
+ engine_str = malloc(engine_colon - engine_env + 1);
+ strlcpy(engine_str, engine_env, engine_colon - engine_env + 1);
+ engine_ptr = ENGINE_by_id(engine_str);
+ if (engine_ptr == 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;
+ }
+
+ *pkey = ENGINE_load_private_key(engine_ptr, engine_colon + 1,
+ 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 + 1, engine_str, err);
+ SSLerrfree(err);
+ free(engine_str);
+ ERR_pop_to_mark();
+ return 0;
+ }
+ free(engine_str);
}
-#ifndef WIN32
- if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) ||
- buf.st_uid != geteuid())
+ else
+#endif /* use PGSSLKEY */
{
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("private key file \"%s\" has wrong permissions\n"),
- fnbuf);
- return 0;
- }
+ /* read the user key from file */
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_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 ((fp = fopen(fnbuf, "r")) == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
+
+ 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)));
- return 0;
- }
+ fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+ ERR_pop_to_mark();
+ return 0;
+ }
#ifndef WIN32
- 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);
- return 0;
- }
+ 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_PrivateKey(fp, pkey, cb, NULL) == NULL)
- {
- char *err = SSLerrmessage();
- printfPQExpBuffer(&conn->errorMessage,
+ 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);
- fclose(fp);
- return 0;
+ fnbuf, err);
+ SSLerrfree(err);
+
+ BIO_free(bio);
+ ERR_pop_to_mark();
+ return 0;
+ }
+
+ BIO_free(bio);
}
- fclose(fp);
/* verify that the cert and key go together */
if (!X509_check_private_key(*x509, *pkey))
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;
}
pq_lockingcallback(int mode, int n, const char *file, int line)
{
if (mode & CRYPTO_LOCK)
- pthread_mutex_lock(&pq_lockarray[n]);
+ {
+ if (pthread_mutex_lock(&pq_lockarray[n]))
+ PGTHREAD_ERROR("failed to lock mutex");
+ }
else
- pthread_mutex_unlock(&pq_lockarray[n]);
+ {
+ if (pthread_mutex_unlock(&pq_lockarray[n]))
+ PGTHREAD_ERROR("failed to unlock mutex");
+ }
}
#endif /* ENABLE_THREAD_SAFETY */
+/*
+ * Also see similar code in fe-connect.c, default_threadlock()
+ */
static int
init_ssl_system(PGconn *conn)
{
while (InterlockedExchange(&mutex_initlock, 1) == 1)
/* loop, another thread own the lock */ ;
if (init_mutex == NULL)
- pthread_mutex_init(&init_mutex, NULL);
+ {
+ if (pthread_mutex_init(&init_mutex, NULL))
+ return -1;
+ }
InterlockedExchange(&mutex_initlock, 0);
}
#endif
- pthread_mutex_lock(&init_mutex);
+ if (pthread_mutex_lock(&init_mutex))
+ return -1;
if (pq_initssllib && pq_lockarray == NULL)
{
return -1;
}
for (i = 0; i < CRYPTO_num_locks(); i++)
- pthread_mutex_init(&pq_lockarray[i], NULL);
+ {
+ if (pthread_mutex_init(&pq_lockarray[i], NULL))
+ return -1;
+ }
CRYPTO_set_locking_callback(pq_lockingcallback);
}
{
if (pq_initssllib)
{
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+ OPENSSL_config(NULL);
+#endif
SSL_library_init();
SSL_load_error_strings();
}
if (init_ssl_system(conn))
return -1;
+ /*
+ * If sslverify is set to anything other than "none", perform certificate
+ * verification. If set to "cn" we will also do further verifications after
+ * the connection has been completed.
+ */
+
/* Set up to verify server cert, if root.crt is present */
if (pqGetHomeDirectory(homedir, sizeof(homedir)))
{
char *err = SSLerrmessage();
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("Installed SSL library does not support CRL certificates, file \"%s\"\n"),
+ libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
fnbuf);
SSLerrfree(err);
return -1;
SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER, verify_cb);
}
+ else
+ {
+ if (strcmp(conn->sslverify, "none") != 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("root certificate file (%s) not found"), fnbuf);
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ if (strcmp(conn->sslverify, "none") != 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot find home directory to locate root certificate file"));
+ return -1;
+ }
}
/* set up mechanism to provide client certificate, if available */
}
}
- /* check the certificate chain of the server */
-
-#ifdef NOT_USED
- /* CLIENT CERTIFICATES NOT REQUIRED bjm 2002-09-26 */
-
/*
- * this eliminates simple man-in-the-middle attacks and simple
- * impersonations
+ * We already checked the server certificate in initialize_SSL()
+ * using SSL_CTX_set_verify() if root.crt exists.
*/
- r = SSL_get_verify_result(conn->ssl);
- if (r != X509_V_OK)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("certificate could not be validated: %s\n"),
- X509_verify_cert_error_string(r));
- close_SSL(conn);
- return PGRES_POLLING_FAILED;
- }
-#endif
/* pull out server distinguished and common names */
conn->peer = SSL_get_peer_certificate(conn->ssl);
NID_commonName, conn->peer_cn, SM_USER);
conn->peer_cn[SM_USER] = '\0';
- /* verify that the common name resolves to peer */
-
-#ifdef NOT_USED
- /* CLIENT CERTIFICATES NOT REQUIRED bjm 2002-09-26 */
-
- /*
- * this is necessary to eliminate man-in-the-middle attacks and
- * impersonations where the attacker somehow learned the server's private
- * key
- */
- if (verify_peer(conn) == -1)
+ if (!verify_peer_name_matches_certificate(conn))
{
close_SSL(conn);
return PGRES_POLLING_FAILED;
}
-#endif
/* SSL handshake is complete */
return PGRES_POLLING_OK;
{
if (conn->ssl)
{
+ DISABLE_SIGPIPE((void) 0);
SSL_shutdown(conn->ssl);
SSL_free(conn->ssl);
conn->ssl = NULL;
+ /* We have to assume we got EPIPE */
+ REMEMBER_EPIPE(true);
+ RESTORE_SIGPIPE();
}
if (conn->peer)
errreason = ERR_reason_error_string(errcode);
if (errreason != NULL)
{
- strncpy(errbuf, errreason, SSL_ERR_LEN - 1);
- errbuf[SSL_ERR_LEN - 1] = '\0';
+ strlcpy(errbuf, errreason, SSL_ERR_LEN);
return errbuf;
}
snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
}
#endif /* USE_SSL */
+
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
/*
SOCK_ERRNO_SET(save_errno);
}
-#endif /* ENABLE_THREAD_SAFETY */
+#endif /* ENABLE_THREAD_SAFETY && !WIN32 */