case $withval in
yes)
-$as_echo "#define USE_SSL 1" >>confdefs.h
+$as_echo "#define USE_OPENSSL 1" >>confdefs.h
;;
no)
#
AC_MSG_CHECKING([whether to build with OpenSSL support])
PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
- [AC_DEFINE([USE_SSL], 1, [Define to build with (Open)SSL support. (--with-openssl)])])
+ [AC_DEFINE([USE_OPENSSL], 1, [Define to build with OpenSSL support. (--with-openssl)])])
AC_MSG_RESULT([$with_openssl])
AC_SUBST(with_openssl)
OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \
pqformat.o pqsignal.o
+ifeq ($(with_openssl),yes)
+OBJS += be-secure-openssl.o
+endif
+
include $(top_srcdir)/src/backend/common.mk
* RADIUS Authentication
*----------------------------------------------------------------
*/
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
#include <openssl/rand.h>
#endif
static int CheckRADIUSAuth(Port *port);
* already if it didn't verify ok.
*/
#ifdef USE_SSL
- if (!port->peer)
+ if (!port->peer_cert_valid)
{
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\", %s",
hostinfo, port->user_name,
- port->ssl ? _("SSL on") : _("SSL off"))));
+ port->ssl_in_use ? _("SSL on") : _("SSL off"))));
#else
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\", %s",
hostinfo, port->user_name,
port->database_name,
- port->ssl ? _("SSL on") : _("SSL off"))));
+ port->ssl_in_use ? _("SSL on") : _("SSL off"))));
#else
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\", %s",
hostinfo, port->user_name,
- port->ssl ? _("SSL on") : _("SSL off")),
+ port->ssl_in_use ? _("SSL on") : _("SSL off")),
HOSTNAME_LOOKUP_DETAIL(port)));
#else
ereport(FATAL,
errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
hostinfo, port->user_name,
port->database_name,
- port->ssl ? _("SSL on") : _("SSL off")),
+ port->ssl_in_use ? _("SSL on") : _("SSL off")),
HOSTNAME_LOOKUP_DETAIL(port)));
#else
ereport(FATAL,
/* Construct RADIUS packet */
packet->code = RADIUS_ACCESS_REQUEST;
packet->length = RADIUS_HEADER_LENGTH;
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
if (RAND_bytes(packet->vector, RADIUS_VECTOR_LENGTH) != 1)
{
ereport(LOG,
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-openssl.c
+ * functions for OpenSSL support in the backend.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/libpq/be-secure-openssl.c
+ *
+ * Since the server static private key ($DataDir/server.key)
+ * will normally be stored unencrypted so that the database
+ * backend can restart automatically, it is important that
+ * we select an algorithm that continues to provide confidentiality
+ * even if the attacker has the server's private key. Ephemeral
+ * DH (EDH) keys provide this, and in fact provide Perfect Forward
+ * Secrecy (PFS) except for situations where the session can
+ * be hijacked during a periodic handshake/renegotiation.
+ * Even that backdoor can be closed if client certificates
+ * are used (since the imposter will be unable to successfully
+ * complete renegotiation).
+ *
+ * N.B., the static private key should still be protected to
+ * the largest extent possible, to minimize the risk of
+ * impersonations.
+ *
+ * Another benefit of EDH is that it allows the backend and
+ * clients to use DSA keys. DSA keys can only provide digital
+ * signatures, not encryption, and are often acceptable in
+ * jurisdictions where RSA keys are unacceptable.
+ *
+ * The downside to EDH is that it makes it impossible to
+ * use ssldump(1) if there's a problem establishing an SSL
+ * session. In this case you'll need to temporarily disable
+ * EDH by commenting out the callback.
+ *
+ * ...
+ *
+ * Because the risk of cryptanalysis increases as large
+ * amounts of data are sent with the same session key, the
+ * session keys are periodically renegotiated.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <sys/stat.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/dh.h>
+#if SSLEAY_VERSION_NUMBER >= 0x0907000L
+#include <openssl/conf.h>
+#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+#include <openssl/ec.h>
+#endif
+
+#include "libpq/libpq.h"
+#include "tcop/tcopprot.h"
+#include "utils/memutils.h"
+
+
+
+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 verify_cb(int, X509_STORE_CTX *);
+static void info_cb(const SSL *ssl, int type, int args);
+static const char *SSLerrmessage(void);
+
+/* are we in the middle of a renegotiation? */
+static bool in_ssl_renegotiation = false;
+
+static SSL_CTX *SSL_context = NULL;
+
+/* ------------------------------------------------------------ */
+/* Hardcoded values */
+/* ------------------------------------------------------------ */
+
+/*
+ * Hardcoded DH parameters, used in ephemeral 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 DBA... or an attacker... deletes the
+ * $DataDir/dh*.pem files.
+ *
+ * We could refuse SSL connections unless a good DH parameter
+ * file exists, but some clients may quietly renegotiate an
+ * unsecured connection without fully informing the user.
+ * Very uncool.
+ *
+ * Alternatively, the backend could attempt to load these files
+ * on startup if SSL is enabled - and refuse to start if any
+ * do not exist - but this would tend to piss off DBAs.
+ *
+ * If you want to create your own hardcoded DH parameters
+ * for fun and profit, review "Assigned Number for SKIP
+ * Protocols" (http://www.skip-vpn.org/spec/numbers.html)
+ * for suggestions.
+ */
+
+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";
+
+/*
+ * Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len)
+{
+ ssize_t n;
+ int err;
+
+ /*
+ * If SSL renegotiations are enabled and we're getting close to the
+ * limit, start one now; but avoid it if there's one already in
+ * progress. Request the renegotiation 1kB before the limit has
+ * actually expired.
+ */
+ if (ssl_renegotiation_limit && !in_ssl_renegotiation &&
+ port->count > (ssl_renegotiation_limit - 1) * 1024L)
+ {
+ in_ssl_renegotiation = true;
+
+ /*
+ * The way we determine that a renegotiation has completed is by
+ * observing OpenSSL's internal renegotiation counter. Make sure
+ * we start out at zero, and assume that the renegotiation is
+ * complete when the counter advances.
+ *
+ * OpenSSL provides SSL_renegotiation_pending(), but this doesn't
+ * seem to work in testing.
+ */
+ SSL_clear_num_renegotiations(port->ssl);
+
+ SSL_set_session_id_context(port->ssl, (void *) &SSL_context,
+ sizeof(SSL_context));
+ if (SSL_renegotiate(port->ssl) <= 0)
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("SSL failure during renegotiation start")));
+ else
+ {
+ int retries;
+
+ /*
+ * A handshake can fail, so be prepared to retry it, but only
+ * a few times.
+ */
+ for (retries = 0;; retries++)
+ {
+ if (SSL_do_handshake(port->ssl) > 0)
+ break; /* done */
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("SSL handshake failure on renegotiation, retrying")));
+ if (retries >= 20)
+ ereport(FATAL,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("unable to complete SSL handshake")));
+ }
+ }
+ }
+
+wloop:
+ errno = 0;
+ n = SSL_write(port->ssl, ptr, len);
+ err = SSL_get_error(port->ssl, n);
+ switch (err)
+ {
+ case SSL_ERROR_NONE:
+ port->count += n;
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+#ifdef WIN32
+ pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
+ (err == SSL_ERROR_WANT_READ) ?
+ FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
+ INFINITE);
+#endif
+ goto wloop;
+ case SSL_ERROR_SYSCALL:
+ /* leave it to caller to ereport the value of errno */
+ if (n != -1)
+ {
+ errno = ECONNRESET;
+ n = -1;
+ }
+ break;
+ case SSL_ERROR_SSL:
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("SSL error: %s", SSLerrmessage())));
+ /* fall through */
+ case SSL_ERROR_ZERO_RETURN:
+ errno = ECONNRESET;
+ n = -1;
+ break;
+ default:
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("unrecognized SSL error code: %d",
+ err)));
+ errno = ECONNRESET;
+ n = -1;
+ break;
+ }
+
+ if (n >= 0)
+ {
+ /* is renegotiation complete? */
+ if (in_ssl_renegotiation &&
+ SSL_num_renegotiations(port->ssl) >= 1)
+ {
+ in_ssl_renegotiation = false;
+ port->count = 0;
+ }
+
+ /*
+ * if renegotiation is still ongoing, and we've gone beyond the
+ * limit, kill the connection now -- continuing to use it can be
+ * considered a security problem.
+ */
+ if (in_ssl_renegotiation &&
+ port->count > ssl_renegotiation_limit * 1024L)
+ ereport(FATAL,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("SSL failed to renegotiate connection before limit expired")));
+ }
+
+ return n;
+}
+
+/* ------------------------------------------------------------ */
+/* OpenSSL specific code */
+/* ------------------------------------------------------------ */
+
+/*
+ * Private substitute BIO: this does the sending and receiving using send() and
+ * recv() instead. This is so that we can enable and disable interrupts
+ * just while calling recv(). We cannot have interrupts occurring while
+ * the bulk of openssl runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+ int res = 0;
+
+ if (buf != NULL)
+ {
+ res = secure_raw_read(((Port *)h->ptr), buf, size);
+ BIO_clear_retry_flags(h);
+ if (res <= 0)
+ {
+ /* If we were interrupted, tell caller to retry */
+ if (errno == EINTR)
+ {
+ BIO_set_retry_read(h);
+ }
+ }
+ }
+
+ return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+ int res = 0;
+
+ res = secure_raw_write(((Port *) h->ptr), buf, size);
+ BIO_clear_retry_flags(h);
+ if (res <= 0)
+ {
+ if (errno == EINTR)
+ {
+ BIO_set_retry_write(h);
+ }
+ }
+
+ return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+ if (!my_bio_initialized)
+ {
+ memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
+ my_bio_methods.bread = my_sock_read;
+ my_bio_methods.bwrite = my_sock_write;
+ my_bio_initialized = true;
+ }
+ return &my_bio_methods;
+}
+
+/* This should exactly match openssl's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(Port *port, int fd)
+{
+ int ret = 0;
+ BIO *bio = NULL;
+
+ bio = BIO_new(my_BIO_s_socket());
+
+ if (bio == NULL)
+ {
+ SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+ goto err;
+ }
+ /* Use 'ptr' to store pointer to PGconn */
+ bio->ptr = port;
+
+ BIO_set_fd(bio, fd, BIO_NOCLOSE);
+ SSL_set_bio(port->ssl, bio, bio);
+ ret = 1;
+err:
+ return ret;
+}
+
+/*
+ * 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.
+ */
+static DH *
+load_dh_file(int keylength)
+{
+ FILE *fp;
+ char fnbuf[MAXPGPATH];
+ DH *dh = NULL;
+ int codes;
+
+ /* attempt to open file. It's not an error if it doesn't exist. */
+ snprintf(fnbuf, sizeof(fnbuf), "dh%d.pem", 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)
+ {
+ elog(LOG, "DH errors (%s): %d bits expected, %d bits found",
+ fnbuf, keylength, 8 * DH_size(dh));
+ dh = NULL;
+ }
+
+ /* make sure the DH parameters are usable */
+ if (dh != NULL)
+ {
+ if (DH_check(dh, &codes) == 0)
+ {
+ elog(LOG, "DH_check error (%s): %s", fnbuf, SSLerrmessage());
+ return NULL;
+ }
+ if (codes & DH_CHECK_P_NOT_PRIME)
+ {
+ elog(LOG, "DH error (%s): p is not prime", fnbuf);
+ return NULL;
+ }
+ if ((codes & DH_NOT_SUITABLE_GENERATOR) &&
+ (codes & DH_CHECK_P_NOT_SAFE_PRIME))
+ {
+ elog(LOG,
+ "DH error (%s): neither suitable generator or safe prime",
+ fnbuf);
+ return NULL;
+ }
+ }
+
+ 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);
+ if (dh == NULL)
+ ereport(DEBUG2,
+ (errmsg_internal("DH load buffer: %s",
+ SSLerrmessage())));
+ BIO_free(bio);
+
+ return dh;
+}
+
+/*
+ * Generate an ephemeral 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)
+ {
+ 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;
+ }
+
+ /* this may take a long time, but it may be necessary... */
+ if (r == NULL || 8 * DH_size(r) < keylength)
+ {
+ ereport(DEBUG2,
+ (errmsg_internal("DH: generating parameters (%d bits)",
+ keylength)));
+ r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
+ }
+
+ return r;
+}
+
+/*
+ * Certificate verification callback
+ *
+ * This callback allows us to log intermediate problems during
+ * verification, but for now we'll see if the final error message
+ * contains enough information.
+ *
+ * This callback also allows us to override the default acceptance
+ * criteria (e.g., accepting self-signed or expired certs), but
+ * for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+ return ok;
+}
+
+/*
+ * This callback is used to copy SSL information messages
+ * into the PostgreSQL log.
+ */
+static void
+info_cb(const SSL *ssl, int type, int args)
+{
+ switch (type)
+ {
+ case SSL_CB_HANDSHAKE_START:
+ ereport(DEBUG4,
+ (errmsg_internal("SSL: handshake start")));
+ break;
+ case SSL_CB_HANDSHAKE_DONE:
+ ereport(DEBUG4,
+ (errmsg_internal("SSL: handshake done")));
+ break;
+ case SSL_CB_ACCEPT_LOOP:
+ ereport(DEBUG4,
+ (errmsg_internal("SSL: accept loop")));
+ break;
+ case SSL_CB_ACCEPT_EXIT:
+ ereport(DEBUG4,
+ (errmsg_internal("SSL: accept exit (%d)", args)));
+ break;
+ case SSL_CB_CONNECT_LOOP:
+ ereport(DEBUG4,
+ (errmsg_internal("SSL: connect loop")));
+ break;
+ case SSL_CB_CONNECT_EXIT:
+ ereport(DEBUG4,
+ (errmsg_internal("SSL: connect exit (%d)", args)));
+ break;
+ case SSL_CB_READ_ALERT:
+ ereport(DEBUG4,
+ (errmsg_internal("SSL: read alert (0x%04x)", args)));
+ break;
+ case SSL_CB_WRITE_ALERT:
+ ereport(DEBUG4,
+ (errmsg_internal("SSL: write alert (0x%04x)", args)));
+ break;
+ }
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+static void
+initialize_ecdh(void)
+{
+ EC_KEY *ecdh;
+ int nid;
+
+ nid = OBJ_sn2nid(SSLECDHCurve);
+ if (!nid)
+ ereport(FATAL,
+ (errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+
+ ecdh = EC_KEY_new_by_curve_name(nid);
+ if (!ecdh)
+ ereport(FATAL,
+ (errmsg("ECDH: could not create key")));
+
+ SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
+ SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+ EC_KEY_free(ecdh);
+}
+#else
+#define initialize_ecdh()
+#endif
+
+/*
+ * Initialize global SSL context.
+ */
+void
+be_tls_init(void)
+{
+ struct stat buf;
+
+ STACK_OF(X509_NAME) *root_cert_list = NULL;
+
+ if (!SSL_context)
+ {
+#if SSLEAY_VERSION_NUMBER >= 0x0907000L
+ OPENSSL_config(NULL);
+#endif
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ /*
+ * We use SSLv23_method() because it can negotiate use of the highest
+ * mutually supported protocol version, while alternatives like
+ * TLSv1_2_method() permit only one specific version. Note that we
+ * don't actually allow SSL v2 or v3, only TLS protocols (see below).
+ */
+ SSL_context = SSL_CTX_new(SSLv23_method());
+ if (!SSL_context)
+ ereport(FATAL,
+ (errmsg("could not create SSL context: %s",
+ SSLerrmessage())));
+
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it
+ * causes unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ /*
+ * Load and verify server's certificate and private key
+ */
+ if (SSL_CTX_use_certificate_chain_file(SSL_context,
+ ssl_cert_file) != 1)
+ ereport(FATAL,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load server certificate file \"%s\": %s",
+ ssl_cert_file, SSLerrmessage())));
+
+ if (stat(ssl_key_file, &buf) != 0)
+ ereport(FATAL,
+ (errcode_for_file_access(),
+ errmsg("could not access private key file \"%s\": %m",
+ ssl_key_file)));
+
+ /*
+ * Require no public access to key file.
+ *
+ * XXX temporarily suppress check when on Windows, because there may
+ * not be proper support for Unix-y file permissions. Need to think
+ * of a reasonable check to apply on Windows. (See also the data
+ * directory permission check in postmaster.c)
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+ ereport(FATAL,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" has group or world access",
+ ssl_key_file),
+ errdetail("Permissions should be u=rw (0600) or less.")));
+#endif
+
+ if (SSL_CTX_use_PrivateKey_file(SSL_context,
+ ssl_key_file,
+ SSL_FILETYPE_PEM) != 1)
+ ereport(FATAL,
+ (errmsg("could not load private key file \"%s\": %s",
+ ssl_key_file, SSLerrmessage())));
+
+ if (SSL_CTX_check_private_key(SSL_context) != 1)
+ ereport(FATAL,
+ (errmsg("check of private key failed: %s",
+ SSLerrmessage())));
+ }
+
+ /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
+ SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
+ SSL_CTX_set_options(SSL_context,
+ SSL_OP_SINGLE_DH_USE |
+ SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ /* set up ephemeral ECDH keys */
+ initialize_ecdh();
+
+ /* set up the allowed cipher list */
+ if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
+ elog(FATAL, "could not set the cipher list (no valid ciphers available)");
+
+ /* Let server choose order */
+ if (SSLPreferServerCiphers)
+ SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ /*
+ * Load CA store, so we can verify client certificates if needed.
+ */
+ if (ssl_ca_file[0])
+ {
+ if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
+ (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
+ ereport(FATAL,
+ (errmsg("could not load root certificate file \"%s\": %s",
+ ssl_ca_file, SSLerrmessage())));
+ }
+
+ /*----------
+ * Load the Certificate Revocation List (CRL).
+ * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
+ *----------
+ */
+ if (ssl_crl_file[0])
+ {
+ X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
+
+ if (cvstore)
+ {
+ /* Set the flags to check against the complete CRL chain */
+ if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
+ {
+ /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
+#ifdef X509_V_FLAG_CRL_CHECK
+ X509_STORE_set_flags(cvstore,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#else
+ ereport(LOG,
+ (errmsg("SSL certificate revocation list file \"%s\" ignored",
+ ssl_crl_file),
+ errdetail("SSL library does not support certificate revocation lists.")));
+#endif
+ }
+ else
+ ereport(FATAL,
+ (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
+ ssl_crl_file, SSLerrmessage())));
+ }
+ }
+
+ if (ssl_ca_file[0])
+ {
+ /*
+ * Always ask for SSL client cert, but don't fail if it's not
+ * presented. We might fail such connections later, depending on what
+ * we find in pg_hba.conf.
+ */
+ SSL_CTX_set_verify(SSL_context,
+ (SSL_VERIFY_PEER |
+ SSL_VERIFY_CLIENT_ONCE),
+ verify_cb);
+
+ /* Set flag to remember CA store is successfully loaded */
+ ssl_loaded_verify_locations = true;
+
+ /*
+ * Tell OpenSSL to send the list of root certs we trust to clients in
+ * CertificateRequests. This lets a client with a keystore select the
+ * appropriate client certificate to send to us.
+ */
+ SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
+ }
+}
+
+/*
+ * Attempt to negotiate SSL connection.
+ */
+int
+be_tls_open_server(Port *port)
+{
+ int r;
+ int err;
+
+ Assert(!port->ssl);
+ Assert(!port->peer);
+
+ if (!(port->ssl = SSL_new(SSL_context)))
+ {
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("could not initialize SSL connection: %s",
+ SSLerrmessage())));
+ be_tls_close(port);
+ return -1;
+ }
+ if (!my_SSL_set_fd(port, port->sock))
+ {
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("could not set SSL socket: %s",
+ SSLerrmessage())));
+ be_tls_close(port);
+ return -1;
+ }
+ port->ssl_in_use = true;
+
+aloop:
+ r = SSL_accept(port->ssl);
+ if (r <= 0)
+ {
+ err = SSL_get_error(port->ssl, r);
+ switch (err)
+ {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+#ifdef WIN32
+ pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
+ (err == SSL_ERROR_WANT_READ) ?
+ FD_READ | FD_CLOSE | FD_ACCEPT : FD_WRITE | FD_CLOSE,
+ INFINITE);
+#endif
+ goto aloop;
+ case SSL_ERROR_SYSCALL:
+ if (r < 0)
+ ereport(COMMERROR,
+ (errcode_for_socket_access(),
+ errmsg("could not accept SSL connection: %m")));
+ else
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("could not accept SSL connection: EOF detected")));
+ break;
+ case SSL_ERROR_SSL:
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("could not accept SSL connection: %s",
+ SSLerrmessage())));
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("could not accept SSL connection: EOF detected")));
+ break;
+ default:
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("unrecognized SSL error code: %d",
+ err)));
+ break;
+ }
+ be_tls_close(port);
+ return -1;
+ }
+
+ port->count = 0;
+
+ /* Get client certificate, if available. */
+ port->peer = SSL_get_peer_certificate(port->ssl);
+
+ /* and extract the Common Name from it. */
+ port->peer_cn = NULL;
+ port->peer_cert_valid = false;
+ if (port->peer != NULL)
+ {
+ int len;
+
+ len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
+ NID_commonName, NULL, 0);
+ if (len != -1)
+ {
+ char *peer_cn;
+
+ peer_cn = MemoryContextAlloc(TopMemoryContext, len + 1);
+ r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
+ NID_commonName, peer_cn, len + 1);
+ peer_cn[len] = '\0';
+ if (r != len)
+ {
+ /* shouldn't happen */
+ pfree(peer_cn);
+ be_tls_close(port);
+ return -1;
+ }
+
+ /*
+ * Reject embedded NULLs in certificate common name to prevent
+ * attacks like CVE-2009-4034.
+ */
+ if (len != strlen(peer_cn))
+ {
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("SSL certificate's common name contains embedded null")));
+ pfree(peer_cn);
+ be_tls_close(port);
+ return -1;
+ }
+
+ port->peer_cn = peer_cn;
+ }
+ port->peer_cert_valid = true;
+ }
+
+ ereport(DEBUG2,
+ (errmsg("SSL connection from \"%s\"",
+ port->peer_cn ? port->peer_cn : "(anonymous)")));
+
+ /* set up debugging/info callback */
+ SSL_CTX_set_info_callback(SSL_context, info_cb);
+
+ return 0;
+}
+
+/*
+ * Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+ if (port->ssl)
+ {
+ SSL_shutdown(port->ssl);
+ SSL_free(port->ssl);
+ port->ssl = NULL;
+ port->ssl_in_use = false;
+ }
+
+ if (port->peer)
+ {
+ X509_free(port->peer);
+ port->peer = NULL;
+ }
+
+ if (port->peer_cn)
+ {
+ pfree(port->peer_cn);
+ port->peer_cn = NULL;
+ }
+}
+
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len)
+{
+ ssize_t n;
+ int err;
+
+rloop:
+ errno = 0;
+ n = SSL_read(port->ssl, ptr, len);
+ err = SSL_get_error(port->ssl, n);
+ switch (err)
+ {
+ case SSL_ERROR_NONE:
+ port->count += n;
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ if (port->noblock)
+ {
+ errno = EWOULDBLOCK;
+ n = -1;
+ break;
+ }
+#ifdef WIN32
+ pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
+ (err == SSL_ERROR_WANT_READ) ?
+ FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
+ INFINITE);
+#endif
+ goto rloop;
+ case SSL_ERROR_SYSCALL:
+ /* leave it to caller to ereport the value of errno */
+ if (n != -1)
+ {
+ errno = ECONNRESET;
+ n = -1;
+ }
+ break;
+ case SSL_ERROR_SSL:
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("SSL error: %s", SSLerrmessage())));
+ /* fall through */
+ case SSL_ERROR_ZERO_RETURN:
+ errno = ECONNRESET;
+ n = -1;
+ break;
+ default:
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("unrecognized SSL error code: %d",
+ err)));
+ errno = ECONNRESET;
+ n = -1;
+ break;
+ }
+
+ return n;
+}
+
+/*
+ * Obtain reason string for last SSL error
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code. We don't
+ * want to return NULL ever.
+ */
+static const char *
+SSLerrmessage(void)
+{
+ unsigned long errcode;
+ const char *errreason;
+ static char errbuf[32];
+
+ errcode = ERR_get_error();
+ if (errcode == 0)
+ return _("no SSL error reported");
+ errreason = ERR_reason_error_string(errcode);
+ if (errreason != NULL)
+ return errreason;
+ snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
+ return errbuf;
+}
+
* IDENTIFICATION
* src/backend/libpq/be-secure.c
*
- * Since the server static private key ($DataDir/server.key)
- * will normally be stored unencrypted so that the database
- * backend can restart automatically, it is important that
- * we select an algorithm that continues to provide confidentiality
- * even if the attacker has the server's private key. Ephemeral
- * DH (EDH) keys provide this, and in fact provide Perfect Forward
- * Secrecy (PFS) except for situations where the session can
- * be hijacked during a periodic handshake/renegotiation.
- * Even that backdoor can be closed if client certificates
- * are used (since the imposter will be unable to successfully
- * complete renegotiation).
- *
- * N.B., the static private key should still be protected to
- * the largest extent possible, to minimize the risk of
- * impersonations.
- *
- * Another benefit of EDH is that it allows the backend and
- * clients to use DSA keys. DSA keys can only provide digital
- * signatures, not encryption, and are often acceptable in
- * jurisdictions where RSA keys are unacceptable.
- *
- * The downside to EDH is that it makes it impossible to
- * use ssldump(1) if there's a problem establishing an SSL
- * session. In this case you'll need to temporarily disable
- * EDH by commenting out the callback.
- *
- * ...
- *
- * Because the risk of cryptanalysis increases as large
- * amounts of data are sent with the same session key, the
- * session keys are periodically renegotiated.
- *
*-------------------------------------------------------------------------
*/
#include <arpa/inet.h>
#endif
-#ifdef USE_SSL
-#include <openssl/ssl.h>
-#include <openssl/dh.h>
-#if SSLEAY_VERSION_NUMBER >= 0x0907000L
-#include <openssl/conf.h>
-#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
-#include <openssl/ec.h>
-#endif
-#endif /* USE_SSL */
-
#include "libpq/libpq.h"
#include "tcop/tcopprot.h"
#include "utils/memutils.h"
-#ifdef USE_SSL
-
-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 verify_cb(int, X509_STORE_CTX *);
-static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_SSL(void);
-static int open_server_SSL(Port *);
-static void close_SSL(Port *);
-static const char *SSLerrmessage(void);
-#endif
-
char *ssl_cert_file;
char *ssl_key_file;
char *ssl_ca_file;
int ssl_renegotiation_limit;
#ifdef USE_SSL
-/* are we in the middle of a renegotiation? */
-static bool in_ssl_renegotiation = false;
-
-static SSL_CTX *SSL_context = NULL;
-static bool ssl_loaded_verify_locations = false;
+bool ssl_loaded_verify_locations = false;
#endif
/* GUC variable controlling SSL cipher list */
/* GUC variable: if false, prefer client ciphers */
bool SSLPreferServerCiphers;
-/* ------------------------------------------------------------ */
-/* Hardcoded values */
-/* ------------------------------------------------------------ */
-
-/*
- * Hardcoded DH parameters, used in ephemeral 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 DBA... or an attacker... deletes the
- * $DataDir/dh*.pem files.
- *
- * We could refuse SSL connections unless a good DH parameter
- * file exists, but some clients may quietly renegotiate an
- * unsecured connection without fully informing the user.
- * Very uncool.
- *
- * Alternatively, the backend could attempt to load these files
- * on startup if SSL is enabled - and refuse to start if any
- * do not exist - but this would tend to piss off DBAs.
- *
- * If you want to create your own hardcoded DH parameters
- * for fun and profit, review "Assigned Number for SKIP
- * Protocols" (http://www.skip-vpn.org/spec/numbers.html)
- * for suggestions.
- */
-#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
-
/* ------------------------------------------------------------ */
/* Procedures common to all secure sessions */
/* ------------------------------------------------------------ */
secure_initialize(void)
{
#ifdef USE_SSL
- initialize_SSL();
+ be_tls_init();
#endif
return 0;
int r = 0;
#ifdef USE_SSL
- r = open_server_SSL(port);
+ r = be_tls_open_server(port);
#endif
return r;
secure_close(Port *port)
{
#ifdef USE_SSL
- if (port->ssl)
- close_SSL(port);
+ if (port->ssl_in_use)
+ be_tls_close(port);
#endif
}
ssize_t n;
#ifdef USE_SSL
- if (port->ssl)
+ if (port->ssl_in_use)
{
- int err;
-
-rloop:
- errno = 0;
- n = SSL_read(port->ssl, ptr, len);
- err = SSL_get_error(port->ssl, n);
- switch (err)
- {
- case SSL_ERROR_NONE:
- port->count += n;
- break;
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- if (port->noblock)
- {
- errno = EWOULDBLOCK;
- n = -1;
- break;
- }
-#ifdef WIN32
- pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
- (err == SSL_ERROR_WANT_READ) ?
- FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
- INFINITE);
-#endif
- goto rloop;
- case SSL_ERROR_SYSCALL:
- /* leave it to caller to ereport the value of errno */
- if (n != -1)
- {
- errno = ECONNRESET;
- n = -1;
- }
- break;
- case SSL_ERROR_SSL:
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("SSL error: %s", SSLerrmessage())));
- /* fall through */
- case SSL_ERROR_ZERO_RETURN:
- errno = ECONNRESET;
- n = -1;
- break;
- default:
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("unrecognized SSL error code: %d",
- err)));
- errno = ECONNRESET;
- n = -1;
- break;
- }
+ n = be_tls_read(port, ptr, len);
}
else
#endif
{
- prepare_for_client_read();
-
- n = recv(port->sock, ptr, len, 0);
-
- client_read_ended();
+ n = secure_raw_read(port, ptr, len);
}
return n;
}
-/*
- * Write data to a secure connection.
- */
ssize_t
-secure_write(Port *port, void *ptr, size_t len)
+secure_raw_read(Port *port, void *ptr, size_t len)
{
ssize_t n;
-#ifdef USE_SSL
- if (port->ssl)
- {
- int err;
-
- /*
- * If SSL renegotiations are enabled and we're getting close to the
- * limit, start one now; but avoid it if there's one already in
- * progress. Request the renegotiation 1kB before the limit has
- * actually expired.
- */
- if (ssl_renegotiation_limit && !in_ssl_renegotiation &&
- port->count > (ssl_renegotiation_limit - 1) * 1024L)
- {
- in_ssl_renegotiation = true;
-
- /*
- * The way we determine that a renegotiation has completed is by
- * observing OpenSSL's internal renegotiation counter. Make sure
- * we start out at zero, and assume that the renegotiation is
- * complete when the counter advances.
- *
- * OpenSSL provides SSL_renegotiation_pending(), but this doesn't
- * seem to work in testing.
- */
- SSL_clear_num_renegotiations(port->ssl);
-
- SSL_set_session_id_context(port->ssl, (void *) &SSL_context,
- sizeof(SSL_context));
- if (SSL_renegotiate(port->ssl) <= 0)
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("SSL failure during renegotiation start")));
- else
- {
- int retries;
-
- /*
- * A handshake can fail, so be prepared to retry it, but only
- * a few times.
- */
- for (retries = 0;; retries++)
- {
- if (SSL_do_handshake(port->ssl) > 0)
- break; /* done */
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("SSL handshake failure on renegotiation, retrying")));
- if (retries >= 20)
- ereport(FATAL,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("unable to complete SSL handshake")));
- }
- }
- }
-
-wloop:
- errno = 0;
- n = SSL_write(port->ssl, ptr, len);
- err = SSL_get_error(port->ssl, n);
- switch (err)
- {
- case SSL_ERROR_NONE:
- port->count += n;
- break;
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
-#ifdef WIN32
- pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
- (err == SSL_ERROR_WANT_READ) ?
- FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
- INFINITE);
-#endif
- goto wloop;
- case SSL_ERROR_SYSCALL:
- /* leave it to caller to ereport the value of errno */
- if (n != -1)
- {
- errno = ECONNRESET;
- n = -1;
- }
- break;
- case SSL_ERROR_SSL:
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("SSL error: %s", SSLerrmessage())));
- /* fall through */
- case SSL_ERROR_ZERO_RETURN:
- errno = ECONNRESET;
- n = -1;
- break;
- default:
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("unrecognized SSL error code: %d",
- err)));
- errno = ECONNRESET;
- n = -1;
- break;
- }
-
- if (n >= 0)
- {
- /* is renegotiation complete? */
- if (in_ssl_renegotiation &&
- SSL_num_renegotiations(port->ssl) >= 1)
- {
- in_ssl_renegotiation = false;
- port->count = 0;
- }
-
- /*
- * if renegotiation is still ongoing, and we've gone beyond the
- * limit, kill the connection now -- continuing to use it can be
- * considered a security problem.
- */
- if (in_ssl_renegotiation &&
- port->count > ssl_renegotiation_limit * 1024L)
- ereport(FATAL,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("SSL failed to renegotiate connection before limit expired")));
- }
- }
- else
-#endif
- n = send(port->sock, ptr, len, 0);
-
- return n;
-}
-
-/* ------------------------------------------------------------ */
-/* SSL specific code */
-/* ------------------------------------------------------------ */
-#ifdef USE_SSL
-
-/*
- * Private substitute BIO: this does the sending and receiving using send() and
- * recv() instead. This is so that we can enable and disable interrupts
- * just while calling recv(). We cannot have interrupts occurring while
- * the bulk of openssl runs, because it uses malloc() and possibly other
- * non-reentrant libc facilities. We also need to call send() and recv()
- * directly so it gets passed through the socket/signals layer on Win32.
- *
- * These functions are closely modelled on the standard socket BIO in OpenSSL;
- * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
- * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
- * to retry; do we need to adopt their logic for that?
- */
-
-static bool my_bio_initialized = false;
-static BIO_METHOD my_bio_methods;
-
-static int
-my_sock_read(BIO *h, char *buf, int size)
-{
- int res = 0;
-
prepare_for_client_read();
- if (buf != NULL)
- {
- res = recv(h->num, buf, size, 0);
- BIO_clear_retry_flags(h);
- if (res <= 0)
- {
- /* If we were interrupted, tell caller to retry */
- if (errno == EINTR)
- {
- BIO_set_retry_read(h);
- }
- }
- }
+ n = recv(port->sock, ptr, len, 0);
client_read_ended();
- return res;
-}
-
-static int
-my_sock_write(BIO *h, const char *buf, int size)
-{
- int res = 0;
-
- res = send(h->num, buf, size, 0);
- BIO_clear_retry_flags(h);
- if (res <= 0)
- {
- if (errno == EINTR)
- {
- BIO_set_retry_write(h);
- }
- }
-
- return res;
-}
-
-static BIO_METHOD *
-my_BIO_s_socket(void)
-{
- if (!my_bio_initialized)
- {
- memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
- my_bio_methods.bread = my_sock_read;
- my_bio_methods.bwrite = my_sock_write;
- my_bio_initialized = true;
- }
- return &my_bio_methods;
-}
-
-/* This should exactly match openssl's SSL_set_fd except for using my BIO */
-static int
-my_SSL_set_fd(SSL *s, int fd)
-{
- int ret = 0;
- BIO *bio = NULL;
-
- bio = BIO_new(my_BIO_s_socket());
-
- if (bio == NULL)
- {
- SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
- goto err;
- }
- BIO_set_fd(bio, fd, BIO_NOCLOSE);
- SSL_set_bio(s, bio, bio);
- ret = 1;
-err:
- return ret;
-}
-
-/*
- * 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.
- */
-static DH *
-load_dh_file(int keylength)
-{
- FILE *fp;
- char fnbuf[MAXPGPATH];
- DH *dh = NULL;
- int codes;
-
- /* attempt to open file. It's not an error if it doesn't exist. */
- snprintf(fnbuf, sizeof(fnbuf), "dh%d.pem", 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)
- {
- elog(LOG, "DH errors (%s): %d bits expected, %d bits found",
- fnbuf, keylength, 8 * DH_size(dh));
- dh = NULL;
- }
-
- /* make sure the DH parameters are usable */
- if (dh != NULL)
- {
- if (DH_check(dh, &codes) == 0)
- {
- elog(LOG, "DH_check error (%s): %s", fnbuf, SSLerrmessage());
- return NULL;
- }
- if (codes & DH_CHECK_P_NOT_PRIME)
- {
- elog(LOG, "DH error (%s): p is not prime", fnbuf);
- return NULL;
- }
- if ((codes & DH_NOT_SUITABLE_GENERATOR) &&
- (codes & DH_CHECK_P_NOT_SAFE_PRIME))
- {
- elog(LOG,
- "DH error (%s): neither suitable generator or safe prime",
- fnbuf);
- return NULL;
- }
- }
-
- 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);
- if (dh == NULL)
- ereport(DEBUG2,
- (errmsg_internal("DH load buffer: %s",
- SSLerrmessage())));
- BIO_free(bio);
-
- return dh;
-}
-
-/*
- * Generate an ephemeral 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)
- {
- 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;
- }
-
- /* this may take a long time, but it may be necessary... */
- if (r == NULL || 8 * DH_size(r) < keylength)
- {
- ereport(DEBUG2,
- (errmsg_internal("DH: generating parameters (%d bits)",
- keylength)));
- r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
- }
-
- return r;
-}
-
-/*
- * Certificate verification callback
- *
- * This callback allows us to log intermediate problems during
- * verification, but for now we'll see if the final error message
- * contains enough information.
- *
- * This callback also allows us to override the default acceptance
- * criteria (e.g., accepting self-signed or expired certs), but
- * for now we accept the default checks.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
-{
- return ok;
-}
-
-/*
- * This callback is used to copy SSL information messages
- * into the PostgreSQL log.
- */
-static void
-info_cb(const SSL *ssl, int type, int args)
-{
- switch (type)
- {
- case SSL_CB_HANDSHAKE_START:
- ereport(DEBUG4,
- (errmsg_internal("SSL: handshake start")));
- break;
- case SSL_CB_HANDSHAKE_DONE:
- ereport(DEBUG4,
- (errmsg_internal("SSL: handshake done")));
- break;
- case SSL_CB_ACCEPT_LOOP:
- ereport(DEBUG4,
- (errmsg_internal("SSL: accept loop")));
- break;
- case SSL_CB_ACCEPT_EXIT:
- ereport(DEBUG4,
- (errmsg_internal("SSL: accept exit (%d)", args)));
- break;
- case SSL_CB_CONNECT_LOOP:
- ereport(DEBUG4,
- (errmsg_internal("SSL: connect loop")));
- break;
- case SSL_CB_CONNECT_EXIT:
- ereport(DEBUG4,
- (errmsg_internal("SSL: connect exit (%d)", args)));
- break;
- case SSL_CB_READ_ALERT:
- ereport(DEBUG4,
- (errmsg_internal("SSL: read alert (0x%04x)", args)));
- break;
- case SSL_CB_WRITE_ALERT:
- ereport(DEBUG4,
- (errmsg_internal("SSL: write alert (0x%04x)", args)));
- break;
- }
+ return n;
}
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
-static void
-initialize_ecdh(void)
-{
- EC_KEY *ecdh;
- int nid;
-
- nid = OBJ_sn2nid(SSLECDHCurve);
- if (!nid)
- ereport(FATAL,
- (errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
-
- ecdh = EC_KEY_new_by_curve_name(nid);
- if (!ecdh)
- ereport(FATAL,
- (errmsg("ECDH: could not create key")));
-
- SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
- SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
- EC_KEY_free(ecdh);
-}
-#else
-#define initialize_ecdh()
-#endif
/*
- * Initialize global SSL context.
- */
-static void
-initialize_SSL(void)
-{
- struct stat buf;
-
- STACK_OF(X509_NAME) *root_cert_list = NULL;
-
- if (!SSL_context)
- {
-#if SSLEAY_VERSION_NUMBER >= 0x0907000L
- OPENSSL_config(NULL);
-#endif
- SSL_library_init();
- SSL_load_error_strings();
-
- /*
- * We use SSLv23_method() because it can negotiate use of the highest
- * mutually supported protocol version, while alternatives like
- * TLSv1_2_method() permit only one specific version. Note that we
- * don't actually allow SSL v2 or v3, only TLS protocols (see below).
- */
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- ereport(FATAL,
- (errmsg("could not create SSL context: %s",
- SSLerrmessage())));
-
- /*
- * Disable OpenSSL's moving-write-buffer sanity check, because it
- * causes unnecessary failures in nonblocking send cases.
- */
- SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
- /*
- * Load and verify server's certificate and private key
- */
- if (SSL_CTX_use_certificate_chain_file(SSL_context,
- ssl_cert_file) != 1)
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not load server certificate file \"%s\": %s",
- ssl_cert_file, SSLerrmessage())));
-
- if (stat(ssl_key_file, &buf) != 0)
- ereport(FATAL,
- (errcode_for_file_access(),
- errmsg("could not access private key file \"%s\": %m",
- ssl_key_file)));
-
- /*
- * Require no public access to key file.
- *
- * XXX temporarily suppress check when on Windows, because there may
- * not be proper support for Unix-y file permissions. Need to think
- * of a reasonable check to apply on Windows. (See also the data
- * directory permission check in postmaster.c)
- */
-#if !defined(WIN32) && !defined(__CYGWIN__)
- if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
- ereport(FATAL,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("private key file \"%s\" has group or world access",
- ssl_key_file),
- errdetail("Permissions should be u=rw (0600) or less.")));
-#endif
-
- if (SSL_CTX_use_PrivateKey_file(SSL_context,
- ssl_key_file,
- SSL_FILETYPE_PEM) != 1)
- ereport(FATAL,
- (errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage())));
-
- if (SSL_CTX_check_private_key(SSL_context) != 1)
- ereport(FATAL,
- (errmsg("check of private key failed: %s",
- SSLerrmessage())));
- }
-
- /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
- SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context,
- SSL_OP_SINGLE_DH_USE |
- SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
- /* set up ephemeral ECDH keys */
- initialize_ecdh();
-
- /* set up the allowed cipher list */
- if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
- elog(FATAL, "could not set the cipher list (no valid ciphers available)");
-
- /* Let server choose order */
- if (SSLPreferServerCiphers)
- SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- /*
- * Load CA store, so we can verify client certificates if needed.
- */
- if (ssl_ca_file[0])
- {
- if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
- (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
- ereport(FATAL,
- (errmsg("could not load root certificate file \"%s\": %s",
- ssl_ca_file, SSLerrmessage())));
- }
-
- /*----------
- * Load the Certificate Revocation List (CRL).
- * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
- *----------
- */
- if (ssl_crl_file[0])
- {
- X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
-
- if (cvstore)
- {
- /* Set the flags to check against the complete CRL chain */
- if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
- {
- /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
-#ifdef X509_V_FLAG_CRL_CHECK
- X509_STORE_set_flags(cvstore,
- X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
-#else
- ereport(LOG,
- (errmsg("SSL certificate revocation list file \"%s\" ignored",
- ssl_crl_file),
- errdetail("SSL library does not support certificate revocation lists.")));
-#endif
- }
- else
- ereport(FATAL,
- (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
- ssl_crl_file, SSLerrmessage())));
- }
- }
-
- if (ssl_ca_file[0])
- {
- /*
- * Always ask for SSL client cert, but don't fail if it's not
- * presented. We might fail such connections later, depending on what
- * we find in pg_hba.conf.
- */
- SSL_CTX_set_verify(SSL_context,
- (SSL_VERIFY_PEER |
- SSL_VERIFY_CLIENT_ONCE),
- verify_cb);
-
- /* Set flag to remember CA store is successfully loaded */
- ssl_loaded_verify_locations = true;
-
- /*
- * Tell OpenSSL to send the list of root certs we trust to clients in
- * CertificateRequests. This lets a client with a keystore select the
- * appropriate client certificate to send to us.
- */
- SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
- }
-}
-
-/*
- * Attempt to negotiate SSL connection.
+ * Write data to a secure connection.
*/
-static int
-open_server_SSL(Port *port)
+ssize_t
+secure_write(Port *port, void *ptr, size_t len)
{
- int r;
- int err;
-
- Assert(!port->ssl);
- Assert(!port->peer);
+ ssize_t n;
- if (!(port->ssl = SSL_new(SSL_context)))
- {
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("could not initialize SSL connection: %s",
- SSLerrmessage())));
- close_SSL(port);
- return -1;
- }
- if (!my_SSL_set_fd(port->ssl, port->sock))
+#ifdef USE_SSL
+ if (port->ssl_in_use)
{
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("could not set SSL socket: %s",
- SSLerrmessage())));
- close_SSL(port);
- return -1;
+ n = be_tls_write(port, ptr, len);
}
-
-aloop:
- r = SSL_accept(port->ssl);
- if (r <= 0)
- {
- err = SSL_get_error(port->ssl, r);
- switch (err)
- {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
-#ifdef WIN32
- pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
- (err == SSL_ERROR_WANT_READ) ?
- FD_READ | FD_CLOSE | FD_ACCEPT : FD_WRITE | FD_CLOSE,
- INFINITE);
+ else
#endif
- goto aloop;
- case SSL_ERROR_SYSCALL:
- if (r < 0)
- ereport(COMMERROR,
- (errcode_for_socket_access(),
- errmsg("could not accept SSL connection: %m")));
- else
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("could not accept SSL connection: EOF detected")));
- break;
- case SSL_ERROR_SSL:
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("could not accept SSL connection: %s",
- SSLerrmessage())));
- break;
- case SSL_ERROR_ZERO_RETURN:
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("could not accept SSL connection: EOF detected")));
- break;
- default:
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("unrecognized SSL error code: %d",
- err)));
- break;
- }
- close_SSL(port);
- return -1;
- }
-
- port->count = 0;
-
- /* Get client certificate, if available. */
- port->peer = SSL_get_peer_certificate(port->ssl);
-
- /* and extract the Common Name from it. */
- port->peer_cn = NULL;
- if (port->peer != NULL)
- {
- int len;
+ n = secure_raw_write(port, ptr, len);
- len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
- NID_commonName, NULL, 0);
- if (len != -1)
- {
- char *peer_cn;
-
- peer_cn = MemoryContextAlloc(TopMemoryContext, len + 1);
- r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
- NID_commonName, peer_cn, len + 1);
- peer_cn[len] = '\0';
- if (r != len)
- {
- /* shouldn't happen */
- pfree(peer_cn);
- close_SSL(port);
- return -1;
- }
-
- /*
- * Reject embedded NULLs in certificate common name to prevent
- * attacks like CVE-2009-4034.
- */
- if (len != strlen(peer_cn))
- {
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("SSL certificate's common name contains embedded null")));
- pfree(peer_cn);
- close_SSL(port);
- return -1;
- }
-
- port->peer_cn = peer_cn;
- }
- }
-
- ereport(DEBUG2,
- (errmsg("SSL connection from \"%s\"",
- port->peer_cn ? port->peer_cn : "(anonymous)")));
-
- /* set up debugging/info callback */
- SSL_CTX_set_info_callback(SSL_context, info_cb);
-
- return 0;
-}
-
-/*
- * Close SSL connection.
- */
-static void
-close_SSL(Port *port)
-{
- if (port->ssl)
- {
- SSL_shutdown(port->ssl);
- SSL_free(port->ssl);
- port->ssl = NULL;
- }
-
- if (port->peer)
- {
- X509_free(port->peer);
- port->peer = NULL;
- }
-
- if (port->peer_cn)
- {
- pfree(port->peer_cn);
- port->peer_cn = NULL;
- }
+ return n;
}
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code. We don't
- * want to return NULL ever.
- */
-static const char *
-SSLerrmessage(void)
+ssize_t
+secure_raw_write(Port *port, const void *ptr, size_t len)
{
- unsigned long errcode;
- const char *errreason;
- static char errbuf[32];
-
- errcode = ERR_get_error();
- if (errcode == 0)
- return _("no SSL error reported");
- errreason = ERR_reason_error_string(errcode);
- if (errreason != NULL)
- return errreason;
- snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
- return errbuf;
+ return send(port->sock, ptr, len, 0);
}
-
-#endif /* USE_SSL */
/* Check SSL state */
#ifdef USE_SSL
- if (port->ssl)
+ if (port->ssl_in_use)
{
/* Connection is SSL, match both "host" and "hostssl" */
if (hba->conntype == ctHostNoSSL)
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
#include <openssl/rand.h>
#endif
/*
* Make sure processes do not share OpenSSL randomness state.
*/
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
RAND_cleanup();
#endif
}
{
if (am_walsender)
{
-#ifdef USE_SSL
- if (port->ssl)
+#ifdef USE_OPENSSL
+ if (port->ssl_in_use)
ereport(LOG,
(errmsg("replication connection authorized: user=%s SSL enabled (protocol=%s, cipher=%s, compression=%s)",
port->user_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl),
}
else
{
-#ifdef USE_SSL
- if (port->ssl)
+#ifdef USE_OPENSSL
+ if (port->ssl_in_use)
ereport(LOG,
(errmsg("connection authorized: user=%s database=%s SSL enabled (protocol=%s, cipher=%s, compression=%s)",
port->user_name, port->database_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl),
extern char *temp_tablespaces;
extern bool ignore_checksum_failure;
extern bool synchronize_seqscans;
-extern char *SSLCipherSuites;
-extern char *SSLECDHCurve;
-extern bool SSLPreferServerCiphers;
#ifdef TRACE_SORT
extern bool trace_sort;
#include <sys/types.h> /* for umask() */
#include <sys/stat.h> /* for stat() */
#endif
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#endif
static void
printSSLInfo(void)
{
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
int sslbits = -1;
SSL *ssl;
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
#endif
/*
- * SSL structures (keep these last so that USE_SSL doesn't affect
- * locations of other fields)
+ * SSL structures (keep these last so that the locations of other fields
+ * are the same whether or not you build with SSL)
*/
#ifdef USE_SSL
+ bool ssl_in_use;
+ char *peer_cn;
+ bool peer_cert_valid;
+#endif
+#ifdef USE_OPENSSL
SSL *ssl;
X509 *peer;
- char *peer_cn;
unsigned long count;
#endif
} Port;
+#ifdef USE_SSL
+/*
+ * These functions are implemented by the glue code specific to each
+ * SSL implementation (e.g. be-secure-openssl.c)
+ */
+extern void be_tls_init(void);
+extern int be_tls_open_server(Port *port);
+extern void be_tls_close(Port *port);
+extern ssize_t be_tls_read(Port *port, void *ptr, size_t len);
+extern ssize_t be_tls_write(Port *port, void *ptr, size_t len);
+
+#endif
extern ProtocolVersion FrontendProtocol;
extern void secure_close(Port *port);
extern ssize_t secure_read(Port *port, void *ptr, size_t len);
extern ssize_t secure_write(Port *port, void *ptr, size_t len);
+extern ssize_t secure_raw_read(Port *port, void *ptr, size_t len);
+extern ssize_t secure_raw_write(Port *port, const void *ptr, size_t len);
+
+extern bool ssl_loaded_verify_locations;
+
+/* GUCs */
+extern char *SSLCipherSuites;
+extern char *SSLECDHCurve;
+extern bool SSLPreferServerCiphers;
#endif /* LIBPQ_H */
/* Define to select named POSIX semaphores. */
#undef USE_NAMED_POSIX_SEMAPHORES
+/* Define to build with OpenSSL support. (--with-openssl) */
+#undef USE_OPENSSL
+
/* Define to 1 to build with PAM support. (--with-pam) */
#undef USE_PAM
/* Use replacement snprintf() functions. */
#undef USE_REPL_SNPRINTF
-/* Define to build with (Open)SSL support. (--with-openssl) */
-#undef USE_SSL
-
/* Define to select SysV-style semaphores. */
#undef USE_SYSV_SEMAPHORES
/* Define to select named POSIX semaphores. */
/* #undef USE_NAMED_POSIX_SEMAPHORES */
+/* Define to build with OpenSSL support. (--with-openssl) */
+/* #undef USE_OPENSSL */
+
/* Define to 1 to build with PAM support. (--with-pam) */
/* #undef USE_PAM */
/* Use replacement snprintf() functions. */
#define USE_REPL_SNPRINTF 1
-/* Define to build with (Open)SSL support. (--with-openssl) */
-/* #undef USE_SSL */
-
/* Define to select SysV-style semaphores. */
/* #undef USE_SYSV_SEMAPHORES */
#define USE_PREFETCH
#endif
+/*
+ * USE_SSL code should be compiled only when compiling with an SSL
+ * implementation. (Currently, only OpenSSL is supported, but we might add
+ * more implementations in the future.)
+ */
+#ifdef USE_OPENSSL
+#define USE_SSL
+#endif
+
/*
* This is the default directory in which AF_UNIX socket files are
* placed. Caution: changing this risks breaking your existing client
# utils/mb
OBJS += encnames.o wchar.o
+ifeq ($(with_openssl),yes)
+OBJS += fe-secure-openssl.o
+endif
+
ifeq ($(PORTNAME), cygwin)
override shlib = cyg$(NAME)$(DLSUFFIX)
endif
conn->allow_ssl_try = false;
}
if (conn->allow_ssl_try && !conn->wait_ssl_try &&
- conn->ssl == NULL)
+ !conn->ssl_in_use)
{
ProtocolVersion pv;
* On first time through, get the postmaster's response to our
* SSL negotiation packet.
*/
- if (conn->ssl == NULL)
+ if (!conn->ssl_in_use)
{
/*
* We use pqReadData here since it has the logic to
* connection already, then retry with an SSL connection
*/
if (conn->sslmode[0] == 'a' /* "allow" */
- && conn->ssl == NULL
+ && !conn->ssl_in_use
&& conn->allow_ssl_try
&& conn->wait_ssl_try)
{
#ifdef USE_SSL
conn->allow_ssl_try = true;
conn->wait_ssl_try = false;
+ conn->ssl_in_use = false;
#endif
/*
*/
#ifdef USE_SSL
- if (conn->ssl)
+ if (conn->ssl_in_use)
return 0;
#endif
return -1;
}
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
/* Check for SSL library buffering read bytes */
if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
{
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-openssl.c
+ * OpenSSL support
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-secure-openssl.c
+ *
+ * NOTES
+ *
+ * We don't provide informational callbacks here (like
+ * info_cb() in be-secure.c), since there's no good mechanism to
+ * display such information to the user.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "libpq-fe.h"
+#include "fe-auth.h"
+#include "libpq-int.h"
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.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
+#endif
+
+#include <openssl/ssl.h>
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
+#include <openssl/conf.h>
+#endif
+#ifdef USE_SSL_ENGINE
+#include <openssl/engine.h>
+#endif
+
+static bool verify_peer_name_matches_certificate(PGconn *);
+static int verify_cb(int ok, X509_STORE_CTX *ctx);
+static void destroy_ssl_system(void);
+static int initialize_SSL(PGconn *conn);
+static PostgresPollingStatusType open_client_SSL(PGconn *);
+static char *SSLerrmessage(void);
+static void SSLerrfree(char *buf);
+
+static int my_sock_read(BIO *h, char *buf, int size);
+static int my_sock_write(BIO *h, const char *buf, int size);
+static BIO_METHOD *my_BIO_s_socket(void);
+static int my_SSL_set_fd(PGconn *conn, int fd);
+
+
+static bool pq_init_ssl_lib = true;
+static bool pq_init_crypto_lib = true;
+
+/*
+ * SSL_context is currently shared between threads and therefore we need to be
+ * careful to lock around any usage of it when providing thread safety.
+ * ssl_config_mutex is the mutex that we use to protect it.
+ */
+static SSL_CTX *SSL_context = NULL;
+
+#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 */
+
+
+/* ------------------------------------------------------------ */
+/* Procedures common to all secure sessions */
+/* ------------------------------------------------------------ */
+
+/*
+ * Exported function to allow application to tell us it's already
+ * initialized OpenSSL and/or libcrypto.
+ */
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+#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;
+}
+
+/*
+ * Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+ /* First time through? */
+ if (conn->ssl == NULL)
+ {
+#ifdef ENABLE_THREAD_SAFETY
+ int rc;
+#endif
+
+#ifdef ENABLE_THREAD_SAFETY
+ if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+ return PGRES_POLLING_FAILED;
+ }
+#endif
+ /* Create a connection-specific SSL object */
+ if (!(conn->ssl = SSL_new(SSL_context)) ||
+ !SSL_set_app_data(conn->ssl, conn) ||
+ !my_SSL_set_fd(conn, conn->sock))
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not establish SSL connection: %s\n"),
+ err);
+ SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+ pgtls_close(conn);
+
+ return PGRES_POLLING_FAILED;
+ }
+ conn->ssl_in_use = true;
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+
+ /*
+ * Load client certificate, private key, and trusted CA certs.
+ */
+ if (initialize_SSL(conn) != 0)
+ {
+ /* initialize_SSL already put a message in conn->errorMessage */
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+ }
+
+ /* Begin or continue the actual handshake */
+ return open_client_SSL(conn);
+}
+
+/*
+ * Read data from a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage. The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, void *ptr, size_t len)
+{
+ ssize_t n;
+ int result_errno = 0;
+ char sebuf[256];
+ int err;
+
+rloop:
+ SOCK_ERRNO_SET(0);
+ n = SSL_read(conn->ssl, ptr, len);
+ err = SSL_get_error(conn->ssl, n);
+ switch (err)
+ {
+ case SSL_ERROR_NONE:
+ if (n < 0)
+ {
+ /* Not supposed to happen, so we don't translate the msg */
+ printfPQExpBuffer(&conn->errorMessage,
+ "SSL_read failed but did not provide error information\n");
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ }
+ break;
+ case SSL_ERROR_WANT_READ:
+ n = 0;
+ break;
+ 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.
+ */
+ goto rloop;
+ case SSL_ERROR_SYSCALL:
+ if (n < 0)
+ {
+ result_errno = SOCK_ERRNO;
+ if (result_errno == EPIPE ||
+ result_errno == ECONNRESET)
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext(
+ "server closed the connection unexpectedly\n"
+ "\tThis probably means the server terminated abnormally\n"
+ "\tbefore or while processing the request.\n"));
+ else
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(result_errno,
+ sebuf, sizeof(sebuf)));
+ }
+ else
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ }
+ break;
+ case SSL_ERROR_SSL:
+ {
+ char *errm = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL error: %s\n"), errm);
+ SSLerrfree(errm);
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ }
+ case SSL_ERROR_ZERO_RETURN:
+
+ /*
+ * Per OpenSSL documentation, this error code is only returned
+ * for a clean connection closure, so we should not report it
+ * as a server crash.
+ */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL connection has been closed unexpectedly\n"));
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ default:
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unrecognized SSL error code: %d\n"),
+ err);
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ }
+
+ /* ensure we return the intended errno to caller */
+ SOCK_ERRNO_SET(result_errno);
+
+ return n;
+}
+
+/*
+ * Write data to a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage. The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+ ssize_t n;
+ int result_errno = 0;
+ char sebuf[256];
+ int err;
+
+ SOCK_ERRNO_SET(0);
+ n = SSL_write(conn->ssl, ptr, len);
+ err = SSL_get_error(conn->ssl, n);
+ switch (err)
+ {
+ case SSL_ERROR_NONE:
+ if (n < 0)
+ {
+ /* Not supposed to happen, so we don't translate the msg */
+ printfPQExpBuffer(&conn->errorMessage,
+ "SSL_write failed but did not provide error information\n");
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ }
+ break;
+ case SSL_ERROR_WANT_READ:
+
+ /*
+ * 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.
+ */
+ n = 0;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ n = 0;
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (n < 0)
+ {
+ result_errno = SOCK_ERRNO;
+ if (result_errno == EPIPE || result_errno == ECONNRESET)
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext(
+ "server closed the connection unexpectedly\n"
+ "\tThis probably means the server terminated abnormally\n"
+ "\tbefore or while processing the request.\n"));
+ else
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(result_errno,
+ sebuf, sizeof(sebuf)));
+ }
+ else
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ }
+ break;
+ case SSL_ERROR_SSL:
+ {
+ char *errm = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL error: %s\n"), errm);
+ SSLerrfree(errm);
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ }
+ case SSL_ERROR_ZERO_RETURN:
+
+ /*
+ * Per OpenSSL documentation, this error code is only returned
+ * for a clean connection closure, so we should not report it
+ * as a server crash.
+ */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL connection has been closed unexpectedly\n"));
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ default:
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unrecognized SSL error code: %d\n"),
+ err);
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ }
+
+ /* ensure we return the intended errno to caller */
+ SOCK_ERRNO_SET(result_errno);
+
+ return n;
+}
+
+/* ------------------------------------------------------------ */
+/* OpenSSL specific code */
+/* ------------------------------------------------------------ */
+
+/*
+ * Certificate verification callback
+ *
+ * This callback allows us to log intermediate problems during
+ * verification, but there doesn't seem to be a clean way to get
+ * our PGconn * structure. So we can't log anything!
+ *
+ * This callback also allows us to override the default acceptance
+ * criteria (e.g., accepting self-signed or expired certs), but
+ * for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+ return ok;
+}
+
+
+/*
+ * 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 case-insensitive, since DNS is case insensitive.
+ */
+static int
+wildcard_certificate_match(const char *pattern, const char *string)
+{
+ int lenpat = strlen(pattern);
+ int lenstr = strlen(string);
+
+ /* 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;
+
+ if (lenpat > lenstr)
+ /* If pattern is longer than the string, we can never match */
+ return 0;
+
+ 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;
+
+ 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;
+
+ /* String ended with pattern, and didn't have a dot before, so we match */
+ return 1;
+}
+
+
+/*
+ * Verify that common name resolves to peer.
+ */
+static bool
+verify_peer_name_matches_certificate(PGconn *conn)
+{
+ char *peer_cn;
+ int r;
+ int len;
+ bool result;
+
+ /*
+ * If told not to verify the peer name, don't do it. Return true
+ * indicating that the verification was successful.
+ */
+ if (strcmp(conn->sslmode, "verify-full") != 0)
+ return true;
+
+ /*
+ * Extract the common name from the certificate.
+ *
+ * XXX: Should support alternate names here
+ */
+ /* First find out the name's length and allocate a buffer for it. */
+ len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+ NID_commonName, NULL, 0);
+ if (len == -1)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not get server common name from server certificate\n"));
+ return false;
+ }
+ peer_cn = malloc(len + 1);
+ if (peer_cn == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return false;
+ }
+
+ r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+ NID_commonName, peer_cn, len + 1);
+ if (r != len)
+ {
+ /* Got different length than on the first call. Shouldn't happen. */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not get server common name from server certificate\n"));
+ free(peer_cn);
+ return false;
+ }
+ peer_cn[len] = '\0';
+
+ /*
+ * Reject embedded NULLs in certificate common name to prevent attacks
+ * like CVE-2009-4034.
+ */
+ if (len != strlen(peer_cn))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL certificate's common name contains embedded null\n"));
+ free(peer_cn);
+ return false;
+ }
+
+ /*
+ * We got the peer's common name. Now compare it against the originally
+ * given hostname.
+ */
+ if (!(conn->pghost && conn->pghost[0] != '\0'))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("host name must be specified for a verified SSL connection\n"));
+ result = false;
+ }
+ else
+ {
+ if (pg_strcasecmp(peer_cn, conn->pghost) == 0)
+ /* Exact name match */
+ result = true;
+ else if (wildcard_certificate_match(peer_cn, conn->pghost))
+ /* Matched wildcard certificate */
+ result = true;
+ else
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"),
+ peer_cn, conn->pghost);
+ result = false;
+ }
+ }
+
+ free(peer_cn);
+ return result;
+}
+
+#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();
+}
+
+static pthread_mutex_t *pq_lockarray;
+
+static void
+pq_lockingcallback(int mode, int n, const char *file, int line)
+{
+ if (mode & CRYPTO_LOCK)
+ {
+ if (pthread_mutex_lock(&pq_lockarray[n]))
+ PGTHREAD_ERROR("failed to lock mutex");
+ }
+ else
+ {
+ if (pthread_mutex_unlock(&pq_lockarray[n]))
+ PGTHREAD_ERROR("failed to unlock mutex");
+ }
+}
+#endif /* ENABLE_THREAD_SAFETY */
+
+/*
+ * Initialize SSL system, in particular creating the SSL_context object
+ * that will be shared by all SSL-using connections in this process.
+ *
+ * In threadsafe mode, this includes setting up libcrypto callback functions
+ * to do thread locking.
+ *
+ * If the caller has told us (through PQinitOpenSSL) that he's taking care
+ * of libcrypto, we expect that callbacks are already set, and won't try to
+ * override it.
+ *
+ * The conn parameter is only used to be able to pass back an error
+ * message - no connection-local setup is made here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+int
+pgtls_init(PGconn *conn)
+{
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+ /* Also see similar code in fe-connect.c, default_threadlock() */
+ if (ssl_config_mutex == NULL)
+ {
+ while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
+ /* loop, another thread own the lock */ ;
+ if (ssl_config_mutex == NULL)
+ {
+ if (pthread_mutex_init(&ssl_config_mutex, NULL))
+ return -1;
+ }
+ InterlockedExchange(&win32_ssl_create_mutex, 0);
+ }
+#endif
+ if (pthread_mutex_lock(&ssl_config_mutex))
+ return -1;
+
+ if (pq_init_crypto_lib)
+ {
+ /*
+ * 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;
+
+ 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;
+ }
+ }
+ }
+
+ if (ssl_open_connections++ == 0)
+ {
+ /* These are only required for threaded libcrypto applications */
+ CRYPTO_set_id_callback(pq_threadidcallback);
+ CRYPTO_set_locking_callback(pq_lockingcallback);
+ }
+ }
+#endif /* ENABLE_THREAD_SAFETY */
+
+ if (!SSL_context)
+ {
+ if (pq_init_ssl_lib)
+ {
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+ OPENSSL_config(NULL);
+#endif
+ SSL_library_init();
+ SSL_load_error_strings();
+ }
+
+ /*
+ * We use SSLv23_method() because it can negotiate use of the highest
+ * mutually supported protocol version, while alternatives like
+ * TLSv1_2_method() permit only one specific version. Note that we
+ * don't actually allow SSL v2 or v3, only TLS protocols (see below).
+ */
+ SSL_context = SSL_CTX_new(SSLv23_method());
+ if (!SSL_context)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not create SSL context: %s\n"),
+ err);
+ SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+ return -1;
+ }
+
+ /* Disable old protocol versions */
+ SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it
+ * causes unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ }
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+ return 0;
+}
+
+/*
+ * 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 or the SSL_context. If we get another
+ * connection in this process, we will just re-use them with the
+ * existing mutexes.
+ *
+ * This means we leak a little memory on repeated load/unload of the
+ * library.
+ */
+ }
+
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+}
+
+/*
+ * Initialize (potentially) per-connection SSL data, namely the
+ * client certificate, private key, and trusted CA certs.
+ *
+ * conn->ssl must already be created. It receives the connection's client
+ * certificate and private key. Note however that certificates also get
+ * loaded into the SSL_context object, and are therefore accessible to all
+ * connections in this process. This should be OK as long as there aren't
+ * any hash collisions among the certs.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+static int
+initialize_SSL(PGconn *conn)
+{
+ struct stat buf;
+ char homedir[MAXPGPATH];
+ char fnbuf[MAXPGPATH];
+ 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
+ {
+ /*
+ * Cert file exists, so load it. Since OpenSSL doesn't provide the
+ * equivalent of "SSL_use_certificate_chain_file", we actually have to
+ * load the file twice. The first call loads any extra certs after
+ * the first one into chain-cert storage associated with the
+ * SSL_context. The second call loads the first cert (only) into the
+ * SSL object, where it will be correctly paired with the private key
+ * we load below. We do it this way so that each connection
+ * understands which subject cert to present, in case different
+ * sslcert settings are used for different connections in the same
+ * process.
+ *
+ * NOTE: This function may also modify our SSL_context and therefore
+ * we have to lock around this call and any places where we use the
+ * SSL_context struct.
+ */
+#ifdef ENABLE_THREAD_SAFETY
+ int rc;
+
+ if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+ return -1;
+ }
+#endif
+ if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read certificate file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+ return -1;
+ }
+
+ if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read certificate file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+ return -1;
+ }
+
+ /* 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 (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+ {
+#ifdef USE_SSL_ENGINE
+ if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+ && conn->sslkey[1] != ':'
+#endif
+ )
+ {
+ /* Colon, but not in second character, treat as engine:key */
+ char *engine_str = strdup(conn->sslkey);
+ char *engine_colon;
+
+ 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();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not load SSL engine \"%s\": %s\n"),
+ engine_str, err);
+ SSLerrfree(err);
+ free(engine_str);
+ return -1;
+ }
+
+ if (ENGINE_init(conn->engine) == 0)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
+ engine_str, err);
+ SSLerrfree(err);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ free(engine_str);
+ return -1;
+ }
+
+ pkey = ENGINE_load_private_key(conn->engine, engine_colon,
+ NULL, NULL);
+ if (pkey == NULL)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+ engine_colon, engine_str, err);
+ SSLerrfree(err);
+ ENGINE_finish(conn->engine);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ free(engine_str);
+ return -1;
+ }
+ if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+ engine_colon, engine_str, err);
+ SSLerrfree(err);
+ ENGINE_finish(conn->engine);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ free(engine_str);
+ return -1;
+ }
+
+ free(engine_str);
+
+ fnbuf[0] = '\0'; /* indicate we're not going to load from a
+ * file */
+ }
+ else
+#endif /* USE_SSL_ENGINE */
+ {
+ /* PGSSLKEY is not an engine, treat it as a filename */
+ strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+ }
+ }
+ else 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)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+ fnbuf);
+ return -1;
+ }
+#ifndef WIN32
+ if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+ fnbuf);
+ return -1;
+ }
+#endif
+
+ if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not load private key file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ return -1;
+ }
+ }
+
+ /* verify that the cert and key go together */
+ if (have_cert &&
+ SSL_check_private_key(conn->ssl) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ return -1;
+ }
+
+ /*
+ * If the root cert file exists, load it so we can perform certificate
+ * verification. If sslmode is "verify-full" we will also do further
+ * verification after the connection has been completed.
+ */
+ if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+ strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+ else if (have_homedir)
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+ else
+ fnbuf[0] = '\0';
+
+ if (fnbuf[0] != '\0' &&
+ stat(fnbuf, &buf) == 0)
+ {
+ X509_STORE *cvstore;
+
+#ifdef ENABLE_THREAD_SAFETY
+ int rc;
+
+ if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+ return -1;
+ }
+#endif
+ if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read root certificate file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+ return -1;
+ }
+
+ if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
+ {
+ if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+ 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)
+ {
+ /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
+#ifdef X509_V_FLAG_CRL_CHECK
+ X509_STORE_set_flags(cvstore,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#else
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
+ fnbuf);
+ SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+ return -1;
+#endif
+ }
+ /* if not found, silently ignore; we do not require CRL */
+ }
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+
+ 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
+ * requested it, disable SSL compression.
+ */
+#ifdef SSL_OP_NO_COMPRESSION
+ if (conn->sslcompression && conn->sslcompression[0] == '0')
+ {
+ SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
+ }
+#endif
+
+ return 0;
+}
+
+/*
+ * Attempt to negotiate SSL connection.
+ */
+static PostgresPollingStatusType
+open_client_SSL(PGconn *conn)
+{
+ int r;
+
+ r = SSL_connect(conn->ssl);
+ if (r <= 0)
+ {
+ int err = SSL_get_error(conn->ssl, r);
+
+ switch (err)
+ {
+ case SSL_ERROR_WANT_READ:
+ return PGRES_POLLING_READING;
+
+ case SSL_ERROR_WANT_WRITE:
+ return PGRES_POLLING_WRITING;
+
+ case SSL_ERROR_SYSCALL:
+ {
+ char sebuf[256];
+
+ if (r == -1)
+ printfPQExpBuffer(&conn->errorMessage,
+ 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"));
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+ case SSL_ERROR_SSL:
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL error: %s\n"),
+ err);
+ SSLerrfree(err);
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+
+ default:
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unrecognized SSL error code: %d\n"),
+ err);
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+ }
+
+ /*
+ * We already checked the server certificate in initialize_SSL() using
+ * SSL_CTX_set_verify(), if root.crt exists.
+ */
+
+ /* get server certificate */
+ conn->peer = SSL_get_peer_certificate(conn->ssl);
+ if (conn->peer == NULL)
+ {
+ char *err = SSLerrmessage();
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("certificate could not be obtained: %s\n"),
+ err);
+ SSLerrfree(err);
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+
+ if (!verify_peer_name_matches_certificate(conn))
+ {
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+
+ /* SSL handshake is complete */
+ return PGRES_POLLING_OK;
+}
+
+/*
+ * Close SSL connection.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+ bool destroy_needed = false;
+
+ if (conn->ssl)
+ {
+ /*
+ * We can't destroy everything SSL-related here due to the possible
+ * later calls to OpenSSL routines which may need our thread
+ * callbacks, so set a flag here and check at the end.
+ */
+ destroy_needed = true;
+
+ SSL_shutdown(conn->ssl);
+ SSL_free(conn->ssl);
+ conn->ssl = NULL;
+ conn->ssl_in_use = false;
+ }
+
+ 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
+
+ /*
+ * This will remove our SSL locking hooks, if this is the last SSL
+ * connection, which means we must wait to call it until after all SSL
+ * calls have been made, otherwise we can end up with a race condition and
+ * possible deadlocks.
+ *
+ * See comments above destroy_ssl_system().
+ */
+ if (destroy_needed)
+ destroy_ssl_system();
+}
+
+
+/*
+ * Obtain reason string for last SSL error
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * 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";
+
+#define SSL_ERR_LEN 128
+
+static char *
+SSLerrmessage(void)
+{
+ unsigned long errcode;
+ const char *errreason;
+ char *errbuf;
+
+ errbuf = malloc(SSL_ERR_LEN);
+ if (!errbuf)
+ return ssl_nomem;
+ errcode = ERR_get_error();
+ if (errcode == 0)
+ {
+ snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
+ return errbuf;
+ }
+ errreason = ERR_reason_error_string(errcode);
+ if (errreason != NULL)
+ {
+ strlcpy(errbuf, errreason, SSL_ERR_LEN);
+ return errbuf;
+ }
+ snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
+ return errbuf;
+}
+
+static void
+SSLerrfree(char *buf)
+{
+ if (buf != ssl_nomem)
+ free(buf);
+}
+
+/*
+ * Return pointer to OpenSSL object.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+ if (!conn)
+ return NULL;
+ return conn->ssl;
+}
+
+
+/*
+ * Private substitute BIO: this does the sending and receiving using send() and
+ * recv() instead. This is so that we can enable and disable interrupts
+ * just while calling recv(). We cannot have interrupts occurring while
+ * the bulk of openssl runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+ int res;
+ int save_errno;
+
+ res = pqsecure_raw_read((PGconn *) h->ptr, buf, size);
+ save_errno = errno;
+ BIO_clear_retry_flags(h);
+ if (res < 0)
+ {
+ switch (save_errno)
+ {
+#ifdef EAGAIN
+ case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+ case EWOULDBLOCK:
+#endif
+ case EINTR:
+ BIO_set_retry_read(h);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ errno = save_errno;
+ return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+ int res;
+ int save_errno;
+
+ res = pqsecure_raw_write((PGconn *) h->ptr, buf, size);
+ save_errno = errno;
+ BIO_clear_retry_flags(h);
+ if (res <= 0)
+ {
+ if (save_errno == EINTR)
+ {
+ BIO_set_retry_write(h);
+ }
+ }
+
+ return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+ if (!my_bio_initialized)
+ {
+ memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
+ my_bio_methods.bread = my_sock_read;
+ my_bio_methods.bwrite = my_sock_write;
+ my_bio_initialized = true;
+ }
+ return &my_bio_methods;
+}
+
+/* This should exactly match openssl's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(PGconn *conn, int fd)
+{
+ int ret = 0;
+ BIO *bio = NULL;
+
+ bio = BIO_new(my_BIO_s_socket());
+ if (bio == NULL)
+ {
+ SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+ goto err;
+ }
+ /* Use 'ptr' to store pointer to PGconn */
+ bio->ptr = conn;
+
+ SSL_set_bio(conn->ssl, bio, bio);
+ BIO_set_fd(bio, fd, BIO_NOCLOSE);
+ ret = 1;
+err:
+ return ret;
+}
#endif
#endif
-#ifdef USE_SSL
-
-#include <openssl/ssl.h>
-#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
-#include <openssl/conf.h>
-#endif
-#ifdef USE_SSL_ENGINE
-#include <openssl/engine.h>
-#endif
-
-
-#ifndef WIN32
-#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 USER_CERT_FILE "postgresql.crt"
-#define USER_KEY_FILE "postgresql.key"
-#define ROOT_CERT_FILE "root.crt"
-#define ROOT_CRL_FILE "root.crl"
-#endif
-
-static bool verify_peer_name_matches_certificate(PGconn *);
-static int verify_cb(int ok, X509_STORE_CTX *ctx);
-static int init_ssl_system(PGconn *conn);
-static void destroy_ssl_system(void);
-static int initialize_SSL(PGconn *conn);
-static void destroySSL(void);
-static PostgresPollingStatusType open_client_SSL(PGconn *);
-static void close_SSL(PGconn *);
-static char *SSLerrmessage(void);
-static void SSLerrfree(char *buf);
-
-static bool pq_init_ssl_lib = true;
-static bool pq_init_crypto_lib = true;
-
-/*
- * SSL_context is currently shared between threads and therefore we need to be
- * careful to lock around any usage of it when providing thread safety.
- * ssl_config_mutex is the mutex that we use to protect it.
- */
-static SSL_CTX *SSL_context = NULL;
-
-#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 */
-
-
/*
* Macros to handle disabling and then restoring the state of SIGPIPE handling.
* On Windows, these are all no-ops since there's no SIGPIPEs.
void
PQinitSSL(int do_init)
{
- PQinitOpenSSL(do_init, do_init);
+#ifdef USE_SSL
+ pgtls_init_library(do_init, do_init);
+#endif
}
/*
PQinitOpenSSL(int do_ssl, int do_crypto)
{
#ifdef USE_SSL
-#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;
+ pgtls_init_library(do_ssl, do_crypto);
#endif
}
int r = 0;
#ifdef USE_SSL
- r = init_ssl_system(conn);
+ r = pgtls_init(conn);
#endif
return r;
}
-/*
- * Destroy global context
- */
-void
-pqsecure_destroy(void)
-{
-#ifdef USE_SSL
- destroySSL();
-#endif
-}
-
/*
* Begin or continue negotiating a secure session.
*/
pqsecure_open_client(PGconn *conn)
{
#ifdef USE_SSL
- /* First time through? */
- if (conn->ssl == NULL)
- {
-#ifdef ENABLE_THREAD_SAFETY
- int rc;
-#endif
-
- /* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
- conn->sigpipe_flag = false;
-
-#ifdef ENABLE_THREAD_SAFETY
- if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
- return PGRES_POLLING_FAILED;
- }
-#endif
- /* Create a connection-specific SSL object */
- if (!(conn->ssl = SSL_new(SSL_context)) ||
- !SSL_set_app_data(conn->ssl, conn) ||
- !SSL_set_fd(conn->ssl, conn->sock))
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not establish SSL connection: %s\n"),
- err);
- SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&ssl_config_mutex);
-#endif
- close_SSL(conn);
-
- return PGRES_POLLING_FAILED;
- }
-#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-
- /*
- * Load client certificate, private key, and trusted CA certs.
- */
- if (initialize_SSL(conn) != 0)
- {
- /* initialize_SSL already put a message in conn->errorMessage */
- close_SSL(conn);
- return PGRES_POLLING_FAILED;
- }
- }
-
- /* Begin or continue the actual handshake */
- return open_client_SSL(conn);
+ return pgtls_open_client(conn);
#else
/* shouldn't get here */
return PGRES_POLLING_FAILED;
pqsecure_close(PGconn *conn)
{
#ifdef USE_SSL
- if (conn->ssl)
- close_SSL(conn);
+ if (conn->ssl_in_use)
+ pgtls_close(conn);
#endif
}
pqsecure_read(PGconn *conn, void *ptr, size_t len)
{
ssize_t n;
- int result_errno = 0;
- char sebuf[256];
#ifdef USE_SSL
- if (conn->ssl)
+ if (conn->ssl_in_use)
+ {
+ n = pgtls_read(conn, ptr, len);
+ }
+ else
+#endif
{
- int err;
+ n = pqsecure_raw_read(conn, ptr, len);
+ }
- DECLARE_SIGPIPE_INFO(spinfo);
+ return n;
+}
- /* SSL_read can write to the socket, so we need to disable SIGPIPE */
- DISABLE_SIGPIPE(conn, spinfo, return -1);
+ssize_t
+pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
+{
+ ssize_t n;
+ int result_errno = 0;
+ char sebuf[256];
-rloop:
- SOCK_ERRNO_SET(0);
- n = SSL_read(conn->ssl, ptr, len);
- err = SSL_get_error(conn->ssl, n);
- switch (err)
- {
- case SSL_ERROR_NONE:
- if (n < 0)
- {
- /* Not supposed to happen, so we don't translate the msg */
- printfPQExpBuffer(&conn->errorMessage,
- "SSL_read failed but did not provide error information\n");
- /* assume the connection is broken */
- result_errno = ECONNRESET;
- }
- break;
- case SSL_ERROR_WANT_READ:
- n = 0;
- break;
- 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.
- */
- goto rloop;
- case SSL_ERROR_SYSCALL:
- if (n < 0)
- {
- result_errno = SOCK_ERRNO;
- REMEMBER_EPIPE(spinfo, result_errno == EPIPE);
- if (result_errno == EPIPE ||
- result_errno == ECONNRESET)
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "server closed the connection unexpectedly\n"
- "\tThis probably means the server terminated abnormally\n"
- "\tbefore or while processing the request.\n"));
- else
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL SYSCALL error: %s\n"),
- SOCK_STRERROR(result_errno,
- sebuf, sizeof(sebuf)));
- }
- else
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL SYSCALL error: EOF detected\n"));
- /* assume the connection is broken */
- result_errno = ECONNRESET;
- n = -1;
- }
- break;
- case SSL_ERROR_SSL:
- {
- char *errm = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL error: %s\n"), errm);
- SSLerrfree(errm);
- /* assume the connection is broken */
- result_errno = ECONNRESET;
- n = -1;
- break;
- }
- case SSL_ERROR_ZERO_RETURN:
-
- /*
- * Per OpenSSL documentation, this error code is only returned
- * for a clean connection closure, so we should not report it
- * as a server crash.
- */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL connection has been closed unexpectedly\n"));
- result_errno = ECONNRESET;
- n = -1;
- break;
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("unrecognized SSL error code: %d\n"),
- err);
- /* assume the connection is broken */
- result_errno = ECONNRESET;
- n = -1;
- break;
- }
+ n = recv(conn->sock, ptr, len, 0);
- RESTORE_SIGPIPE(conn, spinfo);
- }
- else
-#endif /* USE_SSL */
+ if (n < 0)
{
- n = recv(conn->sock, ptr, len, 0);
+ result_errno = SOCK_ERRNO;
- if (n < 0)
+ /* Set error message if appropriate */
+ switch (result_errno)
{
- result_errno = SOCK_ERRNO;
-
- /* Set error message if appropriate */
- switch (result_errno)
- {
#ifdef EAGAIN
- case EAGAIN:
+ case EAGAIN:
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
#endif
- case EINTR:
- /* no error message, caller is expected to retry */
- break;
+ case EINTR:
+ /* no error message, caller is expected to retry */
+ break;
#ifdef ECONNRESET
- case ECONNRESET:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
+ case ECONNRESET:
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext(
"server closed the connection unexpectedly\n"
"\tThis probably means the server terminated abnormally\n"
"\tbefore or while processing the request.\n"));
- break;
+ break;
#endif
- default:
- printfPQExpBuffer(&conn->errorMessage,
+ default:
+ printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not receive data from server: %s\n"),
- SOCK_STRERROR(result_errno,
- sebuf, sizeof(sebuf)));
- break;
- }
+ SOCK_STRERROR(result_errno,
+ sebuf, sizeof(sebuf)));
+ break;
}
}
pqsecure_write(PGconn *conn, const void *ptr, size_t len)
{
ssize_t n;
- int result_errno = 0;
- char sebuf[256];
-
- DECLARE_SIGPIPE_INFO(spinfo);
#ifdef USE_SSL
- if (conn->ssl)
+ if (conn->ssl_in_use)
{
- int err;
-
- DISABLE_SIGPIPE(conn, spinfo, return -1);
-
- SOCK_ERRNO_SET(0);
- n = SSL_write(conn->ssl, ptr, len);
- err = SSL_get_error(conn->ssl, n);
- switch (err)
- {
- case SSL_ERROR_NONE:
- if (n < 0)
- {
- /* Not supposed to happen, so we don't translate the msg */
- printfPQExpBuffer(&conn->errorMessage,
- "SSL_write failed but did not provide error information\n");
- /* assume the connection is broken */
- result_errno = ECONNRESET;
- }
- break;
- case SSL_ERROR_WANT_READ:
-
- /*
- * 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.
- */
- n = 0;
- break;
- case SSL_ERROR_WANT_WRITE:
- n = 0;
- break;
- case SSL_ERROR_SYSCALL:
- if (n < 0)
- {
- result_errno = SOCK_ERRNO;
- REMEMBER_EPIPE(spinfo, result_errno == EPIPE);
- if (result_errno == EPIPE ||
- result_errno == ECONNRESET)
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "server closed the connection unexpectedly\n"
- "\tThis probably means the server terminated abnormally\n"
- "\tbefore or while processing the request.\n"));
- else
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL SYSCALL error: %s\n"),
- SOCK_STRERROR(result_errno,
- sebuf, sizeof(sebuf)));
- }
- else
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL SYSCALL error: EOF detected\n"));
- /* assume the connection is broken */
- result_errno = ECONNRESET;
- n = -1;
- }
- break;
- case SSL_ERROR_SSL:
- {
- char *errm = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL error: %s\n"), errm);
- SSLerrfree(errm);
- /* assume the connection is broken */
- result_errno = ECONNRESET;
- n = -1;
- break;
- }
- case SSL_ERROR_ZERO_RETURN:
-
- /*
- * Per OpenSSL documentation, this error code is only returned
- * for a clean connection closure, so we should not report it
- * as a server crash.
- */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL connection has been closed unexpectedly\n"));
- result_errno = ECONNRESET;
- n = -1;
- break;
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("unrecognized SSL error code: %d\n"),
- err);
- /* assume the connection is broken */
- result_errno = ECONNRESET;
- n = -1;
- break;
- }
+ n = pgtls_write(conn, ptr, len);
}
else
-#endif /* USE_SSL */
+#endif
{
- int flags = 0;
+ n = pqsecure_raw_write(conn, ptr, len);
+ }
+
+ return n;
+}
+
+ssize_t
+pqsecure_raw_write(PGconn *conn, const void *ptr, size_t len)
+{
+ ssize_t n;
+ int flags = 0;
+ int result_errno = 0;
+ char sebuf[256];
+
+ DECLARE_SIGPIPE_INFO(spinfo);
#ifdef MSG_NOSIGNAL
- if (conn->sigpipe_flag)
- flags |= MSG_NOSIGNAL;
+ if (conn->sigpipe_flag)
+ flags |= MSG_NOSIGNAL;
retry_masked:
#endif /* MSG_NOSIGNAL */
- DISABLE_SIGPIPE(conn, spinfo, return -1);
+ DISABLE_SIGPIPE(conn, spinfo, return -1);
- n = send(conn->sock, ptr, len, flags);
+ n = send(conn->sock, ptr, len, flags);
- if (n < 0)
- {
- result_errno = SOCK_ERRNO;
+ if (n < 0)
+ {
+ result_errno = SOCK_ERRNO;
- /*
- * 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().
- */
+ /*
+ * 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 && result_errno == EINVAL)
- {
- conn->sigpipe_flag = false;
- flags = 0;
- goto retry_masked;
- }
+ if (flags != 0 && result_errno == EINVAL)
+ {
+ conn->sigpipe_flag = false;
+ flags = 0;
+ goto retry_masked;
+ }
#endif /* MSG_NOSIGNAL */
- /* Set error message if appropriate */
- switch (result_errno)
- {
+ /* Set error message if appropriate */
+ switch (result_errno)
+ {
#ifdef EAGAIN
- case EAGAIN:
+ case EAGAIN:
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- case EWOULDBLOCK:
+ case EWOULDBLOCK:
#endif
- case EINTR:
- /* no error message, caller is expected to retry */
- break;
+ case EINTR:
+ /* no error message, caller is expected to retry */
+ break;
- case EPIPE:
- /* Set flag for EPIPE */
- REMEMBER_EPIPE(spinfo, true);
- /* FALL THRU */
+ case EPIPE:
+ /* Set flag for EPIPE */
+ REMEMBER_EPIPE(spinfo, true);
+ /* FALL THRU */
#ifdef ECONNRESET
- case ECONNRESET:
+ case ECONNRESET:
#endif
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext(
"server closed the connection unexpectedly\n"
"\tThis probably means the server terminated abnormally\n"
"\tbefore or while processing the request.\n"));
- break;
+ break;
- default:
- printfPQExpBuffer(&conn->errorMessage,
+ default:
+ printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not send data to server: %s\n"),
SOCK_STRERROR(result_errno,
sebuf, sizeof(sebuf)));
- break;
- }
+ break;
}
}
return n;
}
-/* ------------------------------------------------------------ */
-/* SSL specific code */
-/* ------------------------------------------------------------ */
-#ifdef USE_SSL
-
-/*
- * Certificate verification callback
- *
- * This callback allows us to log intermediate problems during
- * verification, but there doesn't seem to be a clean way to get
- * our PGconn * structure. So we can't log anything!
- *
- * This callback also allows us to override the default acceptance
- * criteria (e.g., accepting self-signed or expired certs), but
- * for now we accept the default checks.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
-{
- return ok;
-}
-
-
-/*
- * 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 case-insensitive, since DNS is case insensitive.
- */
-static int
-wildcard_certificate_match(const char *pattern, const char *string)
-{
- int lenpat = strlen(pattern);
- int lenstr = strlen(string);
-
- /* 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;
-
- if (lenpat > lenstr)
- /* If pattern is longer than the string, we can never match */
- return 0;
-
- 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;
-
- 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;
-
- /* String ended with pattern, and didn't have a dot before, so we match */
- return 1;
-}
-
-
-/*
- * Verify that common name resolves to peer.
- */
-static bool
-verify_peer_name_matches_certificate(PGconn *conn)
-{
- char *peer_cn;
- int r;
- int len;
- bool result;
-
- /*
- * If told not to verify the peer name, don't do it. Return true
- * indicating that the verification was successful.
- */
- if (strcmp(conn->sslmode, "verify-full") != 0)
- return true;
-
- /*
- * Extract the common name from the certificate.
- *
- * XXX: Should support alternate names here
- */
- /* First find out the name's length and allocate a buffer for it. */
- len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
- NID_commonName, NULL, 0);
- if (len == -1)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get server common name from server certificate\n"));
- return false;
- }
- peer_cn = malloc(len + 1);
- if (peer_cn == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return false;
- }
-
- r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
- NID_commonName, peer_cn, len + 1);
- if (r != len)
- {
- /* Got different length than on the first call. Shouldn't happen. */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get server common name from server certificate\n"));
- free(peer_cn);
- return false;
- }
- peer_cn[len] = '\0';
-
- /*
- * Reject embedded NULLs in certificate common name to prevent attacks
- * like CVE-2009-4034.
- */
- if (len != strlen(peer_cn))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL certificate's common name contains embedded null\n"));
- free(peer_cn);
- return false;
- }
-
- /*
- * We got the peer's common name. Now compare it against the originally
- * given hostname.
- */
- if (!(conn->pghost && conn->pghost[0] != '\0'))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("host name must be specified for a verified SSL connection\n"));
- result = false;
- }
- else
- {
- if (pg_strcasecmp(peer_cn, conn->pghost) == 0)
- /* Exact name match */
- result = true;
- else if (wildcard_certificate_match(peer_cn, conn->pghost))
- /* Matched wildcard certificate */
- result = true;
- else
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"),
- peer_cn, conn->pghost);
- result = false;
- }
- }
-
- free(peer_cn);
- return result;
-}
-
-#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();
-}
-
-static pthread_mutex_t *pq_lockarray;
-
-static void
-pq_lockingcallback(int mode, int n, const char *file, int line)
-{
- if (mode & CRYPTO_LOCK)
- {
- if (pthread_mutex_lock(&pq_lockarray[n]))
- PGTHREAD_ERROR("failed to lock mutex");
- }
- else
- {
- if (pthread_mutex_unlock(&pq_lockarray[n]))
- PGTHREAD_ERROR("failed to unlock mutex");
- }
-}
-#endif /* ENABLE_THREAD_SAFETY */
-
-/*
- * Initialize SSL system, in particular creating the SSL_context object
- * that will be shared by all SSL-using connections in this process.
- *
- * In threadsafe mode, this includes setting up libcrypto callback functions
- * to do thread locking.
- *
- * If the caller has told us (through PQinitOpenSSL) that he's taking care
- * of libcrypto, we expect that callbacks are already set, and won't try to
- * override it.
- *
- * The conn parameter is only used to be able to pass back an error
- * message - no connection-local setup is made here.
- *
- * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
- */
-static int
-init_ssl_system(PGconn *conn)
-{
-#ifdef ENABLE_THREAD_SAFETY
-#ifdef WIN32
- /* Also see similar code in fe-connect.c, default_threadlock() */
- if (ssl_config_mutex == NULL)
- {
- while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
- /* loop, another thread own the lock */ ;
- if (ssl_config_mutex == NULL)
- {
- if (pthread_mutex_init(&ssl_config_mutex, NULL))
- return -1;
- }
- InterlockedExchange(&win32_ssl_create_mutex, 0);
- }
-#endif
- if (pthread_mutex_lock(&ssl_config_mutex))
- return -1;
-
- if (pq_init_crypto_lib)
- {
- /*
- * 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;
-
- 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;
- }
- }
- }
-
- if (ssl_open_connections++ == 0)
- {
- /* These are only required for threaded libcrypto applications */
- CRYPTO_set_id_callback(pq_threadidcallback);
- CRYPTO_set_locking_callback(pq_lockingcallback);
- }
- }
-#endif /* ENABLE_THREAD_SAFETY */
-
- if (!SSL_context)
- {
- if (pq_init_ssl_lib)
- {
-#if SSLEAY_VERSION_NUMBER >= 0x00907000L
- OPENSSL_config(NULL);
-#endif
- SSL_library_init();
- SSL_load_error_strings();
- }
-
- /*
- * We use SSLv23_method() because it can negotiate use of the highest
- * mutually supported protocol version, while alternatives like
- * TLSv1_2_method() permit only one specific version. Note that we
- * don't actually allow SSL v2 or v3, only TLS protocols (see below).
- */
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not create SSL context: %s\n"),
- err);
- SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&ssl_config_mutex);
-#endif
- return -1;
- }
-
- /* Disable old protocol versions */
- SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
- /*
- * Disable OpenSSL's moving-write-buffer sanity check, because it
- * causes unnecessary failures in nonblocking send cases.
- */
- SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- }
-
-#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&ssl_config_mutex);
-#endif
- return 0;
-}
-
-/*
- * 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 or the SSL_context. If we get another
- * connection in this process, we will just re-use them with the
- * existing mutexes.
- *
- * This means we leak a little memory on repeated load/unload of the
- * library.
- */
- }
-
- pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-}
-
-/*
- * Initialize (potentially) per-connection SSL data, namely the
- * client certificate, private key, and trusted CA certs.
- *
- * conn->ssl must already be created. It receives the connection's client
- * certificate and private key. Note however that certificates also get
- * loaded into the SSL_context object, and are therefore accessible to all
- * connections in this process. This should be OK as long as there aren't
- * any hash collisions among the certs.
- *
- * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
- */
-static int
-initialize_SSL(PGconn *conn)
-{
- struct stat buf;
- char homedir[MAXPGPATH];
- char fnbuf[MAXPGPATH];
- 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
- {
- /*
- * Cert file exists, so load it. Since OpenSSL doesn't provide the
- * equivalent of "SSL_use_certificate_chain_file", we actually have to
- * load the file twice. The first call loads any extra certs after
- * the first one into chain-cert storage associated with the
- * SSL_context. The second call loads the first cert (only) into the
- * SSL object, where it will be correctly paired with the private key
- * we load below. We do it this way so that each connection
- * understands which subject cert to present, in case different
- * sslcert settings are used for different connections in the same
- * process.
- *
- * NOTE: This function may also modify our SSL_context and therefore
- * we have to lock around this call and any places where we use the
- * SSL_context struct.
- */
-#ifdef ENABLE_THREAD_SAFETY
- int rc;
-
- if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
- return -1;
- }
-#endif
- if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not read certificate file \"%s\": %s\n"),
- fnbuf, err);
- SSLerrfree(err);
-
-#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&ssl_config_mutex);
-#endif
- return -1;
- }
-
- if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not read certificate file \"%s\": %s\n"),
- fnbuf, err);
- SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&ssl_config_mutex);
-#endif
- return -1;
- }
-
- /* 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 (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
- {
-#ifdef USE_SSL_ENGINE
- if (strchr(conn->sslkey, ':')
-#ifdef WIN32
- && conn->sslkey[1] != ':'
-#endif
- )
- {
- /* Colon, but not in second character, treat as engine:key */
- char *engine_str = strdup(conn->sslkey);
- char *engine_colon;
-
- 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();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not load SSL engine \"%s\": %s\n"),
- engine_str, err);
- SSLerrfree(err);
- free(engine_str);
- return -1;
- }
-
- if (ENGINE_init(conn->engine) == 0)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
- engine_str, err);
- SSLerrfree(err);
- ENGINE_free(conn->engine);
- conn->engine = NULL;
- free(engine_str);
- return -1;
- }
-
- pkey = ENGINE_load_private_key(conn->engine, engine_colon,
- NULL, NULL);
- if (pkey == NULL)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
- engine_colon, engine_str, err);
- SSLerrfree(err);
- ENGINE_finish(conn->engine);
- ENGINE_free(conn->engine);
- conn->engine = NULL;
- free(engine_str);
- return -1;
- }
- if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
- engine_colon, engine_str, err);
- SSLerrfree(err);
- ENGINE_finish(conn->engine);
- ENGINE_free(conn->engine);
- conn->engine = NULL;
- free(engine_str);
- return -1;
- }
-
- free(engine_str);
-
- fnbuf[0] = '\0'; /* indicate we're not going to load from a
- * file */
- }
- else
-#endif /* USE_SSL_ENGINE */
- {
- /* PGSSLKEY is not an engine, treat it as a filename */
- strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
- }
- }
- else 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)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("certificate present, but not private key file \"%s\"\n"),
- fnbuf);
- return -1;
- }
-#ifndef WIN32
- if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
- fnbuf);
- return -1;
- }
-#endif
-
- if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not load private key file \"%s\": %s\n"),
- fnbuf, err);
- SSLerrfree(err);
- return -1;
- }
- }
-
- /* verify that the cert and key go together */
- if (have_cert &&
- SSL_check_private_key(conn->ssl) != 1)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
- fnbuf, err);
- SSLerrfree(err);
- return -1;
- }
-
- /*
- * If the root cert file exists, load it so we can perform certificate
- * verification. If sslmode is "verify-full" we will also do further
- * verification after the connection has been completed.
- */
- if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
- strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
- else if (have_homedir)
- snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
- else
- fnbuf[0] = '\0';
-
- if (fnbuf[0] != '\0' &&
- stat(fnbuf, &buf) == 0)
- {
- X509_STORE *cvstore;
-
-#ifdef ENABLE_THREAD_SAFETY
- int rc;
-
- if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
- return -1;
- }
-#endif
- if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not read root certificate file \"%s\": %s\n"),
- fnbuf, err);
- SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&ssl_config_mutex);
-#endif
- return -1;
- }
-
- if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
- {
- if (conn->sslcrl && strlen(conn->sslcrl) > 0)
- 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)
- {
- /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
-#ifdef X509_V_FLAG_CRL_CHECK
- X509_STORE_set_flags(cvstore,
- X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
-#else
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
- fnbuf);
- SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&ssl_config_mutex);
-#endif
- return -1;
-#endif
- }
- /* if not found, silently ignore; we do not require CRL */
- }
-#ifdef ENABLE_THREAD_SAFETY
- pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-
- 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
- * requested it, disable SSL compression.
- */
-#ifdef SSL_OP_NO_COMPRESSION
- if (conn->sslcompression && conn->sslcompression[0] == '0')
- {
- SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
- }
-#endif
-
- return 0;
-}
-
-static void
-destroySSL(void)
-{
- destroy_ssl_system();
-}
-
-/*
- * Attempt to negotiate SSL connection.
- */
-static PostgresPollingStatusType
-open_client_SSL(PGconn *conn)
-{
- int r;
-
- r = SSL_connect(conn->ssl);
- if (r <= 0)
- {
- int err = SSL_get_error(conn->ssl, r);
-
- switch (err)
- {
- case SSL_ERROR_WANT_READ:
- return PGRES_POLLING_READING;
-
- case SSL_ERROR_WANT_WRITE:
- return PGRES_POLLING_WRITING;
-
- case SSL_ERROR_SYSCALL:
- {
- char sebuf[256];
-
- if (r == -1)
- printfPQExpBuffer(&conn->errorMessage,
- 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"));
- close_SSL(conn);
- return PGRES_POLLING_FAILED;
- }
- case SSL_ERROR_SSL:
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSL error: %s\n"),
- err);
- SSLerrfree(err);
- close_SSL(conn);
- return PGRES_POLLING_FAILED;
- }
-
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("unrecognized SSL error code: %d\n"),
- err);
- close_SSL(conn);
- return PGRES_POLLING_FAILED;
- }
- }
-
- /*
- * We already checked the server certificate in initialize_SSL() using
- * SSL_CTX_set_verify(), if root.crt exists.
- */
-
- /* get server certificate */
- conn->peer = SSL_get_peer_certificate(conn->ssl);
- if (conn->peer == NULL)
- {
- char *err = SSLerrmessage();
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("certificate could not be obtained: %s\n"),
- err);
- SSLerrfree(err);
- close_SSL(conn);
- return PGRES_POLLING_FAILED;
- }
-
- if (!verify_peer_name_matches_certificate(conn))
- {
- close_SSL(conn);
- return PGRES_POLLING_FAILED;
- }
-
- /* SSL handshake is complete */
- return PGRES_POLLING_OK;
-}
-
-/*
- * Close SSL connection.
- */
-static void
-close_SSL(PGconn *conn)
-{
- bool destroy_needed = false;
-
- if (conn->ssl)
- {
- DECLARE_SIGPIPE_INFO(spinfo);
-
- /*
- * We can't destroy everything SSL-related here due to the possible
- * later calls to OpenSSL routines which may need our thread
- * callbacks, so set a flag here and check at the end.
- */
- destroy_needed = true;
-
- DISABLE_SIGPIPE(conn, spinfo, (void) 0);
- SSL_shutdown(conn->ssl);
- SSL_free(conn->ssl);
- conn->ssl = NULL;
- /* 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
-
- /*
- * This will remove our SSL locking hooks, if this is the last SSL
- * connection, which means we must wait to call it until after all SSL
- * calls have been made, otherwise we can end up with a race condition and
- * possible deadlocks.
- *
- * See comments above destroy_ssl_system().
- */
- if (destroy_needed)
- pqsecure_destroy();
-}
-
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * 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";
-
-#define SSL_ERR_LEN 128
-
-static char *
-SSLerrmessage(void)
-{
- unsigned long errcode;
- const char *errreason;
- char *errbuf;
-
- errbuf = malloc(SSL_ERR_LEN);
- if (!errbuf)
- return ssl_nomem;
- errcode = ERR_get_error();
- if (errcode == 0)
- {
- snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
- return errbuf;
- }
- errreason = ERR_reason_error_string(errcode);
- if (errreason != NULL)
- {
- strlcpy(errbuf, errreason, SSL_ERR_LEN);
- return errbuf;
- }
- snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
- return errbuf;
-}
-
-static void
-SSLerrfree(char *buf)
-{
- if (buf != ssl_nomem)
- free(buf);
-}
-
-/*
- * Return pointer to OpenSSL object.
- */
-void *
-PQgetssl(PGconn *conn)
-{
- if (!conn)
- return NULL;
- return conn->ssl;
-}
-#else /* !USE_SSL */
-
+#ifndef USE_SSL
void *
PQgetssl(PGconn *conn)
{
#endif
#endif /* ENABLE_SSPI */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
#define USE_SSL_ENGINE
#endif
-#endif /* USE_SSL */
+#endif /* USE_OPENSSL */
/*
* POSTGRES backend dependent Constants.
bool allow_ssl_try; /* Allowed to try SSL negotiation */
bool wait_ssl_try; /* Delay SSL negotiation until after
* attempting normal connection */
+ bool ssl_in_use;
+#ifdef USE_OPENSSL
SSL *ssl; /* SSL status, if have SSL connection */
X509 *peer; /* X509 cert of server */
#ifdef USE_SSL_ENGINE
void *engine; /* dummy field to keep struct the same if
* OpenSSL version changes */
#endif
+#endif /* USE_OPENSSL */
#endif /* USE_SSL */
#ifdef ENABLE_GSS
*/
extern char *const pgresStatus[];
+
+#ifdef USE_SSL
+
+#ifndef WIN32
+#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 USER_CERT_FILE "postgresql.crt"
+#define USER_KEY_FILE "postgresql.key"
+#define ROOT_CERT_FILE "root.crt"
+#define ROOT_CRL_FILE "root.crl"
+#endif
+
+#endif /* USE_SSL */
+
/* ----------------
* Internal functions of libpq
* Functions declared here need to be visible across files of libpq,
extern void pqsecure_close(PGconn *);
extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
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);
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
extern int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
bool got_epipe);
#endif
+/*
+ * The SSL implementatation provides these functions (fe-secure-openssl.c)
+ */
+extern void pgtls_init_library(bool do_ssl, int do_crypto);
+extern int pgtls_init(PGconn *conn);
+extern PostgresPollingStatusType pgtls_open_client(PGconn *conn);
+extern void pgtls_close(PGconn *conn);
+extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);
+extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
+
/*
* this is so that we can check if a connection is non-blocking internally
* without the overhead of a function call
# Will build a static library libpq(d).lib
# and a dynamic library libpq(d).dll with import library libpq(d)dll.lib
-# USE_SSL=1 will compile with OpenSSL
+# USE_OPENSSL=1 will compile with OpenSSL
# USE_KFW=1 will compile with kfw(kerberos for Windows)
# DEBUG=1 compiles with debugging symbols
# ENABLE_THREAD_SAFETY=1 compiles with threading enabled
-@erase "$(OUTDIR)\$(OUTFILENAME).dll.manifest"
-@erase "$(OUTDIR)\*.idb"
-@erase pg_config_paths.h"
+!IFDEF USE_OPENSSL
+ -@erase "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
LIB32=link.exe -lib
"$(INTDIR)\win32error.obj" \
"$(INTDIR)\win32setlocale.obj" \
"$(INTDIR)\pthread-win32.obj"
+!IFDEF USE_OPENSSL
+ LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
config: ..\..\include\pg_config.h ..\..\include\pg_config_ext.h pg_config_paths.h ..\..\include\pg_config_os.h
/Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c \
/D "_CRT_SECURE_NO_DEPRECATE" $(ADD_DEFINES)
-!IFDEF USE_SSL
-CPP_PROJ=$(CPP_PROJ) /D USE_SSL
+!IFDEF USE_OPENSSL
+CPP_PROJ=$(CPP_PROJ) /D USE_OPENSSL
SSL_LIBS=ssleay32.lib libeay32.lib gdi32.lib
!ENDIF
$postgres->AddLibrary('ws2_32.lib');
$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
$postgres->FullExportDLL('postgres.lib');
+ # The OBJS scraper doesn't know about ifdefs, so remove be-secure-openssl.c
+ # if building without OpenSSL
+ if (!$solution->{options}->{openssl})
+ {
+ $postgres->RemoveFile('src\backend\libpq\be-secure-openssl.c');
+ }
my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
'src\backend\snowball');
$libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c',
'src\interfaces\libpq\libpq.rc');
$libpq->AddReference($libpgport);
+ # The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
+ # if building without OpenSSL
+ if (!$solution->{options}->{openssl})
+ {
+ $libpq->RemoveFile('src\interfaces\libpq\fe-secure-openssl.c');
+ }
my $libpqwalreceiver =
$solution->AddProject('libpqwalreceiver', 'dll', '',
if ($self->{options}->{integer_datetimes});
print O "#define USE_LDAP 1\n" if ($self->{options}->{ldap});
print O "#define HAVE_LIBZ 1\n" if ($self->{options}->{zlib});
- print O "#define USE_SSL 1\n" if ($self->{options}->{openssl});
+ print O "#define USE_OPENSSL 1\n" if ($self->{options}->{openssl});
print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
print O "#define BLCKSZ ", 1024 * $self->{options}->{blocksize}, "\n";
$cfg .= ' --with-ldap' if ($self->{options}->{ldap});
$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
$cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
- $cfg .= ' --with-openssl' if ($self->{options}->{ssl});
+ $cfg .= ' --with-openssl' if ($self->{options}->{openssl});
$cfg .= ' --with-ossp-uuid' if ($self->{options}->{uuid});
$cfg .= ' --with-libxml' if ($self->{options}->{xml});
$cfg .= ' --with-libxslt' if ($self->{options}->{xslt});
tcl => undef, # --with-tls=<path>
perl => undef, # --with-perl
python => undef, # --with-python=<path>
- openssl => undef, # --with-ssl=<path>
+ openssl => undef, # --with-openssl=<path>
uuid => undef, # --with-ossp-uuid
xml => undef, # --with-libxml=<path>
xslt => undef, # --with-libxslt=<path>