X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Ftls-openssl.c;h=a2e1136d0d5f6f3aad466d2a588eb01350b08a6c;hp=e2e150c0a139c9ca4cec7f25c4ba3e16c03a8bf4;hb=a3ef73105c3539e9d29c27095573f9d437752f7f;hpb=c80c557026f3933b0472b13331924f8bd4ed9bf7 diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index e2e150c0a..a2e1136d0 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2,9 +2,11 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2014 */ /* See the file NOTICE for conditions of use and distribution. */ +/* Portions Copyright (c) The OpenSSL Project 1999 */ + /* This module provides the TLS (aka SSL) support for Exim using the OpenSSL library. It is #included into the tls.c file when that library is used. The code herein is based on a patch that was originally contributed by Steve @@ -20,6 +22,34 @@ functions from the OpenSSL library. */ #include #include #include +#ifndef DISABLE_OCSP +# include +#endif +#ifdef EXPERIMENTAL_DANE +# include +#endif + + +#ifndef DISABLE_OCSP +# define EXIM_OCSP_SKEW_SECONDS (300L) +# define EXIM_OCSP_MAX_AGE (-1L) +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) +# define EXIM_HAVE_OPENSSL_TLSEXT +#endif +#if OPENSSL_VERSION_NUMBER >= 0x010100000L +# define EXIM_HAVE_OPENSSL_CHECKHOST +#endif +#if OPENSSL_VERSION_NUMBER >= 0x010000000L \ + && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L +# define EXIM_HAVE_OPENSSL_CHECKHOST +#endif + +#if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP) +# warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile" +# define DISABLE_OCSP +#endif /* Structure for collecting random data for seeding. */ @@ -30,19 +60,95 @@ typedef struct randstuff { /* Local static variables */ -static BOOL verify_callback_called = FALSE; +static BOOL client_verify_callback_called = FALSE; +static BOOL server_verify_callback_called = FALSE; static const uschar *sid_ctx = US"exim"; -static SSL_CTX *ctx = NULL; -static SSL *ssl = NULL; +/* We have three different contexts to care about. + +Simple case: client, `client_ctx` + As a client, we can be doing a callout or cut-through delivery while receiving + a message. So we have a client context, which should have options initialised + from the SMTP Transport. + +Server: + There are two cases: with and without ServerNameIndication from the client. + Given TLS SNI, we can be using different keys, certs and various other + configuration settings, because they're re-expanded with $tls_sni set. This + allows vhosting with TLS. This SNI is sent in the handshake. + A client might not send SNI, so we need a fallback, and an initial setup too. + So as a server, we start out using `server_ctx`. + If SNI is sent by the client, then we as server, mid-negotiation, try to clone + `server_sni` from `server_ctx` and then initialise settings by re-expanding + configuration. +*/ + +static SSL_CTX *client_ctx = NULL; +static SSL_CTX *server_ctx = NULL; +static SSL *client_ssl = NULL; +static SSL *server_ssl = NULL; + +#ifdef EXIM_HAVE_OPENSSL_TLSEXT +static SSL_CTX *server_sni = NULL; +#endif static char ssl_errstring[256]; static int ssl_session_timeout = 200; -static BOOL verify_optional = FALSE; +static BOOL client_verify_optional = FALSE; +static BOOL server_verify_optional = FALSE; + +static BOOL reexpand_tls_files_for_sni = FALSE; + + +typedef struct tls_ext_ctx_cb { + uschar *certificate; + uschar *privatekey; +#ifndef DISABLE_OCSP + BOOL is_server; + union { + struct { + uschar *file; + uschar *file_expanded; + OCSP_RESPONSE *response; + } server; + struct { + X509_STORE *verify_store; /* non-null if status requested */ + BOOL verify_required; + } client; + } u_ocsp; +#endif + uschar *dhparam; + /* these are cached from first expand */ + uschar *server_cipher_list; + /* only passed down to tls_error: */ + host_item *host; + +#ifdef EXPERIMENTAL_CERTNAMES + uschar * verify_cert_hostnames; +#endif +#ifdef EXPERIMENTAL_EVENT + uschar * event_action; +#endif +} tls_ext_ctx_cb; +/* should figure out a cleanup of API to handle state preserved per +implementation, for various reasons, which can be void * in the APIs. +For now, we hack around it. */ +tls_ext_ctx_cb *client_static_cbinfo = NULL; +tls_ext_ctx_cb *server_static_cbinfo = NULL; +static int +setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional, + int (*cert_vfy_cb)(int, X509_STORE_CTX *) ); +/* Callbacks */ +#ifdef EXIM_HAVE_OPENSSL_TLSEXT +static int tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg); +#endif +#ifndef DISABLE_OCSP +static int tls_server_stapling_cb(SSL *s, void *arg); +#endif /************************************************* @@ -68,27 +174,28 @@ Returns: OK/DEFER/FAIL static int tls_error(uschar *prefix, host_item *host, uschar *msg) { -if (msg == NULL) +if (!msg) { ERR_error_string(ERR_get_error(), ssl_errstring); msg = (uschar *)ssl_errstring; } -if (host == NULL) +if (host) + { + log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection (%s): %s", + host->name, host->address, prefix, msg); + return FAIL; + } +else { uschar *conn_info = smtp_get_connection_info(); if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; + /* I'd like to get separated H= here, but too hard for now */ log_write(0, LOG_MAIN, "TLS error on %s (%s): %s", conn_info, prefix, msg); return DEFER; } -else - { - log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s): %s", - host->name, host->address, prefix, msg); - return FAIL; - } } @@ -125,6 +232,29 @@ return rsa_key; +/* Extreme debug +#ifndef DISABLE_OCSP +void +x509_store_dump_cert_s_names(X509_STORE * store) +{ +STACK_OF(X509_OBJECT) * roots= store->objs; +int i; +static uschar name[256]; + +for(i= 0; itype == X509_LU_X509) + { + X509 * current_cert= tmp_obj->data.x509; + X509_NAME_oneline(X509_get_subject_name(current_cert), CS name, sizeof(name)); + debug_printf(" %s\n", name); + } + } +} +#endif +*/ + /************************************************* * Callback for verification * @@ -147,53 +277,220 @@ when asked. We get here only if a certificate has been received. Handling of optional verification for this case is done when requesting SSL to verify, by setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case. +May be called multiple times for different issues with a certificate, even +for a given "depth" in the certificate chain. + Arguments: state current yes/no state as 1/0 x509ctx certificate information. + client TRUE for client startup, FALSE for server startup Returns: 1 if verified, 0 if not */ static int -verify_callback(int state, X509_STORE_CTX *x509ctx) +verify_callback(int state, X509_STORE_CTX *x509ctx, + tls_support *tlsp, BOOL *calledp, BOOL *optionalp) { +X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); +int depth = X509_STORE_CTX_get_error_depth(x509ctx); +uschar * ev; static uschar txt[256]; -X509_NAME_oneline(X509_get_subject_name(x509ctx->current_cert), - CS txt, sizeof(txt)); +X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); if (state == 0) { log_write(0, LOG_MAIN, "SSL verify error: depth=%d error=%s cert=%s", - x509ctx->error_depth, - X509_verify_cert_error_string(x509ctx->error), + depth, + X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), txt); - tls_certificate_verified = FALSE; - verify_callback_called = TRUE; - if (!verify_optional) return 0; /* reject */ + *calledp = TRUE; + if (!*optionalp) + { + tlsp->peercert = X509_dup(cert); + return 0; /* reject */ + } DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " "tls_try_verify_hosts)\n"); - return 1; /* accept */ } -if (x509ctx->error_depth != 0) +else if (depth != 0) { - DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d cert=%s\n", - x509ctx->error_depth, txt); + DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, txt); +#ifndef DISABLE_OCSP + if (tlsp == &tls_out && client_static_cbinfo->u_ocsp.client.verify_store) + { /* client, wanting stapling */ + /* Add the server cert's signing chain as the one + for the verification of the OCSP stapled information. */ + + if (!X509_STORE_add_cert(client_static_cbinfo->u_ocsp.client.verify_store, + cert)) + ERR_clear_error(); + } +#endif +#ifdef EXPERIMENTAL_EVENT + ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action; + if (ev) + { + tlsp->peercert = X509_dup(cert); + if (event_raise(ev, US"tls:cert", string_sprintf("%d", depth)) == DEFER) + { + log_write(0, LOG_MAIN, "SSL verify denied by event-action: " + "depth=%d cert=%s", depth, txt); + *calledp = TRUE; + if (!*optionalp) + return 0; /* reject */ + DEBUG(D_tls) debug_printf("Event-action verify failure overridden " + "(host in tls_try_verify_hosts)\n"); + } + X509_free(tlsp->peercert); + tlsp->peercert = NULL; + } +#endif } else { - DEBUG(D_tls) debug_printf("SSL%s peer: %s\n", - verify_callback_called? "" : " authenticated", txt); - tls_peerdn = txt; +#ifdef EXPERIMENTAL_CERTNAMES + uschar * verify_cert_hostnames; +#endif + + tlsp->peerdn = txt; + tlsp->peercert = X509_dup(cert); + +#ifdef EXPERIMENTAL_CERTNAMES + if ( tlsp == &tls_out + && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames))) + /* client, wanting hostname check */ + +# if EXIM_HAVE_OPENSSL_CHECKHOST +# ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS +# define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0 +# endif + { + int sep = 0; + uschar * list = verify_cert_hostnames; + uschar * name; + int rc; + while ((name = string_nextinlist(&list, &sep, NULL, 0))) + if ((rc = X509_check_host(cert, name, 0, + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS))) + { + if (rc < 0) + { + log_write(0, LOG_MAIN, "SSL verify error: internal error\n"); + name = NULL; + } + break; + } + if (!name) + { + log_write(0, LOG_MAIN, + "SSL verify error: certificate name mismatch: \"%s\"\n", txt); + *calledp = TRUE; + if (!*optionalp) + return 0; /* reject */ + DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " + "tls_try_verify_hosts)\n"); + } + } +# else + if (!tls_is_name_for_cert(verify_cert_hostnames, cert)) + { + log_write(0, LOG_MAIN, + "SSL verify error: certificate name mismatch: \"%s\"\n", txt); + *calledp = TRUE; + if (!*optionalp) + return 0; /* reject */ + DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " + "tls_try_verify_hosts)\n"); + } +# endif +#endif /*EXPERIMENTAL_CERTNAMES*/ + +#ifdef EXPERIMENTAL_EVENT + ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action; + if (ev) + if (event_raise(ev, US"tls:cert", US"0") == DEFER) + { + log_write(0, LOG_MAIN, "SSL verify denied by event-action: " + "depth=0 cert=%s", txt); + *calledp = TRUE; + if (!*optionalp) + return 0; /* reject */ + DEBUG(D_tls) debug_printf("Event-action verify failure overridden " + "(host in tls_try_verify_hosts)\n"); + } +#endif + + DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n", + *calledp ? "" : " authenticated", txt); + if (!*calledp) tlsp->certificate_verified = TRUE; + *calledp = TRUE; } -if (!verify_callback_called) tls_certificate_verified = TRUE; -verify_callback_called = TRUE; +return 1; /* accept, at least for this level */ +} + +static int +verify_callback_client(int state, X509_STORE_CTX *x509ctx) +{ +return verify_callback(state, x509ctx, &tls_out, &client_verify_callback_called, &client_verify_optional); +} + +static int +verify_callback_server(int state, X509_STORE_CTX *x509ctx) +{ +return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called, &server_verify_optional); +} + + +#ifdef EXPERIMENTAL_DANE + +/* This gets called *by* the dane library verify callback, which interposes +itself. +*/ +static int +verify_callback_client_dane(int state, X509_STORE_CTX * x509ctx) +{ +X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); +static uschar txt[256]; +#ifdef EXPERIMENTAL_EVENT +int depth = X509_STORE_CTX_get_error_depth(x509ctx); +#endif + +X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); + +DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", txt); +tls_out.peerdn = txt; +tls_out.peercert = X509_dup(cert); + +#ifdef EXPERIMENTAL_EVENT + if (client_static_cbinfo->event_action) + { + if (event_raise(client_static_cbinfo->event_action, + US"tls:cert", string_sprintf("%d", depth)) == DEFER) + { + log_write(0, LOG_MAIN, "DANE verify denied by event-action: " + "depth=%d cert=%s", depth, txt); + tls_out.certificate_verified = FALSE; + return 0; /* reject */ + } + if (depth != 0) + { + X509_free(tls_out.peercert); + tls_out.peercert = NULL; + } + } +#endif -return 1; /* accept */ +if (state == 1) + tls_out.dane_verified = + tls_out.certificate_verified = TRUE; +return 1; } +#endif /*EXPERIMENTAL_DANE*/ /************************************************* @@ -201,8 +498,8 @@ return 1; /* accept */ *************************************************/ /* The SSL library functions call this from time to time to indicate what they -are doing. We copy the string to the debugging output when the level is high -enough. +are doing. We copy the string to the debugging output when TLS debugging has +been requested. Arguments: s the SSL connection @@ -229,79 +526,640 @@ DEBUG(D_tls) debug_printf("SSL info: %s\n", SSL_state_string_long(s)); /* If dhparam is set, expand it, and load up the parameters for DH encryption. Arguments: - dhparam DH parameter file + dhparam DH parameter file or fixed parameter identity string host connected host, if client; NULL if server Returns: TRUE if OK (nothing to set up, or setup worked) */ static BOOL -init_dh(uschar *dhparam, host_item *host) +init_dh(SSL_CTX *sctx, uschar *dhparam, host_item *host) { -BOOL yield = TRUE; BIO *bio; DH *dh; uschar *dhexpanded; +const char *pem; if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded)) return FALSE; -if (dhexpanded == NULL) return TRUE; - -if ((bio = BIO_new_file(CS dhexpanded, "r")) == NULL) +if (!dhexpanded || !*dhexpanded) + bio = BIO_new_mem_buf(CS std_dh_prime_default(), -1); +else if (dhexpanded[0] == '/') { - tls_error(string_sprintf("could not read dhparams file %s", dhexpanded), - host, (uschar *)strerror(errno)); - yield = FALSE; + if (!(bio = BIO_new_file(CS dhexpanded, "r"))) + { + tls_error(string_sprintf("could not read dhparams file %s", dhexpanded), + host, US strerror(errno)); + return FALSE; + } } else { - if ((dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)) == NULL) + if (Ustrcmp(dhexpanded, "none") == 0) { - tls_error(string_sprintf("could not read dhparams file %s", dhexpanded), - host, NULL); - yield = FALSE; + DEBUG(D_tls) debug_printf("Requested no DH parameters.\n"); + return TRUE; } - else + + if (!(pem = std_dh_prime_named(dhexpanded))) { - SSL_CTX_set_tmp_dh(ctx, dh); - DEBUG(D_tls) - debug_printf("Diffie-Hellman initialized from %s with %d-bit key\n", - dhexpanded, 8*DH_size(dh)); - DH_free(dh); + tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded), + host, US strerror(errno)); + return FALSE; } + bio = BIO_new_mem_buf(CS pem, -1); + } + +if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL))) + { BIO_free(bio); + tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded), + host, NULL); + return FALSE; + } + +/* Even if it is larger, we silently return success rather than cause things + * to fail out, so that a too-large DH will not knock out all TLS; it's a + * debatable choice. */ +if ((8*DH_size(dh)) > tls_dh_max_bits) + { + DEBUG(D_tls) + debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d", + 8*DH_size(dh), tls_dh_max_bits); + } +else + { + SSL_CTX_set_tmp_dh(sctx, dh); + DEBUG(D_tls) + debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n", + dhexpanded ? dhexpanded : US"default", 8*DH_size(dh)); + } + +DH_free(dh); +BIO_free(bio); + +return TRUE; +} + + + + +#ifndef DISABLE_OCSP +/************************************************* +* Load OCSP information into state * +*************************************************/ + +/* Called to load the server OCSP response from the given file into memory, once +caller has determined this is needed. Checks validity. Debugs a message +if invalid. + +ASSUMES: single response, for single cert. + +Arguments: + sctx the SSL_CTX* to update + cbinfo various parts of session state + expanded the filename putatively holding an OCSP response + +*/ + +static void +ocsp_load_response(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo, const uschar *expanded) +{ +BIO *bio; +OCSP_RESPONSE *resp; +OCSP_BASICRESP *basic_response; +OCSP_SINGLERESP *single_response; +ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; +X509_STORE *store; +unsigned long verify_flags; +int status, reason, i; + +cbinfo->u_ocsp.server.file_expanded = string_copy(expanded); +if (cbinfo->u_ocsp.server.response) + { + OCSP_RESPONSE_free(cbinfo->u_ocsp.server.response); + cbinfo->u_ocsp.server.response = NULL; + } + +bio = BIO_new_file(CS cbinfo->u_ocsp.server.file_expanded, "rb"); +if (!bio) + { + DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n", + cbinfo->u_ocsp.server.file_expanded); + return; + } + +resp = d2i_OCSP_RESPONSE_bio(bio, NULL); +BIO_free(bio); +if (!resp) + { + DEBUG(D_tls) debug_printf("Error reading OCSP response.\n"); + return; + } + +status = OCSP_response_status(resp); +if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) + { + DEBUG(D_tls) debug_printf("OCSP response not valid: %s (%d)\n", + OCSP_response_status_str(status), status); + goto bad; + } + +basic_response = OCSP_response_get1_basic(resp); +if (!basic_response) + { + DEBUG(D_tls) + debug_printf("OCSP response parse error: unable to extract basic response.\n"); + goto bad; + } + +store = SSL_CTX_get_cert_store(sctx); +verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */ + +/* May need to expose ability to adjust those flags? +OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT +OCSP_TRUSTOTHER OCSP_NOINTERN */ + +i = OCSP_basic_verify(basic_response, NULL, store, verify_flags); +if (i <= 0) + { + DEBUG(D_tls) { + ERR_error_string(ERR_get_error(), ssl_errstring); + debug_printf("OCSP response verify failure: %s\n", US ssl_errstring); + } + goto bad; + } + +/* Here's the simplifying assumption: there's only one response, for the +one certificate we use, and nothing for anything else in a chain. If this +proves false, we need to extract a cert id from our issued cert +(tls_certificate) and use that for OCSP_resp_find_status() (which finds the +right cert in the stack and then calls OCSP_single_get0_status()). + +I'm hoping to avoid reworking a bunch more of how we handle state here. */ +single_response = OCSP_resp_get0(basic_response, 0); +if (!single_response) + { + DEBUG(D_tls) + debug_printf("Unable to get first response from OCSP basic response.\n"); + goto bad; + } + +status = OCSP_single_get0_status(single_response, &reason, &rev, &thisupd, &nextupd); +if (status != V_OCSP_CERTSTATUS_GOOD) + { + DEBUG(D_tls) debug_printf("OCSP response bad cert status: %s (%d) %s (%d)\n", + OCSP_cert_status_str(status), status, + OCSP_crl_reason_str(reason), reason); + goto bad; + } + +if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE)) + { + DEBUG(D_tls) debug_printf("OCSP status invalid times.\n"); + goto bad; + } + +supply_response: + cbinfo->u_ocsp.server.response = resp; +return; + +bad: + if (running_in_test_harness) + { + extern char ** environ; + uschar ** p; + for (p = USS environ; *p != NULL; p++) + if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0) + { + DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n"); + goto supply_response; + } + } +return; +} +#endif /*!DISABLE_OCSP*/ + + + + +/************************************************* +* Expand key and cert file specs * +*************************************************/ + +/* Called once during tls_init and possibly again during TLS setup, for a +new context, if Server Name Indication was used and tls_sni was seen in +the certificate string. + +Arguments: + sctx the SSL_CTX* to update + cbinfo various parts of session state + +Returns: OK/DEFER/FAIL +*/ + +static int +tls_expand_session_files(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo) +{ +uschar *expanded; + +if (cbinfo->certificate == NULL) + return OK; + +if (Ustrstr(cbinfo->certificate, US"tls_sni") || + Ustrstr(cbinfo->certificate, US"tls_in_sni") || + Ustrstr(cbinfo->certificate, US"tls_out_sni") + ) + reexpand_tls_files_for_sni = TRUE; + +if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded)) + return DEFER; + +if (expanded != NULL) + { + DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded); + if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded)) + return tls_error(string_sprintf( + "SSL_CTX_use_certificate_chain_file file=%s", expanded), + cbinfo->host, NULL); + } + +if (cbinfo->privatekey != NULL && + !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded)) + return DEFER; + +/* If expansion was forced to fail, key_expanded will be NULL. If the result +of the expansion is an empty string, ignore it also, and assume the private +key is in the same file as the certificate. */ + +if (expanded != NULL && *expanded != 0) + { + DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded); + if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM)) + return tls_error(string_sprintf( + "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL); + } + +#ifndef DISABLE_OCSP +if (cbinfo->is_server && cbinfo->u_ocsp.server.file != NULL) + { + if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded)) + return DEFER; + + if (expanded != NULL && *expanded != 0) + { + DEBUG(D_tls) debug_printf("tls_ocsp_file %s\n", expanded); + if (cbinfo->u_ocsp.server.file_expanded && + (Ustrcmp(expanded, cbinfo->u_ocsp.server.file_expanded) == 0)) + { + DEBUG(D_tls) + debug_printf("tls_ocsp_file value unchanged, using existing values.\n"); + } else { + ocsp_load_response(sctx, cbinfo, expanded); + } + } } +#endif -return yield; +return OK; } +/************************************************* +* Callback to handle SNI * +*************************************************/ + +/* Called when acting as server during the TLS session setup if a Server Name +Indication extension was sent by the client. + +API documentation is OpenSSL s_server.c implementation. + +Arguments: + s SSL* of the current session + ad unknown (part of OpenSSL API) (unused) + arg Callback of "our" registered data + +Returns: SSL_TLSEXT_ERR_{OK,ALERT_WARNING,ALERT_FATAL,NOACK} +*/ + +#ifdef EXIM_HAVE_OPENSSL_TLSEXT +static int +tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg) +{ +const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); +tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg; +int rc; +int old_pool = store_pool; + +if (!servername) + return SSL_TLSEXT_ERR_OK; + +DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername, + reexpand_tls_files_for_sni ? "" : " (unused for certificate selection)"); + +/* Make the extension value available for expansion */ +store_pool = POOL_PERM; +tls_in.sni = string_copy(US servername); +store_pool = old_pool; + +if (!reexpand_tls_files_for_sni) + return SSL_TLSEXT_ERR_OK; + +/* Can't find an SSL_CTX_clone() or equivalent, so we do it manually; +not confident that memcpy wouldn't break some internal reference counting. +Especially since there's a references struct member, which would be off. */ + +if (!(server_sni = SSL_CTX_new(SSLv23_server_method()))) + { + ERR_error_string(ERR_get_error(), ssl_errstring); + DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring); + return SSL_TLSEXT_ERR_NOACK; + } + +/* Not sure how many of these are actually needed, since SSL object +already exists. Might even need this selfsame callback, for reneg? */ + +SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(server_ctx)); +SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(server_ctx)); +SSL_CTX_set_options(server_sni, SSL_CTX_get_options(server_ctx)); +SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(server_ctx)); +SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb); +SSL_CTX_set_tlsext_servername_arg(server_sni, cbinfo); +if (cbinfo->server_cipher_list) + SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list); +#ifndef DISABLE_OCSP +if (cbinfo->u_ocsp.server.file) + { + SSL_CTX_set_tlsext_status_cb(server_sni, tls_server_stapling_cb); + SSL_CTX_set_tlsext_status_arg(server_sni, cbinfo); + } +#endif + +rc = setup_certs(server_sni, tls_verify_certificates, tls_crl, NULL, FALSE, verify_callback_server); +if (rc != OK) return SSL_TLSEXT_ERR_NOACK; + +/* do this after setup_certs, because this can require the certs for verifying +OCSP information. */ +rc = tls_expand_session_files(server_sni, cbinfo); +if (rc != OK) return SSL_TLSEXT_ERR_NOACK; + +if (!init_dh(server_sni, cbinfo->dhparam, NULL)) + return SSL_TLSEXT_ERR_NOACK; + +DEBUG(D_tls) debug_printf("Switching SSL context.\n"); +SSL_set_SSL_CTX(s, server_sni); + +return SSL_TLSEXT_ERR_OK; +} +#endif /* EXIM_HAVE_OPENSSL_TLSEXT */ + + + + +#ifndef DISABLE_OCSP + +/************************************************* +* Callback to handle OCSP Stapling * +*************************************************/ + +/* Called when acting as server during the TLS session setup if the client +requests OCSP information with a Certificate Status Request. + +Documentation via openssl s_server.c and the Apache patch from the OpenSSL +project. + +*/ + +static int +tls_server_stapling_cb(SSL *s, void *arg) +{ +const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg; +uschar *response_der; +int response_der_len; + +DEBUG(D_tls) + debug_printf("Received TLS status request (OCSP stapling); %s response.", + cbinfo->u_ocsp.server.response ? "have" : "lack"); + +tls_in.ocsp = OCSP_NOT_RESP; +if (!cbinfo->u_ocsp.server.response) + return SSL_TLSEXT_ERR_NOACK; + +response_der = NULL; +response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response, + &response_der); +if (response_der_len <= 0) + return SSL_TLSEXT_ERR_NOACK; + +SSL_set_tlsext_status_ocsp_resp(server_ssl, response_der, response_der_len); +tls_in.ocsp = OCSP_VFIED; +return SSL_TLSEXT_ERR_OK; +} + + +static void +time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time) +{ +BIO_printf(bp, "\t%s: ", str); +ASN1_GENERALIZEDTIME_print(bp, time); +BIO_puts(bp, "\n"); +} + +static int +tls_client_stapling_cb(SSL *s, void *arg) +{ +tls_ext_ctx_cb * cbinfo = arg; +const unsigned char * p; +int len; +OCSP_RESPONSE * rsp; +OCSP_BASICRESP * bs; +int i; + +DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):"); +len = SSL_get_tlsext_status_ocsp_resp(s, &p); +if(!p) + { + /* Expect this when we requested ocsp but got none */ + if ( cbinfo->u_ocsp.client.verify_required + && log_extra_selector & LX_tls_cipher) + log_write(0, LOG_MAIN, "Received TLS status callback, null content"); + else + DEBUG(D_tls) debug_printf(" null\n"); + return cbinfo->u_ocsp.client.verify_required ? 0 : 1; + } + +if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) + { + tls_out.ocsp = OCSP_FAILED; + if (log_extra_selector & LX_tls_cipher) + log_write(0, LOG_MAIN, "Received TLS cert status response, parse error"); + else + DEBUG(D_tls) debug_printf(" parse error\n"); + return 0; + } + +if(!(bs = OCSP_response_get1_basic(rsp))) + { + tls_out.ocsp = OCSP_FAILED; + if (log_extra_selector & LX_tls_cipher) + log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response"); + else + DEBUG(D_tls) debug_printf(" error parsing response\n"); + OCSP_RESPONSE_free(rsp); + return 0; + } + +/* We'd check the nonce here if we'd put one in the request. */ +/* However that would defeat cacheability on the server so we don't. */ + +/* This section of code reworked from OpenSSL apps source; + The OpenSSL Project retains copyright: + Copyright (c) 1999 The OpenSSL Project. All rights reserved. +*/ + { + BIO * bp = NULL; + int status, reason; + ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; + + DEBUG(D_tls) bp = BIO_new_fp(stderr, BIO_NOCLOSE); + + /*OCSP_RESPONSE_print(bp, rsp, 0); extreme debug: stapling content */ + + /* Use the chain that verified the server cert to verify the stapled info */ + /* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */ + + if ((i = OCSP_basic_verify(bs, NULL, + cbinfo->u_ocsp.client.verify_store, 0)) <= 0) + { + tls_out.ocsp = OCSP_FAILED; + if (log_extra_selector & LX_tls_cipher) + log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable"); + BIO_printf(bp, "OCSP response verify failure\n"); + ERR_print_errors(bp); + i = cbinfo->u_ocsp.client.verify_required ? 0 : 1; + goto out; + } + + BIO_printf(bp, "OCSP response well-formed and signed OK\n"); + + { + STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses; + OCSP_SINGLERESP * single; + + if (sk_OCSP_SINGLERESP_num(sresp) != 1) + { + tls_out.ocsp = OCSP_FAILED; + log_write(0, LOG_MAIN, "OCSP stapling " + "with multiple responses not handled"); + i = cbinfo->u_ocsp.client.verify_required ? 0 : 1; + goto out; + } + single = OCSP_resp_get0(bs, 0); + status = OCSP_single_get0_status(single, &reason, &rev, + &thisupd, &nextupd); + } + + DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd); + DEBUG(D_tls) if(nextupd) time_print(bp, "Next OCSP Update", nextupd); + if (!OCSP_check_validity(thisupd, nextupd, + EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE)) + { + tls_out.ocsp = OCSP_FAILED; + DEBUG(D_tls) ERR_print_errors(bp); + log_write(0, LOG_MAIN, "Server OSCP dates invalid"); + i = cbinfo->u_ocsp.client.verify_required ? 0 : 1; + } + else + { + DEBUG(D_tls) BIO_printf(bp, "Certificate status: %s\n", + OCSP_cert_status_str(status)); + switch(status) + { + case V_OCSP_CERTSTATUS_GOOD: + tls_out.ocsp = OCSP_VFIED; + i = 1; + break; + case V_OCSP_CERTSTATUS_REVOKED: + tls_out.ocsp = OCSP_FAILED; + log_write(0, LOG_MAIN, "Server certificate revoked%s%s", + reason != -1 ? "; reason: " : "", + reason != -1 ? OCSP_crl_reason_str(reason) : ""); + DEBUG(D_tls) time_print(bp, "Revocation Time", rev); + i = cbinfo->u_ocsp.client.verify_required ? 0 : 1; + break; + default: + tls_out.ocsp = OCSP_FAILED; + log_write(0, LOG_MAIN, + "Server certificate status unknown, in OCSP stapling"); + i = cbinfo->u_ocsp.client.verify_required ? 0 : 1; + break; + } + } + out: + BIO_free(bp); + } + +OCSP_RESPONSE_free(rsp); +return i; +} +#endif /*!DISABLE_OCSP*/ + + /************************************************* * Initialize for TLS * *************************************************/ -/* Called from both server and client code, to do preliminary initialization of -the library. +/* Called from both server and client code, to do preliminary initialization +of the library. We allocate and return a context structure. Arguments: + ctxp returned SSL context host connected host, if client; NULL if server dhparam DH parameter file certificate certificate file privatekey private key + ocsp_file file of stapling info (server); flag for require ocsp (client) addr address if client; NULL if server (for some randomness) + cbp place to put allocated callback context Returns: OK/DEFER/FAIL */ static int -tls_init(host_item *host, uschar *dhparam, uschar *certificate, - uschar *privatekey, address_item *addr) +tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate, + uschar *privatekey, +#ifndef DISABLE_OCSP + uschar *ocsp_file, +#endif + address_item *addr, tls_ext_ctx_cb ** cbp) { long init_options; +int rc; BOOL okay; +tls_ext_ctx_cb * cbinfo; + +cbinfo = store_malloc(sizeof(tls_ext_ctx_cb)); +cbinfo->certificate = certificate; +cbinfo->privatekey = privatekey; +#ifndef DISABLE_OCSP +if ((cbinfo->is_server = host==NULL)) + { + cbinfo->u_ocsp.server.file = ocsp_file; + cbinfo->u_ocsp.server.file_expanded = NULL; + cbinfo->u_ocsp.server.response = NULL; + } +else + cbinfo->u_ocsp.client.verify_store = NULL; +#endif +cbinfo->dhparam = dhparam; +cbinfo->server_cipher_list = NULL; +cbinfo->host = host; +#ifdef EXPERIMENTAL_EVENT +cbinfo->event_action = NULL; +#endif SSL_load_error_strings(); /* basic set up */ OpenSSL_add_ssl_algorithms(); @@ -312,12 +1170,18 @@ list of available digests. */ EVP_add_digest(EVP_sha256()); #endif -/* Create a context */ +/* Create a context. +The OpenSSL docs in 1.0.1b have not been updated to clarify TLS variant +negotiation in the different methods; as far as I can tell, the only +*_{server,client}_method which allows negotiation is SSLv23, which exists even +when OpenSSL is built without SSLv2 support. +By disabling with openssl_options, we can let admins re-enable with the +existing knob. */ -ctx = SSL_CTX_new((host == NULL)? +*ctxp = SSL_CTX_new((host == NULL)? SSLv23_server_method() : SSLv23_client_method()); -if (ctx == NULL) return tls_error(US"SSL_CTX_new", host, NULL); +if (*ctxp == NULL) return tls_error(US"SSL_CTX_new", host, NULL); /* It turns out that we need to seed the random number generator this early in order to get the full complement of ciphers to work. It took me roughly a day @@ -345,10 +1209,10 @@ if (!RAND_status()) /* Set up the information callback, which outputs if debugging is at a suitable level. */ -SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); +SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback); /* Automatically re-try reads/writes after renegotiation. */ -(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); +(void) SSL_CTX_set_mode(*ctxp, SSL_MODE_AUTO_RETRY); /* Apply administrator-supplied work-arounds. Historically we applied just one requested option, @@ -366,7 +1230,7 @@ if (!okay) if (init_options) { DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options); - if (!(SSL_CTX_set_options(ctx, init_options))) + if (!(SSL_CTX_set_options(*ctxp, init_options))) return tls_error(string_sprintf( "SSL_CTX_set_option(%#lx)", init_options), host, NULL); } @@ -375,49 +1239,63 @@ else /* Initialize with DH parameters if supplied */ -if (!init_dh(dhparam, host)) return DEFER; +if (!init_dh(*ctxp, dhparam, host)) return DEFER; -/* Set up certificate and key */ +/* Set up certificate and key (and perhaps OCSP info) */ -if (certificate != NULL) - { - uschar *expanded; - if (!expand_check(certificate, US"tls_certificate", &expanded)) - return DEFER; +rc = tls_expand_session_files(*ctxp, cbinfo); +if (rc != OK) return rc; - if (expanded != NULL) +/* If we need to handle SNI, do so */ +#ifdef EXIM_HAVE_OPENSSL_TLSEXT +if (host == NULL) /* server */ + { +# ifndef DISABLE_OCSP + /* We check u_ocsp.server.file, not server.response, because we care about if + the option exists, not what the current expansion might be, as SNI might + change the certificate and OCSP file in use between now and the time the + callback is invoked. */ + if (cbinfo->u_ocsp.server.file) { - DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded); - if (!SSL_CTX_use_certificate_chain_file(ctx, CS expanded)) - return tls_error(string_sprintf( - "SSL_CTX_use_certificate_chain_file file=%s", expanded), host, NULL); + SSL_CTX_set_tlsext_status_cb(server_ctx, tls_server_stapling_cb); + SSL_CTX_set_tlsext_status_arg(server_ctx, cbinfo); } - - if (privatekey != NULL && - !expand_check(privatekey, US"tls_privatekey", &expanded)) - return DEFER; - - /* If expansion was forced to fail, key_expanded will be NULL. If the result - of the expansion is an empty string, ignore it also, and assume the private - key is in the same file as the certificate. */ - - if (expanded != NULL && *expanded != 0) +# endif + /* We always do this, so that $tls_sni is available even if not used in + tls_certificate */ + SSL_CTX_set_tlsext_servername_callback(*ctxp, tls_servername_cb); + SSL_CTX_set_tlsext_servername_arg(*ctxp, cbinfo); + } +# ifndef DISABLE_OCSP +else /* client */ + if(ocsp_file) /* wanting stapling */ { - DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded); - if (!SSL_CTX_use_PrivateKey_file(ctx, CS expanded, SSL_FILETYPE_PEM)) - return tls_error(string_sprintf( - "SSL_CTX_use_PrivateKey_file file=%s", expanded), host, NULL); + if (!(cbinfo->u_ocsp.client.verify_store = X509_STORE_new())) + { + DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n"); + return FAIL; + } + SSL_CTX_set_tlsext_status_cb(*ctxp, tls_client_stapling_cb); + SSL_CTX_set_tlsext_status_arg(*ctxp, cbinfo); } - } +# endif +#endif + +#ifdef EXPERIMENTAL_CERTNAMES +cbinfo->verify_cert_hostnames = NULL; +#endif /* Set up the RSA callback */ -SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback); +SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback); /* Finally, set the timeout, and we are done */ -SSL_CTX_set_timeout(ctx, ssl_session_timeout); +SSL_CTX_set_timeout(*ctxp, ssl_session_timeout); DEBUG(D_tls) debug_printf("Initialized TLS\n"); + +*cbp = cbinfo; + return OK; } @@ -428,59 +1306,30 @@ return OK; * Get name of cipher in use * *************************************************/ -/* The answer is left in a static buffer, and tls_cipher is set to point -to it. - +/* Argument: pointer to an SSL structure for the connection + buffer to use for answer + size of buffer + pointer to number of bits for cipher Returns: nothing */ static void -construct_cipher_name(SSL *ssl) +construct_cipher_name(SSL *ssl, uschar *cipherbuf, int bsize, int *bits) { -static uschar cipherbuf[256]; /* With OpenSSL 1.0.0a, this needs to be const but the documentation doesn't yet reflect that. It should be a safe change anyway, even 0.9.8 versions have the accessor functions use const in the prototype. */ const SSL_CIPHER *c; -uschar *ver; - -switch (ssl->session->ssl_version) - { - case SSL2_VERSION: - ver = US"SSLv2"; - break; +const uschar *ver; - case SSL3_VERSION: - ver = US"SSLv3"; - break; - - case TLS1_VERSION: - ver = US"TLSv1"; - break; - -#ifdef TLS1_1_VERSION - case TLS1_1_VERSION: - ver = US"TLSv1.1"; - break; -#endif - -#ifdef TLS1_2_VERSION - case TLS1_2_VERSION: - ver = US"TLSv1.2"; - break; -#endif - - default: - ver = US"UNKNOWN"; - } +ver = (const uschar *)SSL_get_version(ssl); c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl); -SSL_CIPHER_get_bits(c, &tls_bits); +SSL_CIPHER_get_bits(c, bits); -string_format(cipherbuf, sizeof(cipherbuf), "%s:%s:%u", ver, - SSL_CIPHER_get_name(c), tls_bits); -tls_cipher = cipherbuf; +string_format(cipherbuf, bsize, "%s:%s:%u", ver, + SSL_CIPHER_get_name(c), *bits); DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf); } @@ -496,27 +1345,30 @@ DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf); /* Called by both client and server startup Arguments: + sctx SSL_CTX* to initialise certs certs file or NULL crl CRL file or NULL host NULL in a server; the remote host in a client optional TRUE if called from a server for a host in tls_try_verify_hosts; otherwise passed as FALSE + cert_vfy_cb Callback function for certificate verification Returns: OK/DEFER/FAIL */ static int -setup_certs(uschar *certs, uschar *crl, host_item *host, BOOL optional) +setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional, + int (*cert_vfy_cb)(int, X509_STORE_CTX *) ) { uschar *expcerts, *expcrl; if (!expand_check(certs, US"tls_verify_certificates", &expcerts)) return DEFER; -if (expcerts != NULL) +if (expcerts != NULL && *expcerts != '\0') { struct stat statbuf; - if (!SSL_CTX_set_default_verify_paths(ctx)) + if (!SSL_CTX_set_default_verify_paths(sctx)) return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); if (Ustat(expcerts, &statbuf) < 0) @@ -539,12 +1391,26 @@ if (expcerts != NULL) says no certificate was supplied.) But this is better. */ if ((file == NULL || statbuf.st_size > 0) && - !SSL_CTX_load_verify_locations(ctx, CS file, CS dir)) + !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) return tls_error(US"SSL_CTX_load_verify_locations", host, NULL); + /* Load the list of CAs for which we will accept certs, for sending + to the client. This is only for the one-file tls_verify_certificates + variant. + If a list isn't loaded into the server, but + some verify locations are set, the server end appears to make + a wildcard reqest for client certs. + Meanwhile, the client library as deafult behaviour *ignores* the list + we send over the wire - see man SSL_CTX_set_client_cert_cb. + Because of this, and that the dir variant is likely only used for + the public-CA bundle (not for a private CA), not worth fixing. + */ if (file != NULL) { - SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CS file)); + STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file); +DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", + sk_X509_NAME_num(names)); + SSL_CTX_set_client_CA_list(sctx, names); } } @@ -576,7 +1442,7 @@ if (expcerts != NULL) { /* is it a file or directory? */ uschar *file, *dir; - X509_STORE *cvstore = SSL_CTX_get_cert_store(ctx); + X509_STORE *cvstore = SSL_CTX_get_cert_store(sctx); if ((statbufcrl.st_mode & S_IFMT) == S_IFDIR) { file = NULL; @@ -603,9 +1469,9 @@ if (expcerts != NULL) /* If verification is optional, don't fail if no certificate */ - SSL_CTX_set_verify(ctx, + SSL_CTX_set_verify(sctx, SSL_VERIFY_PEER | (optional? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT), - verify_callback); + cert_vfy_cb); } return OK; @@ -623,11 +1489,6 @@ a TLS session. Arguments: require_ciphers allowed ciphers - ------------------------------------------------------ - require_mac list of allowed MACs ) Not used - require_kx list of allowed key_exchange methods ) for - require_proto list of allowed protocols ) OpenSSL - ------------------------------------------------------ Returns: OK on success DEFER for errors before the start of the negotiation @@ -636,15 +1497,16 @@ Returns: OK on success */ int -tls_server_start(uschar *require_ciphers, uschar *require_mac, - uschar *require_kx, uschar *require_proto) +tls_server_start(const uschar *require_ciphers) { int rc; uschar *expciphers; +tls_ext_ctx_cb *cbinfo; +static uschar cipherbuf[256]; /* Check for previous activation */ -if (tls_active >= 0) +if (tls_in.active >= 0) { tls_error(US"STARTTLS received after TLS started", NULL, US""); smtp_printf("554 Already in TLS\r\n"); @@ -654,48 +1516,72 @@ if (tls_active >= 0) /* Initialize the SSL library. If it fails, it will already have logged the error. */ -rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey, NULL); +rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey, +#ifndef DISABLE_OCSP + tls_ocsp_file, +#endif + NULL, &server_static_cbinfo); if (rc != OK) return rc; +cbinfo = server_static_cbinfo; if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers)) return FAIL; /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they -are separated by underscores. So that I can use either form in my tests, and -also for general convenience, we turn underscores into hyphens here. */ +were historically separated by underscores. So that I can use either form in my +tests, and also for general convenience, we turn underscores into hyphens here. +*/ if (expciphers != NULL) { uschar *s = expciphers; while (*s != 0) { if (*s == '_') *s = '-'; s++; } DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers); - if (!SSL_CTX_set_cipher_list(ctx, CS expciphers)) + if (!SSL_CTX_set_cipher_list(server_ctx, CS expciphers)) return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL); + cbinfo->server_cipher_list = expciphers; } /* If this is a host for which certificate verification is mandatory or optional, set up appropriately. */ -tls_certificate_verified = FALSE; -verify_callback_called = FALSE; +tls_in.certificate_verified = FALSE; +#ifdef EXPERIMENTAL_DANE +tls_in.dane_verified = FALSE; +#endif +server_verify_callback_called = FALSE; if (verify_check_host(&tls_verify_hosts) == OK) { - rc = setup_certs(tls_verify_certificates, tls_crl, NULL, FALSE); + rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL, + FALSE, verify_callback_server); if (rc != OK) return rc; - verify_optional = FALSE; + server_verify_optional = FALSE; } else if (verify_check_host(&tls_try_verify_hosts) == OK) { - rc = setup_certs(tls_verify_certificates, tls_crl, NULL, TRUE); + rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL, + TRUE, verify_callback_server); if (rc != OK) return rc; - verify_optional = TRUE; + server_verify_optional = TRUE; } /* Prepare for new connection */ -if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", NULL, NULL); -SSL_clear(ssl); +if ((server_ssl = SSL_new(server_ctx)) == NULL) return tls_error(US"SSL_new", NULL, NULL); + +/* Warning: we used to SSL_clear(ssl) here, it was removed. + * + * With the SSL_clear(), we get strange interoperability bugs with + * OpenSSL 1.0.1b and TLS1.1/1.2. It looks as though this may be a bug in + * OpenSSL itself, as a clear should not lead to inability to follow protocols. + * + * The SSL_clear() call is to let an existing SSL* be reused, typically after + * session shutdown. In this case, we have a brand new object and there's no + * obvious reason to immediately clear it. I'm guessing that this was + * originally added because of incomplete initialisation which the clear fixed, + * in some historic release. + */ /* Set context and tell client to go ahead, except in the case of TLS startup on connection, where outputting anything now upsets the clients and tends to @@ -703,8 +1589,8 @@ make them disconnect. We need to have an explicit fflush() here, to force out the response. Other smtp_printf() calls do not need it, because in non-TLS mode, the fflush() happens when smtp_getc() is called. */ -SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx)); -if (!tls_on_connect) +SSL_set_session_id_context(server_ssl, sid_ctx, Ustrlen(sid_ctx)); +if (!tls_in.on_connect) { smtp_printf("220 TLS go ahead\r\n"); fflush(smtp_out); @@ -713,15 +1599,15 @@ if (!tls_on_connect) /* Now negotiate the TLS session. We put our own timer on it, since it seems that the OpenSSL library doesn't. */ -SSL_set_wfd(ssl, fileno(smtp_out)); -SSL_set_rfd(ssl, fileno(smtp_in)); -SSL_set_accept_state(ssl); +SSL_set_wfd(server_ssl, fileno(smtp_out)); +SSL_set_rfd(server_ssl, fileno(smtp_in)); +SSL_set_accept_state(server_ssl); DEBUG(D_tls) debug_printf("Calling SSL_accept\n"); sigalrm_seen = FALSE; if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); -rc = SSL_accept(ssl); +rc = SSL_accept(server_ssl); alarm(0); if (rc <= 0) @@ -738,16 +1624,27 @@ DEBUG(D_tls) debug_printf("SSL_accept was successful\n"); /* TLS has been set up. Adjust the input functions to read via TLS, and initialize things. */ -construct_cipher_name(ssl); +construct_cipher_name(server_ssl, cipherbuf, sizeof(cipherbuf), &tls_in.bits); +tls_in.cipher = cipherbuf; DEBUG(D_tls) { uschar buf[2048]; - if (SSL_get_shared_ciphers(ssl, CS buf, sizeof(buf)) != NULL) + if (SSL_get_shared_ciphers(server_ssl, CS buf, sizeof(buf)) != NULL) debug_printf("Shared ciphers: %s\n", buf); } +/* Record the certificate we presented */ + { + X509 * crt = SSL_get_certificate(server_ssl); + tls_in.ourcert = crt ? X509_dup(crt) : NULL; + } +/* Only used by the server-side tls (tls_in), including tls_getc. + Client-side (tls_out) reads (seem to?) go via + smtp_read_response()/ip_recv(). + Hence no need to duplicate for _in and _out. + */ ssl_xfer_buffer = store_malloc(ssl_xfer_buffer_size); ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0; ssl_xfer_eof = ssl_xfer_error = 0; @@ -758,13 +1655,117 @@ receive_feof = tls_feof; receive_ferror = tls_ferror; receive_smtp_buffered = tls_smtp_buffered; -tls_active = fileno(smtp_out); +tls_in.active = fileno(smtp_out); return OK; } +static int +tls_client_basic_ctx_init(SSL_CTX * ctx, + host_item * host, smtp_transport_options_block * ob +#ifdef EXPERIMENTAL_CERTNAMES + , tls_ext_ctx_cb * cbinfo +#endif + ) +{ +int rc; +/* stick to the old behaviour for compatibility if tls_verify_certificates is + set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only + the specified host patterns if one of them is defined */ + +if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) || + (verify_check_host(&ob->tls_verify_hosts) == OK)) + { + if ((rc = setup_certs(ctx, ob->tls_verify_certificates, + ob->tls_crl, host, FALSE, verify_callback_client)) != OK) + return rc; + client_verify_optional = FALSE; + +#ifdef EXPERIMENTAL_CERTNAMES + if (ob->tls_verify_cert_hostnames) + { + if (!expand_check(ob->tls_verify_cert_hostnames, + US"tls_verify_cert_hostnames", + &cbinfo->verify_cert_hostnames)) + return FAIL; + if (cbinfo->verify_cert_hostnames) + DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", + cbinfo->verify_cert_hostnames); + } +#endif + } +else if (verify_check_host(&ob->tls_try_verify_hosts) == OK) + { + if ((rc = setup_certs(ctx, ob->tls_verify_certificates, + ob->tls_crl, host, TRUE, verify_callback_client)) != OK) + return rc; + client_verify_optional = TRUE; + } + +return OK; +} + + +#ifdef EXPERIMENTAL_DANE +static int +dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa) +{ +dns_record * rr; +dns_scan dnss; +const char * hostnames[2] = { CS host->name, NULL }; +int found = 0; + +if (DANESSL_init(ssl, NULL, hostnames) != 1) + return tls_error(US"hostnames load", host, NULL); + +for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); + rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT) + ) if (rr->type == T_TLSA) + { + uschar * p = rr->data; + uint8_t usage, selector, mtype; + const char * mdname; + + usage = *p++; + + /* Only DANE-TA(2) and DANE-EE(3) are supported */ + if (usage != 2 && usage != 3) continue; + + selector = *p++; + mtype = *p++; + + switch (mtype) + { + default: continue; /* Only match-types 0, 1, 2 are supported */ + case 0: mdname = NULL; break; + case 1: mdname = "sha256"; break; + case 2: mdname = "sha512"; break; + } + + found++; + switch (DANESSL_add_tlsa(ssl, usage, selector, mdname, p, rr->size - 3)) + { + default: + case 0: /* action not taken */ + return tls_error(US"tlsa load", host, NULL); + case 1: break; + } + + tls_out.tlsa_usage |= 1<options_block; static uschar txt[256]; -uschar *expciphers; -X509* server_cert; +uschar * expciphers; +X509 * server_cert; int rc; +static uschar cipherbuf[256]; + +#ifndef DISABLE_OCSP +BOOL request_ocsp = FALSE; +BOOL require_ocsp = FALSE; +#endif + +#ifdef EXPERIMENTAL_DANE +tls_out.tlsa_usage = 0; +#endif + +#ifndef DISABLE_OCSP + { +# ifdef EXPERIMENTAL_DANE + if ( tlsa_dnsa + && ob->hosts_request_ocsp[0] == '*' + && ob->hosts_request_ocsp[1] == '\0' + ) + { + /* Unchanged from default. Use a safer one under DANE */ + request_ocsp = TRUE; + ob->hosts_request_ocsp = US"${if or { {= {0}{$tls_out_tlsa_usage}} " + " {= {4}{$tls_out_tlsa_usage}} } " + " {*}{}}"; + } +# endif + + if ((require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp, + NULL, host->name, host->address, NULL) == OK)) + request_ocsp = TRUE; + else +# ifdef EXPERIMENTAL_DANE + if (!request_ocsp) +# endif + request_ocsp = verify_check_this_host(&ob->hosts_request_ocsp, + NULL, host->name, host->address, NULL) == OK; + } +#endif -rc = tls_init(host, dhparam, certificate, privatekey, addr); +rc = tls_init(&client_ctx, host, NULL, + ob->tls_certificate, ob->tls_privatekey, +#ifndef DISABLE_OCSP + (void *)(long)request_ocsp, +#endif + addr, &client_static_cbinfo); if (rc != OK) return rc; -tls_certificate_verified = FALSE; -verify_callback_called = FALSE; +tls_out.certificate_verified = FALSE; +client_verify_callback_called = FALSE; -if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers)) +if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers", + &expciphers)) return FAIL; /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they @@ -823,45 +1862,140 @@ if (expciphers != NULL) uschar *s = expciphers; while (*s != 0) { if (*s == '_') *s = '-'; s++; } DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers); - if (!SSL_CTX_set_cipher_list(ctx, CS expciphers)) + if (!SSL_CTX_set_cipher_list(client_ctx, CS expciphers)) return tls_error(US"SSL_CTX_set_cipher_list", host, NULL); } -rc = setup_certs(verify_certs, crl, host, FALSE); -if (rc != OK) return rc; +#ifdef EXPERIMENTAL_DANE +if (tlsa_dnsa) + { + SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER, verify_callback_client_dane); + + if (!DANESSL_library_init()) + return tls_error(US"library init", host, NULL); + if (DANESSL_CTX_init(client_ctx) <= 0) + return tls_error(US"context init", host, NULL); + } +else + +#endif + + if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob +#ifdef EXPERIMENTAL_CERTNAMES + , client_static_cbinfo +#endif + )) != OK) + return rc; -if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host, NULL); -SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx)); -SSL_set_fd(ssl, fd); -SSL_set_connect_state(ssl); +if ((client_ssl = SSL_new(client_ctx)) == NULL) + return tls_error(US"SSL_new", host, NULL); +SSL_set_session_id_context(client_ssl, sid_ctx, Ustrlen(sid_ctx)); +SSL_set_fd(client_ssl, fd); +SSL_set_connect_state(client_ssl); + +if (ob->tls_sni) + { + if (!expand_check(ob->tls_sni, US"tls_sni", &tls_out.sni)) + return FAIL; + if (tls_out.sni == NULL) + { + DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n"); + } + else if (!Ustrlen(tls_out.sni)) + tls_out.sni = NULL; + else + { +#ifdef EXIM_HAVE_OPENSSL_TLSEXT + DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_out.sni); + SSL_set_tlsext_host_name(client_ssl, tls_out.sni); +#else + DEBUG(D_tls) + debug_printf("OpenSSL at build-time lacked SNI support, ignoring \"%s\"\n", + tls_out.sni); +#endif + } + } + +#ifdef EXPERIMENTAL_DANE +if (tlsa_dnsa) + if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa)) != OK) + return rc; +#endif + +#ifndef DISABLE_OCSP +/* Request certificate status at connection-time. If the server +does OCSP stapling we will get the callback (set in tls_init()) */ +# ifdef EXPERIMENTAL_DANE +if (request_ocsp) + { + const uschar * s; + if ( ((s = ob->hosts_require_ocsp) && Ustrstr(s, US"tls_out_tlsa_usage")) + || ((s = ob->hosts_request_ocsp) && Ustrstr(s, US"tls_out_tlsa_usage")) + ) + { /* Re-eval now $tls_out_tlsa_usage is populated. If + this means we avoid the OCSP request, we wasted the setup + cost in tls_init(). */ + require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp, + NULL, host->name, host->address, NULL) == OK; + request_ocsp = require_ocsp ? TRUE + : verify_check_this_host(&ob->hosts_request_ocsp, + NULL, host->name, host->address, NULL) == OK; + } + } +# endif + +if (request_ocsp) + { + SSL_set_tlsext_status_type(client_ssl, TLSEXT_STATUSTYPE_ocsp); + client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp; + tls_out.ocsp = OCSP_NOT_RESP; + } +#endif + +#ifdef EXPERIMENTAL_EVENT +client_static_cbinfo->event_action = tb->event_action; +#endif /* There doesn't seem to be a built-in timeout on connection. */ DEBUG(D_tls) debug_printf("Calling SSL_connect\n"); sigalrm_seen = FALSE; -alarm(timeout); -rc = SSL_connect(ssl); +alarm(ob->command_timeout); +rc = SSL_connect(client_ssl); alarm(0); +#ifdef EXPERIMENTAL_DANE +if (tlsa_dnsa) + DANESSL_cleanup(client_ssl); +#endif + if (rc <= 0) return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL); DEBUG(D_tls) debug_printf("SSL_connect succeeded\n"); /* Beware anonymous ciphers which lead to server_cert being NULL */ -server_cert = SSL_get_peer_certificate (ssl); +/*XXX server_cert is never freed... use X509_free() */ +server_cert = SSL_get_peer_certificate (client_ssl); if (server_cert) { - tls_peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert), + tls_out.peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert), CS txt, sizeof(txt)); - tls_peerdn = txt; + tls_out.peerdn = txt; /*XXX a static buffer... */ } else - tls_peerdn = NULL; + tls_out.peerdn = NULL; -construct_cipher_name(ssl); /* Sets tls_cipher */ +construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits); +tls_out.cipher = cipherbuf; + +/* Record the certificate we presented */ + { + X509 * crt = SSL_get_certificate(client_ssl); + tls_out.ourcert = crt ? X509_dup(crt) : NULL; + } -tls_active = fd; +tls_out.active = fd; return OK; } @@ -878,6 +2012,8 @@ it refills the buffer via the SSL reading function. Arguments: none Returns: the next character or EOF + +Only used by the server-side TLS. */ int @@ -888,12 +2024,12 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) int error; int inbytes; - DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl, + DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl, ssl_xfer_buffer, ssl_xfer_buffer_size); if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); - inbytes = SSL_read(ssl, CS ssl_xfer_buffer, ssl_xfer_buffer_size); - error = SSL_get_error(ssl, inbytes); + inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer, ssl_xfer_buffer_size); + error = SSL_get_error(server_ssl, inbytes); alarm(0); /* SSL_ERROR_ZERO_RETURN appears to mean that the SSL session has been @@ -910,11 +2046,13 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) receive_ferror = smtp_ferror; receive_smtp_buffered = smtp_buffered; - SSL_free(ssl); - ssl = NULL; - tls_active = -1; - tls_cipher = NULL; - tls_peerdn = NULL; + SSL_free(server_ssl); + server_ssl = NULL; + tls_in.active = -1; + tls_in.bits = 0; + tls_in.cipher = NULL; + tls_in.peerdn = NULL; + tls_in.sni = NULL; return smtp_getc(); } @@ -961,11 +2099,14 @@ Arguments: Returns: the number of bytes read -1 after a failed read + +Only used by the client-side TLS. */ int -tls_read(uschar *buff, size_t len) +tls_read(BOOL is_server, uschar *buff, size_t len) { +SSL *ssl = is_server ? server_ssl : client_ssl; int inbytes; int error; @@ -998,19 +2139,23 @@ return inbytes; /* Arguments: + is_server channel specifier buff buffer of data len number of bytes Returns: the number of bytes after a successful write, -1 after a failed write + +Used by both server-side and client-side TLS. */ int -tls_write(const uschar *buff, size_t len) +tls_write(BOOL is_server, const uschar *buff, size_t len) { int outbytes; int error; int left = len; +SSL *ssl = is_server ? server_ssl : client_ssl; DEBUG(D_tls) debug_printf("tls_do_write(%p, %d)\n", buff, left); while (left > 0) @@ -1035,6 +2180,11 @@ while (left > 0) log_write(0, LOG_MAIN, "SSL channel closed on write"); return -1; + case SSL_ERROR_SYSCALL: + log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s", + sender_fullhost ? sender_fullhost : US"", + strerror(errno)); + default: log_write(0, LOG_MAIN, "SSL_write error %d", error); return -1; @@ -1055,23 +2205,94 @@ would tamper with the SSL session in the parent process). Arguments: TRUE if SSL_shutdown is to be called Returns: nothing + +Used by both server-side and client-side TLS. */ void -tls_close(BOOL shutdown) +tls_close(BOOL is_server, BOOL shutdown) { -if (tls_active < 0) return; /* TLS was not active */ +SSL **sslp = is_server ? &server_ssl : &client_ssl; +int *fdp = is_server ? &tls_in.active : &tls_out.active; + +if (*fdp < 0) return; /* TLS was not active */ if (shutdown) { DEBUG(D_tls) debug_printf("tls_close(): shutting down SSL\n"); - SSL_shutdown(ssl); + SSL_shutdown(*sslp); + } + +SSL_free(*sslp); +*sslp = NULL; + +*fdp = -1; +} + + + + +/************************************************* +* Let tls_require_ciphers be checked at startup * +*************************************************/ + +/* The tls_require_ciphers option, if set, must be something which the +library can parse. + +Returns: NULL on success, or error message +*/ + +uschar * +tls_validate_require_cipher(void) +{ +SSL_CTX *ctx; +uschar *s, *expciphers, *err; + +/* this duplicates from tls_init(), we need a better "init just global +state, for no specific purpose" singleton function of our own */ + +SSL_load_error_strings(); +OpenSSL_add_ssl_algorithms(); +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) +/* SHA256 is becoming ever more popular. This makes sure it gets added to the +list of available digests. */ +EVP_add_digest(EVP_sha256()); +#endif + +if (!(tls_require_ciphers && *tls_require_ciphers)) + return NULL; + +if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers)) + return US"failed to expand tls_require_ciphers"; + +if (!(expciphers && *expciphers)) + return NULL; + +/* normalisation ripped from above */ +s = expciphers; +while (*s != 0) { if (*s == '_') *s = '-'; s++; } + +err = NULL; + +ctx = SSL_CTX_new(SSLv23_server_method()); +if (!ctx) + { + ERR_error_string(ERR_get_error(), ssl_errstring); + return string_sprintf("SSL_CTX_new() failed: %s", ssl_errstring); } -SSL_free(ssl); -ssl = NULL; +DEBUG(D_tls) + debug_printf("tls_require_ciphers expands to \"%s\"\n", expciphers); -tls_active = -1; +if (!SSL_CTX_set_cipher_list(ctx, CS expciphers)) + { + ERR_error_string(ERR_get_error(), ssl_errstring); + err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed", expciphers); + } + +SSL_CTX_free(ctx); + +return err; } @@ -1087,6 +2308,11 @@ one version of OpenSSL but the run-time linker picks up another version, it can result in serious failures, including crashing with a SIGSEGV. So report the version found by the compiler and the run-time version. +Note: some OS vendors backport security fixes without changing the version +number/string, and the version date remains unchanged. The _build_ date +will change, so we can more usefully assist with version diagnosis by also +reporting the build date. + Arguments: a FILE* to print the results to Returns: nothing */ @@ -1095,16 +2321,20 @@ void tls_version_report(FILE *f) { fprintf(f, "Library version: OpenSSL: Compile: %s\n" - " Runtime: %s\n", + " Runtime: %s\n" + " : %s\n", OPENSSL_VERSION_TEXT, - SSLeay_version(SSLEAY_VERSION)); + SSLeay_version(SSLEAY_VERSION), + SSLeay_version(SSLEAY_BUILT_ON)); +/* third line is 38 characters for the %s and the line is 73 chars long; +the OpenSSL output includes a "built on: " prefix already. */ } /************************************************* -* Pseudo-random number generation * +* Random number generation * *************************************************/ /* Pseudo-random number generation. The result is not expected to be @@ -1119,16 +2349,30 @@ Returns a random number in range [0, max-1] */ int -pseudo_random_number(int max) +vaguely_random_number(int max) { unsigned int r; int i, needed_len; +static pid_t pidlast = 0; +pid_t pidnow; uschar *p; uschar smallbuf[sizeof(r)]; if (max <= 1) return 0; +pidnow = getpid(); +if (pidnow != pidlast) + { + /* Although OpenSSL documents that "OpenSSL makes sure that the PRNG state + is unique for each thread", this doesn't apparently apply across processes, + so our own warning from vaguely_random_number_fallback() applies here too. + Fix per PostgreSQL. */ + if (pidlast != 0) + RAND_cleanup(); + pidlast = pidnow; + } + /* OpenSSL auto-seeds from /dev/random, etc, but this a double-check. */ if (!RAND_status()) { @@ -1155,7 +2399,14 @@ if (i < needed_len) needed_len = i; /* We do not care if crypto-strong */ -(void) RAND_pseudo_bytes(smallbuf, needed_len); +i = RAND_pseudo_bytes(smallbuf, needed_len); +if (i < 0) + { + DEBUG(D_all) + debug_printf("OpenSSL RAND_pseudo_bytes() not supported by RAND method, using fallback.\n"); + return vaguely_random_number_fallback(max); + } + r = 0; for (p = smallbuf; needed_len; --needed_len, ++p) { @@ -1193,7 +2444,9 @@ all options unless explicitly for DTLS, let the administrator choose which to apply. This list is current as of: - ==> 1.0.1b <== */ + ==> 1.0.1b <== +Plus SSL_OP_SAFARI_ECDHE_ECDSA_BUG from 2013-June patch/discussion on openssl-dev +*/ static struct exim_openssl_option exim_openssl_options[] = { /* KEEP SORTED ALPHABETICALLY! */ #ifdef SSL_OP_ALL @@ -1258,6 +2511,9 @@ static struct exim_openssl_option exim_openssl_options[] = { #ifdef SSL_OP_NO_TLSv1_2 { US"no_tlsv1_2", SSL_OP_NO_TLSv1_2 }, #endif +#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG + { US"safari_ecdhe_ecdsa_bug", SSL_OP_SAFARI_ECDHE_ECDSA_BUG }, +#endif #ifdef SSL_OP_SINGLE_DH_USE { US"single_dh_use", SSL_OP_SINGLE_DH_USE }, #endif @@ -1332,9 +2588,10 @@ uschar keep_c; BOOL adding, item_parsed; result = 0L; -/* We grandfather in as default the one option which we used to set always. */ -#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS -result |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; +/* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed + * from default because it increases BEAST susceptibility. */ +#ifdef SSL_OP_NO_SSLv2 +result |= SSL_OP_NO_SSLv2; #endif if (option_spec == NULL) @@ -1378,4 +2635,6 @@ for (s=option_spec; *s != '\0'; /**/) return TRUE; } +/* vi: aw ai sw=2 +*/ /* End of tls-openssl.c */