diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index f7bdcb7f10ef67606c5e7caf78a369ea5daaa2f6..8d8f12952a4a4f14a15f8647b96935e13d68fb39 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,870 +158,876 @@ AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\ KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\ -----END DH PARAMETERS-----\n"; + +/* ------------------------------------------------------------ */ +/* Public interface */ +/* ------------------------------------------------------------ */ + /* - * Write data to a secure connection. + * Initialize global SSL context. */ -ssize_t -be_tls_write(Port *port, void *ptr, size_t len) +void +be_tls_init(void) { - ssize_t n; - int err; + struct stat buf; - /* - * 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) + STACK_OF(X509_NAME) *root_cert_list = NULL; + + if (!SSL_context) { - in_ssl_renegotiation = true; +#if SSLEAY_VERSION_NUMBER >= 0x0907000L + OPENSSL_config(NULL); +#endif + SSL_library_init(); + SSL_load_error_strings(); /* - * 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. + * 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_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; + SSL_context = SSL_CTX_new(SSLv23_method()); + if (!SSL_context) + ereport(FATAL, + (errmsg("could not create SSL context: %s", + SSLerrmessage()))); - /* - * 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"))); - } - } - } + /* + * 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); -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; - } + /* + * 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 (n >= 0) - { - /* is renegotiation complete? */ - if (in_ssl_renegotiation && - SSL_num_renegotiations(port->ssl) >= 1) - { - in_ssl_renegotiation = false; - port->count = 0; - } + 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))); /* - * 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. + * 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 (in_ssl_renegotiation && - port->count > ssl_renegotiation_limit * 1024L) +#if !defined(WIN32) && !defined(__CYGWIN__) + if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO)) ereport(FATAL, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("SSL failed to renegotiate connection before limit expired"))); + (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()))); } - return n; -} + /* 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); -/* ------------------------------------------------------------ */ -/* OpenSSL specific code */ -/* ------------------------------------------------------------ */ + /* set up ephemeral ECDH keys */ + initialize_ecdh(); -/* - * 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? - */ + /* 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)"); -static bool my_bio_initialized = false; -static BIO_METHOD my_bio_methods; + /* Let server choose order */ + if (SSLPreferServerCiphers) + SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE); -static int -my_sock_read(BIO *h, char *buf, int size) -{ - int res = 0; + /* + * 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()))); + } - if (buf != NULL) + /*---------- + * Load the Certificate Revocation List (CRL). + * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html + *---------- + */ + if (ssl_crl_file[0]) { - res = secure_raw_read(((Port *)h->ptr), buf, size); - BIO_clear_retry_flags(h); - if (res <= 0) + X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context); + + if (cvstore) { - /* If we were interrupted, tell caller to retry */ - if (errno == EINTR) + /* Set the flags to check against the complete CRL chain */ + if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1) { - BIO_set_retry_read(h); + /* 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()))); + } + } - return res; + 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); + } } -static int -my_sock_write(BIO *h, const char *buf, int size) +/* + * Attempt to negotiate SSL connection. + */ +int +be_tls_open_server(Port *port) { - int res = 0; + int r; + int err; - res = secure_raw_write(((Port *) h->ptr), buf, size); - BIO_clear_retry_flags(h); - if (res <= 0) + Assert(!port->ssl); + Assert(!port->peer); + + if (!(port->ssl = SSL_new(SSL_context))) { - if (errno == EINTR) + 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) { - BIO_set_retry_write(h); + 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; } - return res; -} + port->count = 0; -static BIO_METHOD * -my_BIO_s_socket(void) -{ - if (!my_bio_initialized) + /* 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) { - 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; -} + int len; -/* 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; + len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer), + NID_commonName, NULL, 0); + if (len != -1) + { + char *peer_cn; - bio = BIO_new(my_BIO_s_socket()); + 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; + } - if (bio == NULL) - { - SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB); - goto err; + /* + * 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; } - /* 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; + 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; } /* - * 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. + * Close SSL connection. */ -static DH * -load_dh_file(int keylength) +void +be_tls_close(Port *port) { - 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) + if (port->ssl) { - elog(LOG, "DH errors (%s): %d bits expected, %d bits found", - fnbuf, keylength, 8 * DH_size(dh)); - dh = NULL; + SSL_shutdown(port->ssl); + SSL_free(port->ssl); + port->ssl = NULL; + port->ssl_in_use = false; } - /* make sure the DH parameters are usable */ - if (dh != NULL) + if (port->peer) { - 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; - } + X509_free(port->peer); + port->peer = NULL; } - return dh; + if (port->peer_cn) + { + pfree(port->peer_cn); + port->peer_cn = NULL; + } } /* - * 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. + * Read data from a secure connection. */ -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) +ssize_t +be_tls_read(Port *port, void *ptr, size_t len) { - DH *r = NULL; - static DH *dh = NULL; - static DH *dh512 = NULL; - static DH *dh1024 = NULL; - static DH *dh2048 = NULL; - static DH *dh4096 = NULL; + ssize_t n; + int err; - switch (keylength) +rloop: + errno = 0; + n = SSL_read(port->ssl, ptr, len); + err = SSL_get_error(port->ssl, n); + switch (err) { - 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; + case SSL_ERROR_NONE: + port->count += n; 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; + 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 4096: - if (dh4096 == NULL) - dh4096 = load_dh_file(keylength); - if (dh4096 == NULL) - dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096); - r = dh4096; + 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: - 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))); + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("unrecognized SSL error code: %d", + err))); + errno = ECONNRESET; + n = -1; 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); + return n; } -#else -#define initialize_ecdh() -#endif /* - * Initialize global SSL context. + * Write data to a secure connection. */ -void -be_tls_init(void) +ssize_t +be_tls_write(Port *port, void *ptr, size_t len) { - struct stat buf; - - STACK_OF(X509_NAME) *root_cert_list = NULL; + ssize_t n; + int err; - if (!SSL_context) + /* + * 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) { -#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))); + in_ssl_renegotiation = true; /* - * Require no public access to key file. + * 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. * - * 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) + * OpenSSL provides SSL_renegotiation_pending(), but this doesn't + * seem to work in testing. */ -#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)"); + SSL_clear_num_renegotiations(port->ssl); - /* Let server choose order */ - if (SSLPreferServerCiphers) - SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE); + 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; - /* - * 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()))); + /* + * 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"))); + } + } } - /*---------- - * Load the Certificate Revocation List (CRL). - * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html - *---------- - */ - if (ssl_crl_file[0]) +wloop: + errno = 0; + n = SSL_write(port->ssl, ptr, len); + err = SSL_get_error(port->ssl, n); + switch (err) { - 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."))); + 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; } - else - ereport(FATAL, - (errmsg("could not load SSL certificate revocation list file \"%s\": %s", - ssl_crl_file, SSLerrmessage()))); - } + 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 (ssl_ca_file[0]) + if (n >= 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; + /* is renegotiation complete? */ + if (in_ssl_renegotiation && + SSL_num_renegotiations(port->ssl) >= 1) + { + in_ssl_renegotiation = false; + port->count = 0; + } /* - * 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. + * 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. */ - SSL_CTX_set_client_CA_list(SSL_context, root_cert_list); + 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; } +/* ------------------------------------------------------------ */ +/* Internal functions */ +/* ------------------------------------------------------------ */ + /* - * Attempt to negotiate SSL connection. + * 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? */ -int -be_tls_open_server(Port *port) -{ - int r; - int err; - Assert(!port->ssl); - Assert(!port->peer); +static bool my_bio_initialized = false; +static BIO_METHOD my_bio_methods; - 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)) +static int +my_sock_read(BIO *h, char *buf, int size) +{ + int res = 0; + + if (buf != NULL) { - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("could not set SSL socket: %s", - SSLerrmessage()))); - be_tls_close(port); - return -1; + 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); + } + } } - port->ssl_in_use = true; -aloop: - r = SSL_accept(port->ssl); - if (r <= 0) + 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) { - err = SSL_get_error(port->ssl, r); - switch (err) + if (errno == EINTR) { - 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; + BIO_set_retry_write(h); } - be_tls_close(port); - return -1; } - port->count = 0; + return res; +} - /* Get client certificate, if available. */ - port->peer = SSL_get_peer_certificate(port->ssl); +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; +} - /* and extract the Common Name from it. */ - port->peer_cn = NULL; - port->peer_cert_valid = false; - if (port->peer != NULL) +/* 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) { - int len; + SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB); + goto err; + } + /* Use 'ptr' to store pointer to PGconn */ + bio->ptr = port; - len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer), - NID_commonName, NULL, 0); - if (len != -1) - { - char *peer_cn; + BIO_set_fd(bio, fd, BIO_NOCLOSE); + SSL_set_bio(port->ssl, bio, bio); + ret = 1; +err: + return ret; +} - 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; - } +/* + * 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; - /* - * 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; - } + /* 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; + } - port->peer_cn = peer_cn; + /* 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; } - port->peer_cert_valid = true; } - ereport(DEBUG2, - (errmsg("SSL connection from \"%s\"", - port->peer_cn ? port->peer_cn : "(anonymous)"))); + return dh; +} - /* set up debugging/info callback */ - SSL_CTX_set_info_callback(SSL_context, info_cb); +/* + * 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; - return 0; + 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; } /* - * Close SSL connection. + * 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. */ -void -be_tls_close(Port *port) +static DH * +tmp_dh_cb(SSL *s, int is_export, int keylength) { - if (port->ssl) - { - SSL_shutdown(port->ssl); - SSL_free(port->ssl); - port->ssl = NULL; - port->ssl_in_use = false; - } + DH *r = NULL; + static DH *dh = NULL; + static DH *dh512 = NULL; + static DH *dh1024 = NULL; + static DH *dh2048 = NULL; + static DH *dh4096 = NULL; - if (port->peer) + switch (keylength) { - X509_free(port->peer); - port->peer = NULL; + 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; } - if (port->peer_cn) + /* this may take a long time, but it may be necessary... */ + if (r == NULL || 8 * DH_size(r) < keylength) { - pfree(port->peer_cn); - port->peer_cn = NULL; + ereport(DEBUG2, + (errmsg_internal("DH: generating parameters (%d bits)", + keylength))); + r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL); } + + return r; } -ssize_t -be_tls_read(Port *port, void *ptr, size_t len) +/* + * 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) { - ssize_t n; - int err; + return ok; +} -rloop: - errno = 0; - n = SSL_read(port->ssl, ptr, len); - err = SSL_get_error(port->ssl, n); - switch (err) +/* + * 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_ERROR_NONE: - port->count += n; + case SSL_CB_HANDSHAKE_START: + ereport(DEBUG4, + (errmsg_internal("SSL: handshake start"))); 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; - } + case SSL_CB_HANDSHAKE_DONE: + ereport(DEBUG4, + (errmsg_internal("SSL: handshake done"))); 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; + case SSL_CB_ACCEPT_LOOP: + ereport(DEBUG4, + (errmsg_internal("SSL: accept loop"))); break; - default: - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("unrecognized SSL error code: %d", - err))); - errno = ECONNRESET; - n = -1; + 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; +static void +initialize_ecdh(void) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH) + 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); +#endif } /*