X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/63f0dbe0ca0aba7be3bc8807f45c8703a1cfafe1..2333e06f406b5d66068cef3e20ab223fc6650307:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index b1dccb8ee..39c5e1f56 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 - 2015 */ +/* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ /* Portions Copyright (c) The OpenSSL Project 1999 */ @@ -41,6 +41,18 @@ functions from the OpenSSL library. */ #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) # define EXIM_HAVE_OPENSSL_TLSEXT #endif +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +# define EXIM_HAVE_RSA_GENKEY_EX +#endif +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +# define EXIM_HAVE_OCSP_RESP_COUNT +#else +# define EXIM_HAVE_EPHEM_RSA_KEX +# define EXIM_HAVE_RAND_PSEUDO +#endif +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) +# define EXIM_HAVE_SHA256 +#endif /* * X509_check_host provides sane certificate hostname checking, but was added @@ -62,13 +74,18 @@ functions from the OpenSSL library. */ && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L # define EXIM_HAVE_OPENSSL_CHECKHOST # endif +#endif +#if !defined(LIBRESSL_VERSION_NUMBER) \ + || LIBRESSL_VERSION_NUMBER >= 0x20010000L # if !defined(OPENSSL_NO_ECDH) # if OPENSSL_VERSION_NUMBER >= 0x0090800fL # define EXIM_HAVE_ECDH # endif # if OPENSSL_VERSION_NUMBER >= 0x10002000L -# define EXIM_HAVE_OPENSSL_ECDH_AUTO +# if OPENSSL_VERSION_NUMBER < 0x10100000L +# define EXIM_HAVE_OPENSSL_ECDH_AUTO +# endif # define EXIM_HAVE_OPENSSL_EC_NIST2NID # endif # endif @@ -142,6 +159,7 @@ typedef struct tls_ext_ctx_cb { } server; struct { X509_STORE *verify_store; /* non-null if status requested */ + STACK_OF(X509) *verify_stack; BOOL verify_required; } client; } u_ocsp; @@ -152,7 +170,7 @@ typedef struct tls_ext_ctx_cb { /* only passed down to tls_error: */ host_item *host; const uschar * verify_cert_hostnames; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT uschar * event_action; #endif } tls_ext_ctx_cb; @@ -197,7 +215,7 @@ Returns: OK/DEFER/FAIL */ static int -tls_error(uschar * prefix, const host_item * host, uschar * msg) +tls_error(uschar * prefix, const host_item * host, uschar * msg) { if (!msg) { @@ -225,6 +243,7 @@ else +#ifdef EXIM_HAVE_EPHEM_RSA_KEX /************************************************* * Callback to generate RSA key * *************************************************/ @@ -242,10 +261,22 @@ static RSA * rsa_callback(SSL *s, int export, int keylength) { RSA *rsa_key; +#ifdef EXIM_HAVE_RSA_GENKEY_EX +BIGNUM *bn = BN_new(); +#endif + export = export; /* Shut picky compilers up */ DEBUG(D_tls) debug_printf("Generating %d bit RSA key...\n", keylength); -rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL); -if (rsa_key == NULL) + +#ifdef EXIM_HAVE_RSA_GENKEY_EX +if ( !BN_set_word(bn, (unsigned long)RSA_F4) + || !(rsa_key = RSA_new()) + || !RSA_generate_key_ex(rsa_key, keylength, bn, NULL) + ) +#else +if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL))) +#endif + { ERR_error_string(ERR_get_error(), ssl_errstring); log_write(0, LOG_MAIN|LOG_PANIC, "TLS error (RSA_generate_key): %s", @@ -254,6 +285,7 @@ if (rsa_key == NULL) } return rsa_key; } +#endif @@ -282,7 +314,7 @@ for(i= 0; ievent_action : event_action; if (ev) { + DEBUG(D_tls) debug_printf("verify_event: %s %d\n", what, depth); old_cert = tlsp->peercert; tlsp->peercert = X509_dup(cert); /* NB we do not bother setting peerdn */ @@ -344,15 +377,17 @@ 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 + preverify_ok current yes/no state as 1/0 + x509ctx certificate information. + tlsp per-direction (client vs. server) support data + calledp has-been-called flag + optionalp verification-is-optional flag -Returns: 1 if verified, 0 if not +Returns: 0 if verification should fail, otherwise 1 */ static int -verify_callback(int state, X509_STORE_CTX *x509ctx, +verify_callback(int preverify_ok, X509_STORE_CTX *x509ctx, tls_support *tlsp, BOOL *calledp, BOOL *optionalp) { X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); @@ -362,7 +397,7 @@ uschar dn[256]; X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)); dn[sizeof(dn)-1] = '\0'; -if (state == 0) +if (preverify_ok == 0) { log_write(0, LOG_MAIN, "[%s] SSL verify error: depth=%d error=%s cert=%s", tlsp == &tls_out ? deliver_host_address : sender_host_address, @@ -388,13 +423,14 @@ else if (depth != 0) { /* 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->u_ocsp.client.verify_stack, cert); } #endif -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL")) return 0; /* reject, with peercert set */ #endif @@ -420,7 +456,7 @@ else uschar * name; int rc; while ((name = string_nextinlist(&list, &sep, NULL, 0))) - if ((rc = X509_check_host(cert, name, 0, + if ((rc = X509_check_host(cert, CCS name, 0, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS, NULL))) @@ -454,7 +490,7 @@ else } } -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL")) return 0; /* reject, with peercert set */ #endif @@ -469,15 +505,17 @@ return 1; /* accept, at least for this level */ } static int -verify_callback_client(int state, X509_STORE_CTX *x509ctx) +verify_callback_client(int preverify_ok, X509_STORE_CTX *x509ctx) { -return verify_callback(state, x509ctx, &tls_out, &client_verify_callback_called, &client_verify_optional); +return verify_callback(preverify_ok, x509ctx, &tls_out, + &client_verify_callback_called, &client_verify_optional); } static int -verify_callback_server(int state, X509_STORE_CTX *x509ctx) +verify_callback_server(int preverify_ok, X509_STORE_CTX *x509ctx) { -return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called, &server_verify_optional); +return verify_callback(preverify_ok, x509ctx, &tls_in, + &server_verify_callback_called, &server_verify_optional); } @@ -487,11 +525,11 @@ return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called, itself. */ static int -verify_callback_client_dane(int state, X509_STORE_CTX * x509ctx) +verify_callback_client_dane(int preverify_ok, X509_STORE_CTX * x509ctx) { X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); uschar dn[256]; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT int depth = X509_STORE_CTX_get_error_depth(x509ctx); BOOL dummy_called, optional = FALSE; #endif @@ -499,18 +537,27 @@ BOOL dummy_called, optional = FALSE; X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)); dn[sizeof(dn)-1] = '\0'; -DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", dn); +DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n", + preverify_ok ? "ok":"BAD", depth, dn); -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT if (verify_event(&tls_out, cert, depth, dn, &dummy_called, &optional, US"DANE")) return 0; /* reject, with peercert set */ #endif -if (state == 1) +if (preverify_ok == 1) tls_out.dane_verified = tls_out.certificate_verified = TRUE; -return 1; +else + { + int err = X509_STORE_CTX_get_error(x509ctx); + DEBUG(D_tls) + debug_printf(" - err %d '%s'\n", err, X509_verify_cert_error_string(err)); + if (err == X509_V_ERR_APPLICATION_VERIFICATION) + preverify_ok = 1; + } +return preverify_ok; } #endif /*EXPERIMENTAL_DANE*/ @@ -735,6 +782,34 @@ return !rv; * Load OCSP information into state * *************************************************/ +static STACK_OF(X509) * +cert_stack_from_store(X509_STORE * store) +{ +STACK_OF(X509_OBJECT) * roots= store->objs; +STACK_OF(X509) * sk = sk_X509_new_null(); +int i; + +for(i = sk_X509_OBJECT_num(roots) - 1; i >= 0; i--) + { + X509_OBJECT * tmp_obj= sk_X509_OBJECT_value(roots, i); + if(tmp_obj->type == X509_LU_X509) + { + X509 * x = tmp_obj->data.x509; + sk_X509_push(sk, x); + } + } +return sk; +} + +static void +cert_stack_free(STACK_OF(X509) * sk) +{ +while (sk_X509_num(sk) > 0) (void) sk_X509_pop(sk); +sk_X509_free(sk); +} + + + /* 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. @@ -751,12 +826,13 @@ Arguments: 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; +BIO * bio; +OCSP_RESPONSE * resp; +OCSP_BASICRESP * basic_response; +OCSP_SINGLERESP * single_response; +ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd; +X509_STORE * store; +STACK_OF(X509) * sk; unsigned long verify_flags; int status, reason, i; @@ -767,8 +843,7 @@ if (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) +if (!(bio = BIO_new_file(CS cbinfo->u_ocsp.server.file_expanded, "rb"))) { DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n", cbinfo->u_ocsp.server.file_expanded); @@ -783,16 +858,14 @@ if (!resp) return; } -status = OCSP_response_status(resp); -if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) +if ((status = OCSP_response_status(resp)) != 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) +if (!(basic_response = OCSP_response_get1_basic(resp))) { DEBUG(D_tls) debug_printf("OCSP response parse error: unable to extract basic response.\n"); @@ -800,21 +873,38 @@ if (!basic_response) } store = SSL_CTX_get_cert_store(sctx); +sk = cert_stack_from_store(store); 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) +/* This does a full verify on the OCSP proof before we load it for serviing +up; possibly overkill - just date-checks might be nice enough. + +OCSP_basic_verify takes a "store" arg, but does not +use it for the chain verification, which is all we do +when OCSP_NOVERIFY is set. The content from the wire +"basic_response" and a cert-stack "sk" are all that is used. + +Seperately 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) - +But what with? We also use OCSP_basic_verify in the client stapling callback. +And there we NEED it; we miust verify that status... unless the +library does it for us anyway? */ + +if ((i = OCSP_basic_verify(basic_response, sk, NULL, verify_flags)) < 0) { - DEBUG(D_tls) { + DEBUG(D_tls) + { ERR_error_string(ERR_get_error(), ssl_errstring); debug_printf("OCSP response verify failure: %s\n", US ssl_errstring); } + cert_stack_free(sk); goto bad; } +cert_stack_free(sk); /* 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 @@ -823,8 +913,8 @@ proves false, we need to extract a cert id from our issued cert 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) + +if (!(single_response = OCSP_resp_get0(basic_response, 0))) { DEBUG(D_tls) debug_printf("Unable to get first response from OCSP basic response.\n"); @@ -855,7 +945,7 @@ bad: { extern char ** environ; uschar ** p; - for (p = USS environ; *p != NULL; p++) + if (environ) 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"); @@ -869,6 +959,73 @@ return; +/* Create and install a selfsigned certificate, for use in server mode */ + +static int +tls_install_selfsign(SSL_CTX * sctx) +{ +X509 * x509 = NULL; +EVP_PKEY * pkey; +RSA * rsa; +X509_NAME * name; +uschar * where; + +where = US"allocating pkey"; +if (!(pkey = EVP_PKEY_new())) + goto err; + +where = US"allocating cert"; +if (!(x509 = X509_new())) + goto err; + +where = US"generating pkey"; + /* deprecated, use RSA_generate_key_ex() */ +if (!(rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL))) + goto err; + +where = US"assiging pkey"; +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); +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); + +name = X509_get_subject_name(x509); +X509_NAME_add_entry_by_txt(name, "C", + MBSTRING_ASC, CUS "UK", -1, -1, 0); +X509_NAME_add_entry_by_txt(name, "O", + MBSTRING_ASC, CUS "Exim Developers", -1, -1, 0); +X509_NAME_add_entry_by_txt(name, "CN", + MBSTRING_ASC, CUS smtp_active_hostname, -1, -1, 0); +X509_set_issuer_name(x509, name); + +where = US"signing cert"; +if (!X509_sign(x509, pkey, EVP_md5())) + goto err; + +where = US"installing selfsign cert"; +if (!SSL_CTX_use_certificate(sctx, x509)) + goto err; + +where = US"installing selfsign key"; +if (!SSL_CTX_use_PrivateKey(sctx, pkey)) + goto err; + +return OK; + +err: + (void) tls_error(where, NULL, NULL); + if (x509) X509_free(x509); + if (pkey) EVP_PKEY_free(pkey); + return DEFER; +} + + + + /************************************************* * Expand key and cert file specs * *************************************************/ @@ -889,59 +1046,68 @@ 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) +if (!cbinfo->certificate) { - 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->host) /* client */ + return OK; + /* server */ + if (tls_install_selfsign(sctx) != OK) + return DEFER; } +else + { + 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 (cbinfo->privatekey != NULL && - !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded)) - return DEFER; + if (!expand_check(cbinfo->certificate, US"tls_certificate", &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) + { + 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 (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); + 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 && *expanded) + { + 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 (cbinfo->is_server && cbinfo->u_ocsp.server.file) { if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded)) return DEFER; - if (expanded != NULL && *expanded != 0) + if (expanded && *expanded) { 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)) + if ( cbinfo->u_ocsp.server.file_expanded + && (Ustrcmp(expanded, cbinfo->u_ocsp.server.file_expanded) == 0)) + { + DEBUG(D_tls) debug_printf(" - value unchanged, using existing values\n"); + } + else { - DEBUG(D_tls) - debug_printf("tls_ocsp_file value unchanged, using existing values.\n"); - } else { - ocsp_load_response(sctx, cbinfo, expanded); + ocsp_load_response(sctx, cbinfo, expanded); } } } @@ -1111,8 +1277,7 @@ 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) + if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS status callback, null content"); else DEBUG(D_tls) debug_printf(" null\n"); @@ -1122,7 +1287,7 @@ if(!p) if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) { tls_out.ocsp = OCSP_FAILED; - if (log_extra_selector & LX_tls_cipher) + if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Received TLS cert status response, parse error"); else DEBUG(D_tls) debug_printf(" parse error\n"); @@ -1132,7 +1297,7 @@ if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) if(!(bs = OCSP_response_get1_basic(rsp))) { tls_out.ocsp = OCSP_FAILED; - if (log_extra_selector & LX_tls_cipher) + if (LOGGING(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"); @@ -1159,31 +1324,41 @@ if(!(bs = OCSP_response_get1_basic(rsp))) /* 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, + if ((i = OCSP_basic_verify(bs, cbinfo->u_ocsp.client.verify_stack, cbinfo->u_ocsp.client.verify_store, 0)) <= 0) { tls_out.ocsp = OCSP_FAILED; - if (log_extra_selector & LX_tls_cipher) + if (LOGGING(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; + goto failed; } BIO_printf(bp, "OCSP response well-formed and signed OK\n"); + /*XXX So we have a good stapled OCSP status. How do we know + it is for the cert of interest? OpenSSL 1.1.0 has a routine + OCSP_resp_find_status() which matches on a cert id, which presumably + we should use. Making an id needs OCSP_cert_id_new(), which takes + issuerName, issuerKey, serialNumber. Are they all in the cert? + + For now, carry on blindly accepting the resp. */ + { - STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses; OCSP_SINGLERESP * single; +#ifdef EXIM_HAVE_OCSP_RESP_COUNT + if (OCSP_resp_count(bs) != 1) +#else + STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses; if (sk_OCSP_SINGLERESP_num(sresp) != 1) +#endif { 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; + goto failed; } single = OCSP_resp_get0(bs, 0); status = OCSP_single_get0_status(single, &reason, &rev, @@ -1198,7 +1373,6 @@ if(!(bs = OCSP_response_get1_basic(rsp))) 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 { @@ -1209,24 +1383,24 @@ if(!(bs = OCSP_response_get1_basic(rsp))) case V_OCSP_CERTSTATUS_GOOD: tls_out.ocsp = OCSP_VFIED; i = 1; - break; + goto good; 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: + failed: + i = cbinfo->u_ocsp.client.verify_required ? 0 : 1; + good: BIO_free(bp); } @@ -1280,19 +1454,22 @@ if ((cbinfo->is_server = host==NULL)) cbinfo->u_ocsp.server.response = NULL; } else + { cbinfo->u_ocsp.client.verify_store = NULL; + cbinfo->u_ocsp.client.verify_stack = NULL; + } #endif cbinfo->dhparam = dhparam; cbinfo->server_cipher_list = NULL; cbinfo->host = host; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT cbinfo->event_action = NULL; #endif SSL_load_error_strings(); /* basic set up */ OpenSSL_add_ssl_algorithms(); -#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) +#ifdef EXIM_HAVE_SHA256 /* SHA256 is becoming ever more popular. This makes sure it gets added to the list of available digests. */ EVP_add_digest(EVP_sha256()); @@ -1306,10 +1483,9 @@ when OpenSSL is built without SSLv2 support. By disabling with openssl_options, we can let admins re-enable with the existing knob. */ -*ctxp = SSL_CTX_new((host == NULL)? - SSLv23_server_method() : SSLv23_client_method()); +*ctxp = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method()); -if (*ctxp == NULL) return tls_error(US"SSL_CTX_new", host, NULL); +if (!*ctxp) 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 @@ -1375,8 +1551,8 @@ if ( !init_dh(*ctxp, dhparam, host) /* Set up certificate and key (and perhaps OCSP info) */ -rc = tls_expand_session_files(*ctxp, cbinfo); -if (rc != OK) return rc; +if ((rc = tls_expand_session_files(*ctxp, cbinfo)) != OK) + return rc; /* If we need to handle SNI, do so */ #ifdef EXIM_HAVE_OPENSSL_TLSEXT @@ -1407,6 +1583,11 @@ else /* client */ DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n"); return FAIL; } + if (!(cbinfo->u_ocsp.client.verify_stack = sk_X509_new_null())) + { + DEBUG(D_tls) debug_printf("failed to create stack 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); } @@ -1415,9 +1596,10 @@ else /* client */ cbinfo->verify_cert_hostnames = NULL; +#ifdef EXIM_HAVE_EPHEM_RSA_KEX /* Set up the RSA callback */ - SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback); +#endif /* Finally, set the timeout, and we are done */ @@ -1518,26 +1700,18 @@ uschar *expcerts, *expcrl; if (!expand_check(certs, US"tls_verify_certificates", &expcerts)) return DEFER; -if (expcerts != NULL && *expcerts != '\0') +if (expcerts && *expcerts) { - if (Ustrcmp(expcerts, "system") == 0) - { - /* Tell the library to use its compiled-in location for the system default - CA bundle, only */ + /* Tell the library to use its compiled-in location for the system default + CA bundle. Then add the ones specified in the config, if any. */ - if (!SSL_CTX_set_default_verify_paths(sctx)) - return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); - } - else + if (!SSL_CTX_set_default_verify_paths(sctx)) + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); + + if (Ustrcmp(expcerts, "system") != 0) { struct stat statbuf; - /* Tell the library to use its compiled-in location for the system default - CA bundle. Those given by the exim config are additional to these */ - - 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) { log_write(0, LOG_MAIN|LOG_PANIC, @@ -1557,8 +1731,8 @@ if (expcerts != NULL && *expcerts != '\0') certificates are recognized, but the error message is still misleading (it says no certificate was supplied.) But this is better. */ - if ((file == NULL || statbuf.st_size > 0) && - !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) + if ( (!file || statbuf.st_size > 0) + && !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 @@ -1567,15 +1741,16 @@ if (expcerts != NULL && *expcerts != '\0') 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 + Meanwhile, the client library as default 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) + if (file) { STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file); - DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", + + DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", sk_X509_NAME_num(names)); SSL_CTX_set_client_CA_list(sctx, names); } @@ -1584,20 +1759,20 @@ if (expcerts != NULL && *expcerts != '\0') /* Handle a certificate revocation list. */ - #if OPENSSL_VERSION_NUMBER > 0x00907000L +#if OPENSSL_VERSION_NUMBER > 0x00907000L /* This bit of code is now the version supplied by Lars Mainka. (I have - * merely reformatted it into the Exim code style.) + merely reformatted it into the Exim code style.) - * "From here I changed the code to add support for multiple crl's - * in pem format in one file or to support hashed directory entries in - * pem format instead of a file. This method now uses the library function - * X509_STORE_load_locations to add the CRL location to the SSL context. - * OpenSSL will then handle the verify against CA certs and CRLs by - * itself in the verify callback." */ + "From here I changed the code to add support for multiple crl's + in pem format in one file or to support hashed directory entries in + pem format instead of a file. This method now uses the library function + X509_STORE_load_locations to add the CRL location to the SSL context. + OpenSSL will then handle the verify against CA certs and CRLs by + itself in the verify callback." */ if (!expand_check(crl, US"tls_crl", &expcrl)) return DEFER; - if (expcrl != NULL && *expcrl != 0) + if (expcrl && *expcrl) { struct stat statbufcrl; if (Ustat(expcrl, &statbufcrl) < 0) @@ -1633,7 +1808,7 @@ if (expcerts != NULL && *expcerts != '\0') } } - #endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */ +#endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */ /* If verification is optional, don't fail if no certificate */ @@ -1821,6 +1996,7 @@ ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0; ssl_xfer_eof = ssl_xfer_error = 0; receive_getc = tls_getc; +receive_get_cache = tls_get_cache; receive_ungetc = tls_ungetc; receive_feof = tls_feof; receive_ferror = tls_ferror; @@ -1839,7 +2015,7 @@ tls_client_basic_ctx_init(SSL_CTX * ctx, ) { int rc; -/* stick to the old behaviour for compatibility if tls_verify_certificates is +/* 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 */ @@ -1861,7 +2037,7 @@ if ((rc = setup_certs(ctx, ob->tls_verify_certificates, if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK) { cbinfo->verify_cert_hostnames = -#ifdef EXPERIMENTAL_INTERNATIONAL +#ifdef SUPPORT_I18N string_domain_utf8_to_alabel(host->name, NULL); #else host->name; @@ -1914,8 +2090,8 @@ for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); 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 0: /* action not taken */ case 1: break; } @@ -2072,8 +2248,7 @@ if (ob->tls_sni) 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", + log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n", tls_out.sni); #endif } @@ -2113,7 +2288,7 @@ if (request_ocsp) } #endif -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT client_static_cbinfo->event_action = tb->event_action; #endif @@ -2192,6 +2367,7 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n"); receive_getc = smtp_getc; + receive_get_cache = smtp_get_cache; receive_ungetc = smtp_ungetc; receive_feof = smtp_feof; receive_ferror = smtp_ferror; @@ -2237,6 +2413,16 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) return ssl_xfer_buffer[ssl_xfer_buffer_lwm++]; } +void +tls_get_cache() +{ +#ifndef DISABLE_DKIM +int n = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm; +if (n > 0) + dkim_exim_verify_feed(ssl_xfer_buffer+ssl_xfer_buffer_lwm, n); +#endif +} + /************************************************* @@ -2318,27 +2504,28 @@ while (left > 0) switch (error) { case SSL_ERROR_SSL: - ERR_error_string(ERR_get_error(), ssl_errstring); - log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring); - return -1; + ERR_error_string(ERR_get_error(), ssl_errstring); + log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring); + return -1; case SSL_ERROR_NONE: - left -= outbytes; - buff += outbytes; - break; + left -= outbytes; + buff += outbytes; + break; case SSL_ERROR_ZERO_RETURN: - log_write(0, LOG_MAIN, "SSL channel closed on write"); - return -1; + 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)); + log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s", + sender_fullhost ? sender_fullhost : US"", + strerror(errno)); + return -1; default: - log_write(0, LOG_MAIN, "SSL_write error %d", error); - return -1; + log_write(0, LOG_MAIN, "SSL_write error %d", error); + return -1; } } return len; @@ -2549,8 +2736,13 @@ i = (i + 7) / 8; if (i < needed_len) needed_len = i; +#ifdef EXIM_HAVE_RAND_PSEUDO /* We do not care if crypto-strong */ i = RAND_pseudo_bytes(smallbuf, needed_len); +#else +i = RAND_bytes(smallbuf, needed_len); +#endif + if (i < 0) { DEBUG(D_all) @@ -2744,6 +2936,9 @@ result = 0L; #ifdef SSL_OP_NO_SSLv2 result |= SSL_OP_NO_SSLv2; #endif +#ifdef SSL_OP_SINGLE_DH_USE +result |= SSL_OP_SINGLE_DH_USE; +#endif if (option_spec == NULL) { @@ -2767,6 +2962,7 @@ for (s=option_spec; *s != '\0'; /**/) keep_c = *end; *end = '\0'; item_parsed = tls_openssl_one_option_parse(s, &item); + *end = keep_c; if (!item_parsed) { DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s); @@ -2778,7 +2974,6 @@ for (s=option_spec; *s != '\0'; /**/) result |= item; else result &= ~item; - *end = keep_c; s = end; }