aboutsummaryrefslogtreecommitdiff
path: root/src/backend/libpq/be-secure.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/libpq/be-secure.c')
-rw-r--r--src/backend/libpq/be-secure.c1027
1 files changed, 24 insertions, 1003 deletions
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 59204cfe801..41ec1ad8ad9 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -13,38 +13,6 @@
* 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.
- *
*-------------------------------------------------------------------------
*/
@@ -63,35 +31,11 @@
#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;
@@ -105,11 +49,7 @@ char *ssl_crl_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 */
@@ -122,73 +62,6 @@ char *SSLECDHCurve;
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 */
/* ------------------------------------------------------------ */
@@ -199,7 +72,7 @@ int
secure_initialize(void)
{
#ifdef USE_SSL
- initialize_SSL();
+ be_tls_init();
#endif
return 0;
@@ -227,7 +100,7 @@ secure_open_server(Port *port)
int r = 0;
#ifdef USE_SSL
- r = open_server_SSL(port);
+ r = be_tls_open_server(port);
#endif
return r;
@@ -240,8 +113,8 @@ void
secure_close(Port *port)
{
#ifdef USE_SSL
- if (port->ssl)
- close_SSL(port);
+ if (port->ssl_in_use)
+ be_tls_close(port);
#endif
}
@@ -254,908 +127,56 @@ secure_read(Port *port, void *ptr, size_t len)
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 */