X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/63f0dbe0ca0aba7be3bc8807f45c8703a1cfafe1..bc3c7bb7d4aba3e563434e5627fe1f2176aa18c0:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index b1dccb8ee..83594c973 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -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 @@ -68,7 +80,9 @@ functions from the OpenSSL library. */ # 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 @@ -152,7 +166,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; @@ -225,6 +239,7 @@ else +#ifdef EXIM_HAVE_EPHEM_RSA_KEX /************************************************* * Callback to generate RSA key * *************************************************/ @@ -242,10 +257,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); + +#ifdef EXIM_HAVE_RSA_GENKEY_EX +if ( !BN_set_word(bn, (unsigned long)RSA_F4) + || !RSA_generate_key_ex(rsa_key, keylength, bn, NULL) + ) +#else rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL); if (rsa_key == 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 +281,7 @@ if (rsa_key == NULL) } return rsa_key; } +#endif @@ -282,7 +310,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 +373,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 +393,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 +419,13 @@ 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(); } #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 +451,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 +485,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 +500,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 +520,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 +532,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*/ @@ -855,7 +897,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"); @@ -918,7 +960,7 @@ if (cbinfo->privatekey != NULL && 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) +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)) @@ -927,21 +969,22 @@ if (expanded != NULL && *expanded != 0) } #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 +1154,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 +1164,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 +1174,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"); @@ -1163,27 +1205,37 @@ if(!(bs = OCSP_response_get1_basic(rsp))) 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 +1250,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 +1260,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); } @@ -1285,14 +1336,14 @@ else 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 +1357,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 @@ -1415,9 +1465,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 +1569,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 +1600,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 +1610,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 +1628,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 +1677,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 */ @@ -1839,7 +1883,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 +1905,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; @@ -2072,8 +2116,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 +2156,7 @@ if (request_ocsp) } #endif -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT client_static_cbinfo->event_action = tb->event_action; #endif @@ -2549,8 +2592,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 +2792,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) {