aboutsummaryrefslogtreecommitdiff
path: root/src/backend/libpq/be-secure-openssl.c
diff options
context:
space:
mode:
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>2014-08-18 13:04:47 +0300
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>2014-08-18 13:12:40 +0300
commit48d50840d53eb62842c0d9b54eab9cd7c9a3a46d (patch)
treee28deb52b238d509c42eecb25c57634a209b1ed4 /src/backend/libpq/be-secure-openssl.c
parent2b475c5946bc8a9beaff3f57b45cc440a78561a1 (diff)
downloadpostgresql-48d50840d53eb62842c0d9b54eab9cd7c9a3a46d.tar.gz
postgresql-48d50840d53eb62842c0d9b54eab9cd7c9a3a46d.zip
Reorganize functions in be-secure-openssl.c
Move the functions within the file so that public interface functions come first, followed by internal functions. Previously, be_tls_write was first, then internal stuff, and finally the rest of the public interface, which clearly didn't make much sense. Per Andres Freund's complaint.
Diffstat (limited to 'src/backend/libpq/be-secure-openssl.c')
-rw-r--r--src/backend/libpq/be-secure-openssl.c805
1 files changed, 408 insertions, 397 deletions
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index f7bdcb7f10e..8d8f12952a4 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -75,12 +75,17 @@
#include "utils/memutils.h"
+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(Port *port, int fd);
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_ecdh(void);
static const char *SSLerrmessage(void);
/* are we in the middle of a renegotiation? */
@@ -153,6 +158,407 @@ AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
-----END DH PARAMETERS-----\n";
+
+/* ------------------------------------------------------------ */
+/* Public interface */
+/* ------------------------------------------------------------ */
+
+/*
+ * 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;
+ }
+}
+
+/*
+ * Read data from a secure connection.
+ */
+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;
+}
+
/*
* Write data to a secure connection.
*/
@@ -284,7 +690,7 @@ wloop:
}
/* ------------------------------------------------------------ */
-/* OpenSSL specific code */
+/* Internal functions */
/* ------------------------------------------------------------ */
/*
@@ -601,10 +1007,10 @@ info_cb(const SSL *ssl, int type, int args)
}
}
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
static void
initialize_ecdh(void)
{
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
EC_KEY *ecdh;
int nid;
@@ -621,402 +1027,7 @@ initialize_ecdh(void)
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;
}
/*