* message integrity and endpoint authentication.
*
*
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2009, 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.69 2005/08/23 20:48:47 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.129 2009/12/09 06:37:06 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
#include <ctype.h>
#include "libpq-fe.h"
-#include "libpq-int.h"
#include "fe-auth.h"
#include "pqsignal.h"
+#include "libpq-int.h"
#ifdef WIN32
#include "win32.h"
#endif
#include <arpa/inet.h>
#endif
+
#include <sys/stat.h>
#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
#include <pthread.h>
#endif
-
-#ifndef HAVE_STRDUP
-#include "strdup.h"
#endif
#ifdef USE_SSL
-#include <openssl/ssl.h>
-#include <openssl/dh.h>
-#endif /* USE_SSL */
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
+#include <openssl/conf.h>
+#endif
+#ifdef USE_SSL_ENGINE
+#include <openssl/engine.h>
+#endif
-#ifdef USE_SSL
#ifndef WIN32
-#define USERCERTFILE ".postgresql/postgresql.crt"
-#define USERKEYFILE ".postgresql/postgresql.key"
-#define ROOTCERTFILE ".postgresql/root.crt"
-#define DHFILEPATTERN "%s/.postgresql/dh%d.pem"
+#define USER_CERT_FILE ".postgresql/postgresql.crt"
+#define USER_KEY_FILE ".postgresql/postgresql.key"
+#define ROOT_CERT_FILE ".postgresql/root.crt"
+#define ROOT_CRL_FILE ".postgresql/root.crl"
#else
/* On Windows, the "home" directory is already PostgreSQL-specific */
-#define USERCERTFILE "postgresql.crt"
-#define USERKEYFILE "postgresql.key"
-#define ROOTCERTFILE "root.crt"
-#define DHFILEPATTERN "%s/dh%d.pem"
+#define USER_CERT_FILE "postgresql.crt"
+#define USER_KEY_FILE "postgresql.key"
+#define ROOT_CERT_FILE "root.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 DH *load_dh_file(int keylength);
-static DH *load_dh_buffer(const char *, size_t);
-static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
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 void destroy_SSL(void);
+static void destroySSL(void);
static PostgresPollingStatusType open_client_SSL(PGconn *);
static void close_SSL(PGconn *);
static char *SSLerrmessage(void);
static void SSLerrfree(char *buf);
-#endif
-
-#ifdef USE_SSL
-static bool pq_initssllib = true;
+static bool pq_init_ssl_lib = true;
+static bool pq_init_crypto_lib = true;
static SSL_CTX *SSL_context = NULL;
+
+#ifdef ENABLE_THREAD_SAFETY
+static long ssl_open_connections = 0;
+
+#ifndef WIN32
+static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
+#else
+static pthread_mutex_t ssl_config_mutex = NULL;
+static long win32_ssl_create_mutex = 0;
#endif
+#endif /* ENABLE_THREAD_SAFETY */
+#endif /* SSL */
-/* ------------------------------------------------------------ */
-/* Hardcoded values */
-/* ------------------------------------------------------------ */
/*
- * Hardcoded DH parameters, used in empheral DH keying.
- * As discussed above, EDH protects the confidentiality of
- * sessions even if the static private key is compromised,
- * so we are *highly* motivated to ensure that we can use
- * EDH even if the user... or an attacker... deletes the
- * ~/.postgresql/dh*.pem files.
- *
- * It's not critical that users have EPH keys, but it doesn't
- * hurt and if it's missing someone will demand it, so....
+ * Macros to handle disabling and then restoring the state of SIGPIPE handling.
+ * On Windows, these are all no-ops since there's no SIGPIPEs.
*/
-#ifdef USE_SSL
-static const char file_dh512[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
-XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh1024[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
-jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
-ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh2048[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
-89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
-T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
-zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
-Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
-CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh4096[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
-l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
-Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
-Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
-VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
-alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
-sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
-ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
-OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
-AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
-KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
------END DH PARAMETERS-----\n";
-#endif
+#ifndef WIN32
+
+#define SIGPIPE_MASKED(conn) ((conn)->sigpipe_so || (conn)->sigpipe_flag)
+
+#ifdef ENABLE_THREAD_SAFETY
+
+struct sigpipe_info
+{
+ sigset_t oldsigmask;
+ bool sigpipe_pending;
+ bool got_epipe;
+};
+
+#define DECLARE_SIGPIPE_INFO(spinfo) struct sigpipe_info spinfo
+
+#define DISABLE_SIGPIPE(conn, spinfo, failaction) \
+ do { \
+ (spinfo).got_epipe = false; \
+ if (!SIGPIPE_MASKED(conn)) \
+ { \
+ if (pq_block_sigpipe(&(spinfo).oldsigmask, \
+ &(spinfo).sigpipe_pending) < 0) \
+ failaction; \
+ } \
+ } while (0)
+
+#define REMEMBER_EPIPE(spinfo, cond) \
+ do { \
+ if (cond) \
+ (spinfo).got_epipe = true; \
+ } while (0)
+
+#define RESTORE_SIGPIPE(conn, spinfo) \
+ do { \
+ if (!SIGPIPE_MASKED(conn)) \
+ pq_reset_sigpipe(&(spinfo).oldsigmask, (spinfo).sigpipe_pending, \
+ (spinfo).got_epipe); \
+ } while (0)
+
+#else /* !ENABLE_THREAD_SAFETY */
+
+#define DECLARE_SIGPIPE_INFO(spinfo) pqsigfunc spinfo = NULL
+
+#define DISABLE_SIGPIPE(conn, spinfo, failaction) \
+ do { \
+ if (!SIGPIPE_MASKED(conn)) \
+ spinfo = pqsignal(SIGPIPE, SIG_IGN); \
+ } while (0)
+
+#define REMEMBER_EPIPE(spinfo, cond)
+
+#define RESTORE_SIGPIPE(conn, spinfo) \
+ do { \
+ if (!SIGPIPE_MASKED(conn)) \
+ pqsignal(SIGPIPE, spinfo); \
+ } while (0)
+
+#endif /* ENABLE_THREAD_SAFETY */
+
+#else /* WIN32 */
+
+#define DECLARE_SIGPIPE_INFO(spinfo)
+#define DISABLE_SIGPIPE(conn, spinfo, failaction)
+#define REMEMBER_EPIPE(spinfo, cond)
+#define RESTORE_SIGPIPE(conn, spinfo)
+
+#endif /* WIN32 */
/* ------------------------------------------------------------ */
/* Procedures common to all secure sessions */
/*
- * Exported (but as yet undocumented) function to allow application to
- * tell us it's already initialized OpenSSL.
+ * Exported function to allow application to tell us it's already
+ * initialized OpenSSL.
*/
void
PQinitSSL(int do_init)
+{
+ PQinitOpenSSL(do_init, do_init);
+}
+
+/*
+ * Exported function to allow application to tell us it's already
+ * initialized OpenSSL and/or libcrypto.
+ */
+void
+PQinitOpenSSL(int do_ssl, int do_crypto)
{
#ifdef USE_SSL
- pq_initssllib = do_init;
+#ifdef ENABLE_THREAD_SAFETY
+
+ /*
+ * Disallow changing the flags while we have open connections, else we'd
+ * get completely confused.
+ */
+ if (ssl_open_connections != 0)
+ return;
+#endif
+
+ pq_init_ssl_lib = do_ssl;
+ pq_init_crypto_lib = do_crypto;
#endif
}
pqsecure_destroy(void)
{
#ifdef USE_SSL
- destroy_SSL();
+ destroySSL();
#endif
}
/* First time through? */
if (conn->ssl == NULL)
{
+ /* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
+ conn->sigpipe_flag = false;
+
if (!(conn->ssl = SSL_new(SSL_context)) ||
!SSL_set_app_data(conn->ssl, conn) ||
!SSL_set_fd(conn->ssl, conn->sock))
char *err = SSLerrmessage();
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not establish SSL connection: %s\n"),
+ libpq_gettext("could not establish SSL connection: %s\n"),
err);
SSLerrfree(err);
close_SSL(conn);
return PGRES_POLLING_FAILED;
}
+
/*
- * Initialize errorMessage to empty. This allows open_client_SSL()
- * to detect whether client_cert_cb() has stored a message.
+ * Initialize errorMessage to empty. This allows open_client_SSL() to
+ * detect whether client_cert_cb() has stored a message.
*/
resetPQExpBuffer(&conn->errorMessage);
}
if (conn->ssl)
{
int err;
+ DECLARE_SIGPIPE_INFO(spinfo);
+
+ /* SSL_read can write to the socket, so we need to disable SIGPIPE */
+ DISABLE_SIGPIPE(conn, spinfo, return -1);
rloop:
n = SSL_read(conn->ssl, ptr, len);
case SSL_ERROR_WANT_WRITE:
/*
- * Returning 0 here would cause caller to wait for
- * read-ready, which is not correct since what SSL wants
- * is wait for write-ready. The former could get us stuck
- * in an infinite wait, so don't risk it; busy-loop
- * instead.
+ * Returning 0 here would cause caller to wait for read-ready,
+ * which is not correct since what SSL wants is wait for
+ * write-ready. The former could get us stuck in an infinite
+ * wait, so don't risk it; busy-loop instead.
*/
goto rloop;
case SSL_ERROR_SYSCALL:
char sebuf[256];
if (n == -1)
+ {
+ REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE);
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL SYSCALL error: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ }
else
{
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+ libpq_gettext("SSL SYSCALL error: EOF detected\n"));
SOCK_ERRNO_SET(ECONNRESET);
n = -1;
char *err = SSLerrmessage();
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL error: %s\n"), err);
+ libpq_gettext("SSL error: %s\n"), err);
SSLerrfree(err);
}
/* fall through */
break;
default:
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("unrecognized SSL error code: %d\n"),
+ libpq_gettext("unrecognized SSL error code: %d\n"),
err);
n = -1;
break;
}
+
+ RESTORE_SIGPIPE(conn, spinfo);
}
else
#endif
pqsecure_write(PGconn *conn, const void *ptr, size_t len)
{
ssize_t n;
-
-#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
-#ifndef WIN32
- pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
-#endif
-#endif
+ DECLARE_SIGPIPE_INFO(spinfo);
#ifdef USE_SSL
if (conn->ssl)
{
int err;
+ DISABLE_SIGPIPE(conn, spinfo, return -1);
+
n = SSL_write(conn->ssl, ptr, len);
err = SSL_get_error(conn->ssl, n);
switch (err)
/*
* Returning 0 here causes caller to wait for write-ready,
- * which is not really the right thing, but it's the best
- * we can do.
+ * which is not really the right thing, but it's the best we
+ * can do.
*/
n = 0;
break;
if (n == -1)
{
-#ifdef ENABLE_THREAD_SAFETY
- if (SOCK_ERRNO == EPIPE)
- got_epipe = true;
-#endif
+ REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE);
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL SYSCALL error: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
}
else
{
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+ libpq_gettext("SSL SYSCALL error: EOF detected\n"));
SOCK_ERRNO_SET(ECONNRESET);
n = -1;
}
char *err = SSLerrmessage();
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL error: %s\n"), err);
+ libpq_gettext("SSL error: %s\n"), err);
SSLerrfree(err);
}
/* fall through */
break;
default:
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("unrecognized SSL error code: %d\n"),
+ libpq_gettext("unrecognized SSL error code: %d\n"),
err);
n = -1;
break;
else
#endif
{
- n = send(conn->sock, ptr, len, 0);
-#ifdef ENABLE_THREAD_SAFETY
- if (n < 0 && SOCK_ERRNO == EPIPE)
- got_epipe = true;
-#endif
+ int flags = 0;
+
+#ifdef MSG_NOSIGNAL
+ if (conn->sigpipe_flag)
+ flags |= MSG_NOSIGNAL;
+
+retry_masked:
+
+#endif /* MSG_NOSIGNAL */
+
+ DISABLE_SIGPIPE(conn, spinfo, return -1);
+
+ n = send(conn->sock, ptr, len, flags);
+
+ if (n < 0)
+ {
+ /*
+ * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't
+ * available on this machine. So, clear sigpipe_flag so we don't
+ * try the flag again, and retry the send().
+ */
+#ifdef MSG_NOSIGNAL
+ if (flags != 0 && SOCK_ERRNO == EINVAL)
+ {
+ conn->sigpipe_flag = false;
+ flags = 0;
+ goto retry_masked;
+ }
+#endif /* MSG_NOSIGNAL */
+
+ REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE);
+ }
}
-#ifdef ENABLE_THREAD_SAFETY
- pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe);
-#else
-#ifndef WIN32
- pqsignal(SIGPIPE, oldsighandler);
-#endif
-#endif
+ RESTORE_SIGPIPE(conn, spinfo);
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;
- socklen_t 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;
- }
+ /* 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;
- /* weird, but legal case */
- if (addr.sa_family == AF_UNIX)
+ if (lenpat > lenstr)
+ /* If pattern is longer than the string, we can never match */
return 0;
- {
- struct hostent hpstr;
- char buf[BUFSIZ];
- int herrno = 0;
+ if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 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().
+ * If string does not end in pattern (minus the wildcard), we don't
+ * match
*/
- pqGethostbyname(conn->peer_cn, &hpstr, buf, sizeof(buf),
- &h, &herrno);
- }
-
- /* 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;
- }
-
- /* 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;
-
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("unsupported protocol\n"));
- return -1;
- }
+ return 0;
- /*
- * the prior test should be definitive, but in practice it sometimes
- * fails. So we also check the aliases.
- */
- for (s = h->h_aliases; *s != NULL; s++)
- {
- if (pg_strcasecmp(conn->peer_cn, *s) == 0)
- return 0;
- }
+ if (strchr(string, '.') < string + lenstr - lenpat)
- /* generate protocol-aware error message */
- switch (addr.sa_family)
- {
- 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:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "server common name \"%s\" does not resolve to peer address\n"),
- conn->peer_cn);
- }
+ /*
+ * If there is a dot left of where the pattern started to match, we
+ * don't match (rule 3)
+ */
+ return 0;
- return -1;
+ /* String ended with pattern, and didn't have a dot before, so we match */
+ return 1;
}
-#endif /* NOT_USED */
+
/*
- * Load precomputed DH parameters.
- *
- * To prevent "downgrade" attacks, we perform a number of checks
- * to verify that the DBA-generated DH parameters file contains
- * what we expect it to contain.
+ * Verify that common name resolves to peer.
*/
-static DH *
-load_dh_file(int keylength)
+static bool
+verify_peer_name_matches_certificate(PGconn *conn)
{
- char homedir[MAXPGPATH];
- char fnbuf[MAXPGPATH];
- FILE *fp;
- DH *dh;
- int codes;
-
- if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
- return NULL;
-
- /* attempt to open file. It's not an error if it doesn't exist. */
- snprintf(fnbuf, sizeof(fnbuf), DHFILEPATTERN, homedir, keylength);
-
- if ((fp = fopen(fnbuf, "r")) == NULL)
- return NULL;
-
-/* flock(fileno(fp), LOCK_SH); */
- dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
-/* flock(fileno(fp), LOCK_UN); */
- fclose(fp);
-
- /* is the prime the correct size? */
- if (dh != NULL && 8 * DH_size(dh) < keylength)
- dh = NULL;
+ /*
+ * If told not to verify the peer name, don't do it. Return 0 indicating
+ * that the verification was successful.
+ */
+ if (strcmp(conn->sslmode, "verify-full") != 0)
+ return true;
- /* make sure the DH parameters are usable */
- if (dh != NULL)
+ if (conn->pghostaddr)
{
- if (DH_check(dh, &codes))
- return NULL;
- if (codes & DH_CHECK_P_NOT_PRIME)
- return NULL;
- if ((codes & DH_NOT_SUITABLE_GENERATOR) &&
- (codes & DH_CHECK_P_NOT_SAFE_PRIME))
- return NULL;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("verified SSL connections are only supported when connecting to a host name"));
+ return false;
}
-
- return dh;
-}
-
-/*
- * Load hardcoded DH parameters.
- *
- * To prevent problems if the DH parameters files don't even
- * exist, we can load DH parameters hardcoded into this file.
- */
-static DH *
-load_dh_buffer(const char *buffer, size_t len)
-{
- BIO *bio;
- DH *dh = NULL;
-
- bio = BIO_new_mem_buf((char *) buffer, len);
- if (bio == NULL)
- return NULL;
- dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
- BIO_free(bio);
-
- return dh;
-}
-
-/*
- * Generate an empheral DH key. Because this can take a long
- * time to compute, we can use precomputed parameters of the
- * common key sizes.
- *
- * Since few sites will bother to precompute these parameter
- * files, we also provide a fallback to the parameters provided
- * by the OpenSSL project.
- *
- * These values can be static (once loaded or computed) since
- * the OpenSSL library can efficiently generate random keys from
- * the information provided.
- */
-static DH *
-tmp_dh_cb(SSL *s, int is_export, int keylength)
-{
- DH *r = NULL;
- static DH *dh = NULL;
- static DH *dh512 = NULL;
- static DH *dh1024 = NULL;
- static DH *dh2048 = NULL;
- static DH *dh4096 = NULL;
-
- switch (keylength)
+ else
{
- case 512:
- if (dh512 == NULL)
- dh512 = load_dh_file(keylength);
- if (dh512 == NULL)
- dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
- r = dh512;
- break;
-
- case 1024:
- if (dh1024 == NULL)
- dh1024 = load_dh_file(keylength);
- if (dh1024 == NULL)
- dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
- r = dh1024;
- break;
-
- case 2048:
- if (dh2048 == NULL)
- dh2048 = load_dh_file(keylength);
- if (dh2048 == NULL)
- dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
- r = dh2048;
- break;
-
- case 4096:
- if (dh4096 == NULL)
- dh4096 = load_dh_file(keylength);
- if (dh4096 == NULL)
- dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
- r = dh4096;
- break;
-
- default:
- if (dh == NULL)
- dh = load_dh_file(keylength);
- r = dh;
+ /*
+ * 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 match host name \"%s\""),
+ conn->peer_cn, conn->pghost);
+ return false;
+ }
}
-
- /* this may take a long time, but it may be necessary... */
- if (r == NULL || 8 * DH_size(r) < keylength)
- r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
-
- return r;
}
/*
* 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
{
char homedir[MAXPGPATH];
struct stat buf;
+
#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)))
+ /*
+ * 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)
{
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get user information\n"));
- return 0;
+ if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not get home directory to locate client certificate files"));
+ return 0;
+ }
}
/* read the user certificate */
- snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USERCERTFILE);
- if ((fp = fopen(fnbuf, "r")) == NULL)
+ 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"),
+ 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();
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not read certificate file \"%s\": %s\n"),
+ 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, USERKEYFILE);
- if (stat(fnbuf, &buf) == -1)
+ 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)
{
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("certificate present, but not private key file \"%s\"\n"),
- fnbuf);
- return 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));
+ }
}
-#ifndef WIN32
- if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) ||
- buf.st_uid != geteuid())
+ else
{
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("private key file \"%s\" has wrong permissions\n"),
- fnbuf);
- return 0;
+ /* No PGSSLKEY specified, load default file */
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
}
-#endif
- if ((fp = fopen(fnbuf, "r")) == NULL)
+
+ if (fnbuf[0] != '\0')
{
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not open private key file \"%s\": %s\n"),
- fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
- return 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 (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;
- }
+ 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 (PEM_read_PrivateKey(fp, pkey, cb, 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;
+ 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);
}
- fclose(fp);
/* verify that the cert and key go together */
- if (!X509_check_private_key(*x509, *pkey))
+ 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"),
+ 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
+ */
static unsigned long
pq_threadidcallback(void)
{
+ /*
+ * This is not standards-compliant. pthread_self() returns pthread_t, and
+ * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
+ * it, so we have to do it.
+ */
return (unsigned long) pthread_self();
}
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 */
+/*
+ * Initialize SSL system. 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.
+ */
static int
init_ssl_system(PGconn *conn)
{
#ifdef ENABLE_THREAD_SAFETY
-#ifndef WIN32
- static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-#else
- static pthread_mutex_t init_mutex = NULL;
- static long mutex_initlock = 0;
-
- if (init_mutex == NULL)
+#ifdef WIN32
+ /* Also see similar code in fe-connect.c, default_threadlock() */
+ if (ssl_config_mutex == NULL)
{
- while (InterlockedExchange(&mutex_initlock, 1) == 1)
+ while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
/* loop, another thread own the lock */ ;
- if (init_mutex == NULL)
- pthread_mutex_init(&init_mutex, NULL);
- InterlockedExchange(&mutex_initlock, 0);
+ if (ssl_config_mutex == NULL)
+ {
+ if (pthread_mutex_init(&ssl_config_mutex, NULL))
+ return -1;
+ }
+ InterlockedExchange(&win32_ssl_create_mutex, 0);
}
#endif
- pthread_mutex_lock(&init_mutex);
+ if (pthread_mutex_lock(&ssl_config_mutex))
+ return -1;
- if (pq_initssllib && pq_lockarray == NULL)
+ if (pq_init_crypto_lib)
{
- int i;
+ /*
+ * If necessary, set up an array to hold locks for libcrypto.
+ * libcrypto will tell us how big to make this array.
+ */
+ if (pq_lockarray == NULL)
+ {
+ int i;
- CRYPTO_set_id_callback(pq_threadidcallback);
+ pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
+ if (!pq_lockarray)
+ {
+ pthread_mutex_unlock(&ssl_config_mutex);
+ return -1;
+ }
+ for (i = 0; i < CRYPTO_num_locks(); i++)
+ {
+ if (pthread_mutex_init(&pq_lockarray[i], NULL))
+ {
+ free(pq_lockarray);
+ pq_lockarray = NULL;
+ pthread_mutex_unlock(&ssl_config_mutex);
+ return -1;
+ }
+ }
+ }
- pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
- if (!pq_lockarray)
+ if (ssl_open_connections++ == 0)
{
- pthread_mutex_unlock(&init_mutex);
- return -1;
+ /* These are only required for threaded libcrypto applications */
+ CRYPTO_set_id_callback(pq_threadidcallback);
+ CRYPTO_set_locking_callback(pq_lockingcallback);
}
- for (i = 0; i < CRYPTO_num_locks(); i++)
- pthread_mutex_init(&pq_lockarray[i], NULL);
-
- CRYPTO_set_locking_callback(pq_lockingcallback);
}
-#endif
+#endif /* ENABLE_THREAD_SAFETY */
+
if (!SSL_context)
{
- if (pq_initssllib)
+ if (pq_init_ssl_lib)
{
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+ OPENSSL_config(NULL);
+#endif
SSL_library_init();
SSL_load_error_strings();
}
char *err = SSLerrmessage();
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not create SSL context: %s\n"),
+ libpq_gettext("could not create SSL context: %s\n"),
err);
SSLerrfree(err);
#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&init_mutex);
+ pthread_mutex_unlock(&ssl_config_mutex);
#endif
return -1;
}
}
+
#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&init_mutex);
+ pthread_mutex_unlock(&ssl_config_mutex);
#endif
return 0;
}
/*
- * Initialize global SSL context.
+ * This function is needed because if the libpq library is unloaded
+ * from the application, the callback functions will no longer exist when
+ * libcrypto is used by other parts of the system. For this reason,
+ * we unregister the callback functions when the last libpq
+ * connection is closed. (The same would apply for OpenSSL callbacks
+ * if we had any.)
+ *
+ * Callbacks are only set when we're compiled in threadsafe mode, so
+ * we only need to remove them in this case.
+ */
+static void
+destroy_ssl_system(void)
+{
+#ifdef ENABLE_THREAD_SAFETY
+ /* Mutex is created in initialize_ssl_system() */
+ if (pthread_mutex_lock(&ssl_config_mutex))
+ return;
+
+ if (pq_init_crypto_lib && ssl_open_connections > 0)
+ --ssl_open_connections;
+
+ if (pq_init_crypto_lib && ssl_open_connections == 0)
+ {
+ /* No connections left, unregister libcrypto callbacks */
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_set_id_callback(NULL);
+
+ /*
+ * We don't free the lock array. If we get another connection in this
+ * process, we will just re-use it with the existing mutexes.
+ *
+ * This means we leak a little memory on repeated load/unload of the
+ * library.
+ */
+ }
+
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+ return;
+}
+
+/*
+ * Initialize SSL context.
*/
static int
initialize_SSL(PGconn *conn)
if (init_ssl_system(conn))
return -1;
- /* Set up to verify server cert, if root.crt is present */
- if (pqGetHomeDirectory(homedir, sizeof(homedir)))
+ /*
+ * 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.
+ */
+ if (!conn->sslrootcert || !conn->sslcrl)
{
- snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOTCERTFILE);
- if (stat(fnbuf, &buf) == 0)
+ if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
{
- if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL))
+ if (conn->sslmode[0] == 'v') /* "verify-ca" or
+ * "verify-full" */
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not get home directory to locate root certificate file"));
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ homedir[0] = '\0';
+ }
+
+ if (conn->sslrootcert)
+ strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+ else
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+
+ if (stat(fnbuf, &buf) == 0)
+ {
+ X509_STORE *cvstore;
+
+ if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read root certificate file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ return -1;
+ }
+
+ if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
+ {
+ if (conn->sslcrl)
+ 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 */
+ if (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);
+ /* if not found, silently ignore; we do not require CRL */
+#else
{
char *err = SSLerrmessage();
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not read root certificate file \"%s\": %s\n"),
- fnbuf, err);
+ libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
+ fnbuf);
SSLerrfree(err);
return -1;
}
+#endif
+ }
- SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER, verify_cb);
+ SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER, verify_cb);
+ }
+ else
+ {
+ /* stat() failed; assume cert file doesn't exist */
+ if (conn->sslmode[0] == 'v') /* "verify-ca" or "verify-full" */
+ {
+ 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;
}
}
- /* set up empheral DH keys */
- SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);
-
/* set up mechanism to provide client certificate, if available */
SSL_CTX_set_client_cert_cb(SSL_context, client_cert_cb);
return 0;
}
-/*
- * Destroy global SSL context.
- */
static void
-destroy_SSL(void)
+destroySSL(void)
{
- if (SSL_context)
- {
- SSL_CTX_free(SSL_context);
- SSL_context = NULL;
- }
+ destroy_ssl_system();
}
/*
r = SSL_connect(conn->ssl);
if (r <= 0)
{
- int err = SSL_get_error(conn->ssl, r);
+ int err = SSL_get_error(conn->ssl, r);
switch (err)
{
if (r == -1)
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL SYSCALL error: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
else
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+ libpq_gettext("SSL SYSCALL error: EOF detected\n"));
close_SSL(conn);
return PGRES_POLLING_FAILED;
}
* 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
+ * OpenSSL itself returns. So check to see if an error
* message was already stored.
*/
if (conn->errorMessage.len == 0)
default:
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("unrecognized SSL error code: %d\n"),
+ libpq_gettext("unrecognized SSL error code: %d\n"),
err);
close_SSL(conn);
return PGRES_POLLING_FAILED;
}
}
- /* 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);
char *err = SSLerrmessage();
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("certificate could not be obtained: %s\n"),
+ libpq_gettext("certificate could not be obtained: %s\n"),
err);
SSLerrfree(err);
close_SSL(conn);
conn->peer_dn, sizeof(conn->peer_dn));
conn->peer_dn[sizeof(conn->peer_dn) - 1] = '\0';
- X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+ r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
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 */
+ conn->peer_cn[SM_USER] = '\0'; /* buffer is SM_USER+1 chars! */
+ if (r == -1)
+ {
+ /* Unable to get the CN, set it to blank so it can't be used */
+ conn->peer_cn[0] = '\0';
+ }
+ else
+ {
+ /*
+ * Reject embedded NULLs in certificate common name to prevent attacks like
+ * CVE-2009-4034.
+ */
+ if (r != strlen(conn->peer_cn))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL certificate's common name contains embedded null\n"));
+ close_SSL(conn);
+ return PGRES_POLLING_FAILED;
+ }
+ }
- /*
- * 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)
{
+ DECLARE_SIGPIPE_INFO(spinfo);
+
+ DISABLE_SIGPIPE(conn, spinfo, (void) 0);
SSL_shutdown(conn->ssl);
SSL_free(conn->ssl);
conn->ssl = NULL;
+ pqsecure_destroy();
+ /* We have to assume we got EPIPE */
+ REMEMBER_EPIPE(spinfo, true);
+ RESTORE_SIGPIPE(conn, spinfo);
}
if (conn->peer)
X509_free(conn->peer);
conn->peer = NULL;
}
+
+#ifdef USE_SSL_ENGINE
+ if (conn->engine)
+ {
+ ENGINE_finish(conn->engine);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ }
+#endif
}
/*
* return NULL if it doesn't recognize the error code. We don't
* want to return NULL ever.
*/
-static char ssl_nomem[] = "Out of memory allocating error description";
+static char ssl_nomem[] = "out of memory allocating error description";
#define SSL_ERR_LEN 128
errcode = ERR_get_error();
if (errcode == 0)
{
- strcpy(errbuf, "No SSL error reported");
+ snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
return errbuf;
}
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, "SSL error code %lu", errcode);
+ snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
return errbuf;
}
}
/*
- * Return pointer to SSL object.
+ * Return pointer to OpenSSL object.
*/
-SSL *
+void *
PQgetssl(PGconn *conn)
{
if (!conn)
return NULL;
return conn->ssl;
}
-
-#else /* !USE_SSL */
+#else /* !USE_SSL */
void *
PQgetssl(PGconn *conn)
{
return NULL;
}
-
#endif /* USE_SSL */
-#ifdef ENABLE_THREAD_SAFETY
+
+#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
/*
- * Block SIGPIPE for this thread. This prevents send()/write() from exiting
+ * Block SIGPIPE for this thread. This prevents send()/write() from exiting
* the application.
*/
int
pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
{
- sigset_t sigpipe_sigset;
- sigset_t sigset;
-
+ sigset_t sigpipe_sigset;
+ sigset_t sigset;
+
sigemptyset(&sigpipe_sigset);
sigaddset(&sigpipe_sigset, SIGPIPE);
/* Is there a pending SIGPIPE? */
if (sigpending(&sigset) != 0)
return -1;
-
+
if (sigismember(&sigset, SIGPIPE))
*sigpipe_pending = true;
else
}
else
*sigpipe_pending = false;
-
+
return 0;
}
-
+
/*
* Discard any pending SIGPIPE and reset the signal mask.
*
* Note: we are effectively assuming here that the C library doesn't queue
- * up multiple SIGPIPE events. If it did, then we'd accidentally leave
+ * up multiple SIGPIPE events. If it did, then we'd accidentally leave
* ours in the queue when an event was already pending and we got another.
* As long as it doesn't queue multiple events, we're OK because the caller
* can't tell the difference.
* gotten one, pass got_epipe = TRUE.
*
* We do not want this to change errno, since if it did that could lose
- * the error code from a preceding send(). We essentially assume that if
+ * the error code from a preceding send(). We essentially assume that if
* we were able to do pq_block_sigpipe(), this can't fail.
*/
void
pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
{
- int save_errno = SOCK_ERRNO;
- int signo;
- sigset_t sigset;
+ int save_errno = SOCK_ERRNO;
+ int signo;
+ sigset_t sigset;
/* Clear SIGPIPE only if none was pending */
if (got_epipe && !sigpipe_pending)
if (sigpending(&sigset) == 0 &&
sigismember(&sigset, SIGPIPE))
{
- sigset_t sigpipe_sigset;
-
+ sigset_t sigpipe_sigset;
+
sigemptyset(&sigpipe_sigset);
sigaddset(&sigpipe_sigset, SIGPIPE);
sigwait(&sigpipe_sigset, &signo);
}
}
-
+
/* Restore saved block mask */
pthread_sigmask(SIG_SETMASK, osigset, NULL);
SOCK_ERRNO_SET(save_errno);
}
-#endif /* ENABLE_THREAD_SAFETY */
+#endif /* ENABLE_THREAD_SAFETY && !WIN32 */