X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4783307727d83aee3cd8d746618dce5266188e0f..afdb5e9cf07fa49e26e128d8d5d2e3cab7a5fe42:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 9816f734f..d8c8101cc 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2017 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* Portions Copyright (c) The OpenSSL Project 1999 */ @@ -28,7 +28,7 @@ functions from the OpenSSL library. */ #ifndef DISABLE_OCSP # include #endif -#ifdef EXPERIMENTAL_DANE +#ifdef SUPPORT_DANE # include "danessl.h" #endif @@ -51,7 +51,7 @@ functions from the OpenSSL library. */ # define EXIM_HAVE_RAND_PSEUDO #endif #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) -# define EXIM_HAVE_SHA256 +# define EXIM_HAVE_SHA256 /*MMMM*/ #endif /* @@ -70,6 +70,7 @@ functions from the OpenSSL library. */ # if OPENSSL_VERSION_NUMBER >= 0x010100000L # define EXIM_HAVE_OPENSSL_CHECKHOST # define EXIM_HAVE_OPENSSL_DH_BITS +# define EXIM_HAVE_OPENSSL_TLS_METHOD # endif # if OPENSSL_VERSION_NUMBER >= 0x010000000L \ && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L @@ -81,7 +82,7 @@ functions from the OpenSSL library. */ || LIBRESSL_VERSION_NUMBER >= 0x20010000L # if !defined(OPENSSL_NO_ECDH) # if OPENSSL_VERSION_NUMBER >= 0x0090800fL -# define EXIM_HAVE_ECDH +# define EXIM_HAVE_ECDH /*MMMM*/ # endif # if OPENSSL_VERSION_NUMBER >= 0x10002000L # define EXIM_HAVE_OPENSSL_EC_NIST2NID @@ -94,6 +95,10 @@ functions from the OpenSSL library. */ # define DISABLE_OCSP #endif +#ifdef EXIM_HAVE_OPENSSL_CHECKHOST +# include +#endif + /* Structure for collecting random data for seeding. */ typedef struct randstuff { @@ -112,7 +117,9 @@ static const uschar *sid_ctx = US"exim"; 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. + from the SMTP Transport. We may also concurrently want to make TLS connections + to utility daemons, so client-contexts are allocated and passed around in call + args rather than using a gobal. Server: There are two cases: with and without ServerNameIndication from the client. @@ -126,9 +133,12 @@ Server: configuration. */ -static SSL_CTX *client_ctx = NULL; +typedef struct { + SSL_CTX * ctx; + SSL * ssl; +} exim_openssl_client_tls_ctx; + static SSL_CTX *server_ctx = NULL; -static SSL *client_ssl = NULL; static SSL *server_ssl = NULL; #ifdef EXIM_HAVE_OPENSSL_TLSEXT @@ -147,8 +157,8 @@ static BOOL reexpand_tls_files_for_sni = FALSE; typedef struct tls_ext_ctx_cb { uschar *certificate; uschar *privatekey; -#ifndef DISABLE_OCSP BOOL is_server; +#ifndef DISABLE_OCSP STACK_OF(X509) *verify_stack; /* chain for verifying the proof */ union { struct { @@ -426,7 +436,7 @@ else if ( tlsp == &tls_out && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames))) - /* client, wanting hostname check */ + /* client, wanting hostname check */ { #ifdef EXIM_HAVE_OPENSSL_CHECKHOST @@ -508,7 +518,7 @@ return verify_callback(preverify_ok, x509ctx, &tls_in, } -#ifdef EXPERIMENTAL_DANE +#ifdef SUPPORT_DANE /* This gets called *by* the dane library verify callback, which interposes itself. @@ -536,8 +546,21 @@ DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n", #endif if (preverify_ok == 1) - tls_out.dane_verified = - tls_out.certificate_verified = TRUE; + { + tls_out.dane_verified = tls_out.certificate_verified = TRUE; +#ifndef DISABLE_OCSP + if (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(); + sk_X509_push(client_static_cbinfo->verify_stack, cert); + } +#endif + } else { int err = X509_STORE_CTX_get_error(x509ctx); @@ -549,7 +572,7 @@ else return preverify_ok; } -#endif /*EXPERIMENTAL_DANE*/ +#endif /*SUPPORT_DANE*/ /************************************************* @@ -887,7 +910,7 @@ We do not free the stack since it could be needed a second time for SNI handling. Separately we might try to replace using OCSP_basic_verify() - which seems to not -be a public interface into the OpenSSL library (there's no manual entry) - +be a public interface into the OpenSSL library (there's no manual entry) - But what with? We also use OCSP_basic_verify in the client stapling callback. And there we NEED it; we must verify that status... unless the library does it for us anyway? */ @@ -933,7 +956,7 @@ if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX } supply_response: - cbinfo->u_ocsp.server.response = resp; + cbinfo->u_ocsp.server.response = resp; /*XXX stack?*/ return; bad: @@ -941,7 +964,7 @@ bad: { extern char ** environ; uschar ** p; - if (environ) for (p = USS environ; *p != NULL; p++) + if (environ) for (p = USS environ; *p; p++) if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0) { DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n"); @@ -983,7 +1006,7 @@ if (!EVP_PKEY_assign_RSA(pkey, rsa)) goto err; X509_set_version(x509, 2); /* N+1 - version 3 */ -ASN1_INTEGER_set(X509_get_serialNumber(x509), 0); +ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); X509_gmtime_adj(X509_get_notBefore(x509), 0); X509_gmtime_adj(X509_get_notAfter(x509), (long)60 * 60); /* 1 hour */ X509_set_pubkey(x509, pkey); @@ -1071,7 +1094,7 @@ if (!cbinfo->certificate) { if (!cbinfo->is_server) /* client */ return OK; - /* server */ + /* server */ if (tls_install_selfsign(sctx, errstr) != OK) return DEFER; } @@ -1130,6 +1153,7 @@ else #ifndef DISABLE_OCSP if (cbinfo->is_server && cbinfo->u_ocsp.server.file) { + /*XXX stack*/ if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded, errstr)) return DEFER; @@ -1198,7 +1222,11 @@ if (!reexpand_tls_files_for_sni) not confident that memcpy wouldn't break some internal reference counting. Especially since there's a references struct member, which would be off. */ +#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD +if (!(server_sni = SSL_CTX_new(TLS_server_method()))) +#else if (!(server_sni = SSL_CTX_new(SSLv23_server_method()))) +#endif { ERR_error_string(ERR_get_error(), ssl_errstring); DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring); @@ -1267,9 +1295,15 @@ static int tls_server_stapling_cb(SSL *s, void *arg) { const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg; -uschar *response_der; +uschar *response_der; /*XXX blob */ int response_der_len; +/*XXX stack: use SSL_get_certificate() to see which cert; from that work +out which ocsp blob to send. Unfortunately, SSL_get_certificate is known +buggy in current OpenSSL; it returns the last cert loaded always rather than +the one actually presented. So we can't support a stack of OCSP proofs at +this time. */ + DEBUG(D_tls) debug_printf("Received TLS status request (OCSP stapling); %s response\n", cbinfo->u_ocsp.server.response ? "have" : "lack"); @@ -1279,7 +1313,7 @@ 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_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response, /*XXX stack*/ &response_der); if (response_der_len <= 0) return SSL_TLSEXT_ERR_NOACK; @@ -1353,7 +1387,7 @@ if(!(bs = OCSP_response_get1_basic(rsp))) int status, reason; ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; - DEBUG(D_tls) bp = BIO_new_fp(stderr, BIO_NOCLOSE); + DEBUG(D_tls) bp = BIO_new_fp(debug_file, BIO_NOCLOSE); /*OCSP_RESPONSE_print(bp, rsp, 0); extreme debug: stapling content */ @@ -1364,10 +1398,12 @@ if(!(bs = OCSP_response_get1_basic(rsp))) cbinfo->u_ocsp.client.verify_store, 0)) <= 0) { tls_out.ocsp = OCSP_FAILED; - if (LOGGING(tls_cipher)) - log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable"); + if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN, + "Received TLS cert status response, itself unverifiable: %s", + ERR_reason_error_string(ERR_peek_error())); BIO_printf(bp, "OCSP response verify failure\n"); ERR_print_errors(bp); + OCSP_RESPONSE_print(bp, rsp, 0); goto failed; } @@ -1471,7 +1507,7 @@ static int tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate, uschar *privatekey, #ifndef DISABLE_OCSP - uschar *ocsp_file, + uschar *ocsp_file, /*XXX stack, in server*/ #endif address_item *addr, tls_ext_ctx_cb ** cbp, uschar ** errstr) { @@ -1483,9 +1519,10 @@ tls_ext_ctx_cb * cbinfo; cbinfo = store_malloc(sizeof(tls_ext_ctx_cb)); cbinfo->certificate = certificate; cbinfo->privatekey = privatekey; +cbinfo->is_server = host==NULL; #ifndef DISABLE_OCSP cbinfo->verify_stack = NULL; -if ((cbinfo->is_server = host==NULL)) +if (!host) { cbinfo->u_ocsp.server.file = ocsp_file; cbinfo->u_ocsp.server.file_expanded = NULL; @@ -1518,7 +1555,11 @@ when OpenSSL is built without SSLv2 support. By disabling with openssl_options, we can let admins re-enable with the existing knob. */ +#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD +if (!(ctx = SSL_CTX_new(host ? TLS_client_method() : TLS_server_method()))) +#else if (!(ctx = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method()))) +#endif return tls_error(US"SSL_CTX_new", host, NULL, errstr); /* It turns out that we need to seed the random number generator this early in @@ -1574,9 +1615,15 @@ if (init_options) else DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); -/* Disable session cache unconditionally */ - +/* We'd like to disable session cache unconditionally, but foolish Outlook +Express clients then give up the first TLS connection and make a second one +(which works). Only when there is an IMAP service on the same machine. +Presumably OE is trying to use the cache for A on B. Leave it enabled for +now, until we work out a decent way of presenting control to the config. It +will never be used because we use a new context every time. */ +#ifdef notdef (void) SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); +#endif /* Initialize with DH parameters if supplied */ /* Initialize ECDH temp key parameter selection */ @@ -1602,7 +1649,7 @@ if ((rc = tls_expand_session_files(ctx, cbinfo, errstr)) != OK) } # endif -if (host == NULL) /* server */ +if (!host) /* server */ { # ifndef DISABLE_OCSP /* We check u_ocsp.server.file, not server.response, because we care about if @@ -1671,15 +1718,13 @@ Returns: nothing static void construct_cipher_name(SSL *ssl, uschar *cipherbuf, int bsize, int *bits) { -/* With OpenSSL 1.0.0a, this needs to be const but the documentation doesn't +/* With OpenSSL 1.0.0a, 'c' 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; -const uschar *ver; -ver = (const uschar *)SSL_get_version(ssl); +const uschar * ver = CUS SSL_get_version(ssl); +const SSL_CIPHER * c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl); -c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl); SSL_CIPHER_get_bits(c, bits); string_format(cipherbuf, bsize, "%s:%s:%u", ver, @@ -1719,6 +1764,7 @@ else * Set up for verifying certificates * *************************************************/ +#ifndef DISABLE_OCSP /* Load certs from file, return TRUE on success */ static BOOL @@ -1727,16 +1773,21 @@ chain_from_pem_file(const uschar * file, STACK_OF(X509) * verify_stack) BIO * bp; X509 * x; +while (sk_X509_num(verify_stack) > 0) + X509_free(sk_X509_pop(verify_stack)); + if (!(bp = BIO_new_file(CS file, "r"))) return FALSE; while ((x = PEM_read_bio_X509(bp, NULL, 0, NULL))) sk_X509_push(verify_stack, x); BIO_free(bp); return TRUE; } +#endif -/* Called by both client and server startup +/* Called by both client and server startup; on the server possibly +repeated after a Server Name Indication. Arguments: sctx SSL_CTX* to initialise @@ -1798,7 +1849,7 @@ if (expcerts && *expcerts) ) { log_write(0, LOG_MAIN|LOG_PANIC, - "failed to load cert hain from %s", file); + "failed to load cert chain from %s", file); return DEFER; } #endif @@ -1828,9 +1879,9 @@ if (expcerts && *expcerts) { STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file); + SSL_CTX_set_client_CA_list(sctx, names); DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", sk_X509_NAME_num(names)); - SSL_CTX_set_client_CA_list(sctx, names); } } } @@ -1929,7 +1980,7 @@ static uschar cipherbuf[256]; /* Check for previous activation */ -if (tls_in.active >= 0) +if (tls_in.active.sock >= 0) { tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr); smtp_printf("554 Already in TLS\r\n", FALSE); @@ -1941,7 +1992,7 @@ the error. */ rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey, #ifndef DISABLE_OCSP - tls_ocsp_file, + tls_ocsp_file, /*XXX stack*/ #endif NULL, &server_static_cbinfo, errstr); if (rc != OK) return rc; @@ -1953,6 +2004,10 @@ if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers, errstr) /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they 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. + +XXX SSL_CTX_set_cipher_list() is replaced by SSL_CTX_set_ciphersuites() +for TLS 1.3 . Since we do not call it at present we get the default list: +TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256 */ if (expciphers) @@ -1969,7 +2024,7 @@ if (expciphers) optional, set up appropriately. */ tls_in.certificate_verified = FALSE; -#ifdef EXPERIMENTAL_DANE +#ifdef SUPPORT_DANE tls_in.dane_verified = FALSE; #endif server_verify_callback_called = FALSE; @@ -1977,14 +2032,14 @@ server_verify_callback_called = FALSE; if (verify_check_host(&tls_verify_hosts) == OK) { rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL, - FALSE, verify_callback_server, errstr); + FALSE, verify_callback_server, errstr); if (rc != OK) return rc; server_verify_optional = FALSE; } else if (verify_check_host(&tls_try_verify_hosts) == OK) { rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL, - TRUE, verify_callback_server, errstr); + TRUE, verify_callback_server, errstr); if (rc != OK) return rc; server_verify_optional = TRUE; } @@ -2068,9 +2123,9 @@ DEBUG(D_tls) smtp_read_response()/ip_recv(). Hence no need to duplicate for _in and _out. */ -ssl_xfer_buffer = store_malloc(ssl_xfer_buffer_size); +if (!ssl_xfer_buffer) 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; +ssl_xfer_eof = ssl_xfer_error = FALSE; receive_getc = tls_getc; receive_getbuf = tls_getbuf; @@ -2080,7 +2135,8 @@ receive_feof = tls_feof; receive_ferror = tls_ferror; receive_smtp_buffered = tls_smtp_buffered; -tls_in.active = fileno(smtp_out); +tls_in.active.sock = fileno(smtp_out); +tls_in.active.tls_ctx = NULL; /* not using explicit ctx for server-side */ return OK; } @@ -2128,7 +2184,7 @@ return OK; } -#ifdef EXPERIMENTAL_DANE +#ifdef SUPPORT_DANE static int dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa, uschar ** errstr) { @@ -2183,7 +2239,7 @@ if (found) log_write(0, LOG_MAIN, "DANE error: No usable TLSA records"); return DEFER; } -#endif /*EXPERIMENTAL_DANE*/ +#endif /*SUPPORT_DANE*/ @@ -2195,27 +2251,28 @@ return DEFER; Argument: fd the fd of the connection - host connected host (for messages) - addr the first address + host connected host (for messages and option-tests) + addr the first address (for some randomness; can be NULL) tb transport (always smtp) tlsa_dnsa tlsa lookup, if DANE, else null + tlsp record details of channel configuration here; must be non-NULL errstr error string pointer -Returns: OK on success - FAIL otherwise - note that tls_error() will not give DEFER - because this is not a server +Returns: Pointer to TLS session context, or NULL on error */ -int +void * tls_client_start(int fd, host_item *host, address_item *addr, transport_instance * tb, -#ifdef EXPERIMENTAL_DANE +#ifdef SUPPORT_DANE dns_answer * tlsa_dnsa, #endif - uschar ** errstr) + tls_support * tlsp, uschar ** errstr) { -smtp_transport_options_block * ob = - (smtp_transport_options_block *)tb->options_block; +smtp_transport_options_block * ob = tb + ? (smtp_transport_options_block *)tb->options_block + : &smtp_transport_option_defaults; +exim_openssl_client_tls_ctx * exim_client_ctx; static uschar peerdn[256]; uschar * expciphers; int rc; @@ -2226,13 +2283,18 @@ BOOL request_ocsp = FALSE; BOOL require_ocsp = FALSE; #endif -#ifdef EXPERIMENTAL_DANE -tls_out.tlsa_usage = 0; +rc = store_pool; +store_pool = POOL_PERM; +exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx)); +store_pool = rc; + +#ifdef SUPPORT_DANE +tlsp->tlsa_usage = 0; #endif #ifndef DISABLE_OCSP { -# ifdef EXPERIMENTAL_DANE +# ifdef SUPPORT_DANE if ( tlsa_dnsa && ob->hosts_request_ocsp[0] == '*' && ob->hosts_request_ocsp[1] == '\0' @@ -2250,7 +2312,7 @@ tls_out.tlsa_usage = 0; verify_check_given_host(&ob->hosts_require_ocsp, host) == OK)) request_ocsp = TRUE; else -# ifdef EXPERIMENTAL_DANE +# ifdef SUPPORT_DANE if (!request_ocsp) # endif request_ocsp = @@ -2258,20 +2320,35 @@ tls_out.tlsa_usage = 0; } #endif -rc = tls_init(&client_ctx, host, NULL, +rc = tls_init(&exim_client_ctx->ctx, host, NULL, ob->tls_certificate, ob->tls_privatekey, #ifndef DISABLE_OCSP (void *)(long)request_ocsp, #endif addr, &client_static_cbinfo, errstr); -if (rc != OK) return rc; +if (rc != OK) return NULL; -tls_out.certificate_verified = FALSE; +tlsp->certificate_verified = FALSE; client_verify_callback_called = FALSE; -if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers", - &expciphers, errstr)) - return FAIL; +expciphers = NULL; +#ifdef SUPPORT_DANE +if (tlsa_dnsa) + { + /* We fall back to tls_require_ciphers if unset, empty or forced failure, but + other failures should be treated as problems. */ + if (ob->dane_require_tls_ciphers && + !expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers", + &expciphers, errstr)) + return NULL; + if (expciphers && *expciphers == '\0') + expciphers = NULL; + } +#endif +if (!expciphers && + !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers", + &expciphers, errstr)) + return NULL; /* 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 @@ -2282,68 +2359,80 @@ if (expciphers) uschar *s = expciphers; while (*s) { if (*s == '_') *s = '-'; s++; } DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers); - if (!SSL_CTX_set_cipher_list(client_ctx, CS expciphers)) - return tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr); + if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers)) + { + tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr); + return NULL; + } } -#ifdef EXPERIMENTAL_DANE +#ifdef SUPPORT_DANE if (tlsa_dnsa) { - SSL_CTX_set_verify(client_ctx, + SSL_CTX_set_verify(exim_client_ctx->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback_client_dane); if (!DANESSL_library_init()) - return tls_error(US"library init", host, NULL, errstr); - if (DANESSL_CTX_init(client_ctx) <= 0) - return tls_error(US"context init", host, NULL, errstr); + { + tls_error(US"library init", host, NULL, errstr); + return NULL; + } + if (DANESSL_CTX_init(exim_client_ctx->ctx) <= 0) + { + tls_error(US"context init", host, NULL, errstr); + return NULL; + } } else #endif - if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob, - client_static_cbinfo, errstr)) != OK) - return rc; + if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob, + client_static_cbinfo, errstr) != OK) + return NULL; -if ((client_ssl = SSL_new(client_ctx)) == NULL) - return tls_error(US"SSL_new", host, NULL, errstr); -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 (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx))) + { + tls_error(US"SSL_new", host, NULL, errstr); + return NULL; + } +SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx)); +SSL_set_fd(exim_client_ctx->ssl, fd); +SSL_set_connect_state(exim_client_ctx->ssl); if (ob->tls_sni) { - if (!expand_check(ob->tls_sni, US"tls_sni", &tls_out.sni, errstr)) - return FAIL; - if (!tls_out.sni) + if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr)) + return NULL; + if (!tlsp->sni) { 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 if (!Ustrlen(tlsp->sni)) + tlsp->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); + DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni); + SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni); #else log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n", - tls_out.sni); + tlsp->sni); #endif } } -#ifdef EXPERIMENTAL_DANE +#ifdef SUPPORT_DANE if (tlsa_dnsa) - if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa, errstr)) != OK) - return rc; + if (dane_tlsa_load(exim_client_ctx->ssl, host, tlsa_dnsa, errstr) != OK) + return NULL; #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 +# ifdef SUPPORT_DANE if (request_ocsp) { const uschar * s; @@ -2362,14 +2451,14 @@ if (request_ocsp) if (request_ocsp) { - SSL_set_tlsext_status_type(client_ssl, TLSEXT_STATUSTYPE_ocsp); + SSL_set_tlsext_status_type(exim_client_ctx->ssl, TLSEXT_STATUSTYPE_ocsp); client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp; - tls_out.ocsp = OCSP_NOT_RESP; + tlsp->ocsp = OCSP_NOT_RESP; } #endif #ifndef DISABLE_EVENT -client_static_cbinfo->event_action = tb->event_action; +client_static_cbinfo->event_action = tb ? tb->event_action : NULL; #endif /* There doesn't seem to be a built-in timeout on connection. */ @@ -2377,33 +2466,36 @@ client_static_cbinfo->event_action = tb->event_action; DEBUG(D_tls) debug_printf("Calling SSL_connect\n"); sigalrm_seen = FALSE; alarm(ob->command_timeout); -rc = SSL_connect(client_ssl); +rc = SSL_connect(exim_client_ctx->ssl); alarm(0); -#ifdef EXPERIMENTAL_DANE +#ifdef SUPPORT_DANE if (tlsa_dnsa) - DANESSL_cleanup(client_ssl); + DANESSL_cleanup(exim_client_ctx->ssl); #endif if (rc <= 0) - return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, - errstr); + { + tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr); + return NULL; + } DEBUG(D_tls) debug_printf("SSL_connect succeeded\n"); -peer_cert(client_ssl, &tls_out, peerdn, sizeof(peerdn)); +peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn)); -construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits); -tls_out.cipher = cipherbuf; +construct_cipher_name(exim_client_ctx->ssl, cipherbuf, sizeof(cipherbuf), &tlsp->bits); +tlsp->cipher = cipherbuf; /* Record the certificate we presented */ { - X509 * crt = SSL_get_certificate(client_ssl); - tls_out.ourcert = crt ? X509_dup(crt) : NULL; + X509 * crt = SSL_get_certificate(exim_client_ctx->ssl); + tlsp->ourcert = crt ? X509_dup(crt) : NULL; } -tls_out.active = fd; -return OK; +tlsp->active.sock = fd; +tlsp->active.tls_ctx = exim_client_ctx; +return exim_client_ctx; } @@ -2423,50 +2515,70 @@ if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer, MIN(ssl_xfer_buffer_size, lim)); error = SSL_get_error(server_ssl, inbytes); -alarm(0); +if (smtp_receive_timeout > 0) alarm(0); + +if (had_command_timeout) /* set by signal handler */ + smtp_command_timeout_exit(); /* does not return */ +if (had_command_sigterm) + smtp_command_sigterm_exit(); +if (had_data_timeout) + smtp_data_timeout_exit(); +if (had_data_sigint) + smtp_data_sigint_exit(); /* SSL_ERROR_ZERO_RETURN appears to mean that the SSL session has been closed down, not that the socket itself has been closed down. Revert to non-SSL handling. */ -if (error == SSL_ERROR_ZERO_RETURN) +switch(error) { - DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n"); + case SSL_ERROR_NONE: + break; - receive_getc = smtp_getc; - receive_getbuf = smtp_getbuf; - receive_get_cache = smtp_get_cache; - receive_ungetc = smtp_ungetc; - receive_feof = smtp_feof; - receive_ferror = smtp_ferror; - receive_smtp_buffered = smtp_buffered; - - 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; + case SSL_ERROR_ZERO_RETURN: + DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n"); - return FALSE; - } + receive_getc = smtp_getc; + receive_getbuf = smtp_getbuf; + receive_get_cache = smtp_get_cache; + receive_ungetc = smtp_ungetc; + receive_feof = smtp_feof; + receive_ferror = smtp_ferror; + receive_smtp_buffered = smtp_buffered; -/* Handle genuine errors */ + if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN) + SSL_shutdown(server_ssl); -else if (error == SSL_ERROR_SSL) - { - ERR_error_string(ERR_get_error(), ssl_errstring); - log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring); - ssl_xfer_error = 1; - return FALSE; - } +#ifndef DISABLE_OCSP + sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free); + server_static_cbinfo->verify_stack = NULL; +#endif + SSL_free(server_ssl); + SSL_CTX_free(server_ctx); + server_ctx = NULL; + server_ssl = NULL; + tls_in.active.sock = -1; + tls_in.active.tls_ctx = NULL; + tls_in.bits = 0; + tls_in.cipher = NULL; + tls_in.peerdn = NULL; + tls_in.sni = NULL; -else if (error != SSL_ERROR_NONE) - { - DEBUG(D_tls) debug_printf("Got SSL error %d\n", error); - ssl_xfer_error = 1; - return FALSE; + return FALSE; + + /* Handle genuine errors */ + case SSL_ERROR_SSL: + ERR_error_string(ERR_get_error(), ssl_errstring); + log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring); + ssl_xfer_error = TRUE; + return FALSE; + + default: + DEBUG(D_tls) debug_printf("Got SSL error %d\n", error); + DEBUG(D_tls) if (error == SSL_ERROR_SYSCALL) + debug_printf(" - syscall %s\n", strerror(errno)); + ssl_xfer_error = TRUE; + return FALSE; } #ifndef DISABLE_DKIM @@ -2550,19 +2662,20 @@ return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm || SSL_pending(server_ssl) > 0; /* Arguments: + ct_ctx client context pointer, or NULL for the one global server context buff buffer of data len size of buffer Returns: the number of bytes read - -1 after a failed read + -1 after a failed read, including EOF Only used by the client-side TLS. */ int -tls_read(BOOL is_server, uschar *buff, size_t len) +tls_read(void * ct_ctx, uschar *buff, size_t len) { -SSL *ssl = is_server ? server_ssl : client_ssl; +SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl; int inbytes; int error; @@ -2593,7 +2706,7 @@ return inbytes; /* Arguments: - is_server channel specifier + ct_ctx client context pointer, or NULL for the one global server context buff buffer of data len number of bytes more further data expected soon @@ -2605,10 +2718,10 @@ Used by both server-side and client-side TLS. */ int -tls_write(BOOL is_server, const uschar *buff, size_t len, BOOL more) +tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more) { int outbytes, error, left; -SSL *ssl = is_server ? server_ssl : client_ssl; +SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl; static gstring * corked = NULL; DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__, @@ -2619,7 +2732,7 @@ DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__, one stream does it, in one context (i.e. no store reset). Currently it is used for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */ -if (is_server && (more || corked)) +if (!ct_ctx && (more || corked)) { corked = string_catn(corked, buff, len); if (more) @@ -2631,7 +2744,7 @@ if (is_server && (more || corked)) for (left = len; left > 0;) { - DEBUG(D_tls) debug_printf("SSL_write(SSL, %p, %d)\n", buff, left); + DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left); outbytes = SSL_write(ssl, CS buff, left); error = SSL_get_error(ssl, outbytes); DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error); @@ -2675,29 +2788,59 @@ return len; daemon, to shut down the TLS library, without actually doing a shutdown (which would tamper with the SSL session in the parent process). -Arguments: TRUE if SSL_shutdown is to be called +Arguments: + ct_ctx client TLS context pointer, or NULL for the one global server context + shutdown 1 if TLS close-alert is to be sent, + 2 if also response to be waited for + Returns: nothing Used by both server-side and client-side TLS. */ void -tls_close(BOOL is_server, BOOL shutdown) +tls_close(void * ct_ctx, int shutdown) { -SSL **sslp = is_server ? &server_ssl : &client_ssl; -int *fdp = is_server ? &tls_in.active : &tls_out.active; +exim_openssl_client_tls_ctx * o_ctx = ct_ctx; +SSL_CTX **ctxp = o_ctx ? &o_ctx->ctx : &server_ctx; +SSL **sslp = o_ctx ? &o_ctx->ssl : &server_ssl; +int *fdp = o_ctx ? &tls_out.active.sock : &tls_in.active.sock; if (*fdp < 0) return; /* TLS was not active */ if (shutdown) { - DEBUG(D_tls) debug_printf("tls_close(): shutting down SSL\n"); - SSL_shutdown(*sslp); + int rc; + DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n", + shutdown > 1 ? " (with response-wait)" : ""); + + if ( (rc = SSL_shutdown(*sslp)) == 0 /* send "close notify" alert */ + && shutdown > 1) + { + alarm(2); + rc = SSL_shutdown(*sslp); /* wait for response */ + alarm(0); + } + + if (rc < 0) DEBUG(D_tls) + { + ERR_error_string(ERR_get_error(), ssl_errstring); + debug_printf("SSL_shutdown: %s\n", ssl_errstring); + } + } + +#ifndef DISABLE_OCSP +if (!o_ctx) /* server side */ + { + sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free); + server_static_cbinfo->verify_stack = NULL; } +#endif +SSL_CTX_free(*ctxp); SSL_free(*sslp); +*ctxp = NULL; *sslp = NULL; - *fdp = -1; } @@ -2747,8 +2890,11 @@ while (*s != 0) { if (*s == '_') *s = '-'; s++; } err = NULL; -ctx = SSL_CTX_new(SSLv23_server_method()); -if (!ctx) +#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD +if (!(ctx = SSL_CTX_new(TLS_server_method()))) +#else +if (!(ctx = SSL_CTX_new(SSLv23_server_method()))) +#endif { ERR_error_string(ERR_get_error(), ssl_errstring); return string_sprintf("SSL_CTX_new() failed: %s", ssl_errstring);