X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Ftls-openssl.c;h=f45daa767386b1eb768d0b136150b835ed527dcb;hb=468c0c7e3066886ff5028bb423b96712a155fe05;hp=456ca8142d36b0ce56e18651109a0ed30ff8c403;hpb=34e3241d80cf27c1ac37d4aab00bc77392a6265e;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 456ca8142..f45daa767 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 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Portions Copyright (c) The OpenSSL Project 1999 */ @@ -22,6 +22,9 @@ functions from the OpenSSL library. */ #include #include #include +#ifndef OPENSSL_NO_ECDH +# include +#endif #ifndef DISABLE_OCSP # include #endif @@ -59,6 +62,16 @@ functions from the OpenSSL library. */ && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L # define EXIM_HAVE_OPENSSL_CHECKHOST # endif + +# 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 +# define EXIM_HAVE_OPENSSL_EC_NIST2NID +# endif +# endif #endif #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP) @@ -139,7 +152,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; @@ -269,7 +282,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 */ @@ -331,15 +345,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); @@ -349,7 +365,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, @@ -375,13 +391,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 @@ -407,7 +423,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))) @@ -441,7 +457,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 @@ -456,15 +472,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); } @@ -474,11 +492,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 @@ -486,18 +504,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*/ @@ -644,42 +671,74 @@ Returns: TRUE if OK (nothing to set up, or setup worked) */ static BOOL -init_ecdh(SSL_CTX *sctx, host_item *host) +init_ecdh(SSL_CTX * sctx, host_item * host) { +#ifdef OPENSSL_NO_ECDH +return TRUE; +#else + +EC_KEY * ecdh; +uschar * exp_curve; +int nid; +BOOL rv; + if (host) /* No ECDH setup for clients, only for servers */ return TRUE; -#ifndef SSL_CTX_set_tmp_ecdh -/* No elliptic curve API in OpenSSL, skip it */ +# ifndef EXIM_HAVE_ECDH DEBUG(D_tls) debug_printf("No OpenSSL API to define ECDH parameters, skipping\n"); return TRUE; -#else -# ifndef NID_X9_62_prime256v1 -/* For now, stick to NIST P-256 to get "something" running. -If that's not available, bail */ -DEBUG(D_tls) - debug_printf("NIST P-256 EC curve not available, skipping ECDH setup\n"); -return TRUE; # else + +if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve)) + return FALSE; +if (!exp_curve || !*exp_curve) + return TRUE; + +# ifdef EXIM_HAVE_OPENSSL_ECDH_AUTO +/* check if new enough library to support auto ECDH temp key parameter selection */ +if (Ustrcmp(exp_curve, "auto") == 0) { - EC_KEY * ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - BOOL rv; + DEBUG(D_tls) debug_printf( + "ECDH temp key parameter settings: OpenSSL 1.2+ autoselection\n"); + SSL_CTX_set_ecdh_auto(sctx, 1); + return TRUE; + } +# endif - /* The "tmp" in the name here refers to setting a tempoary key - not to the stability of the interface. */ +DEBUG(D_tls) debug_printf("ECDH: curve '%s'\n", exp_curve); +if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef +# ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID + && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef +# endif + ) + { + tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", + exp_curve), + host, NULL); + return FALSE; + } - if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) != 0)) - { - DEBUG(D_tls) debug_printf("ECDH: enable NIST P-256 curve\n"); - } - else - tls_error(US"Error enabling NIST P-256 curve", host, NULL); - EC_KEY_free(ecdh); - return rv; +if (!(ecdh = EC_KEY_new_by_curve_name(nid))) + { + tls_error(US"Unable to create ec curve", host, NULL); + return FALSE; } -# endif -#endif + +/* The "tmp" in the name here refers to setting a temporary key +not to the stability of the interface. */ + +if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0)) + tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL); +else + DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve); + +EC_KEY_free(ecdh); +return !rv; + +# endif /*EXIM_HAVE_ECDH*/ +#endif /*OPENSSL_NO_ECDH*/ } @@ -873,7 +932,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)) @@ -882,21 +941,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("tls_ocsp_file value unchanged, using existing values.\n"); - } else { - ocsp_load_response(sctx, cbinfo, expanded); + DEBUG(D_tls) debug_printf(" - value unchanged, using existing values\n"); + } + else + { + ocsp_load_response(sctx, cbinfo, expanded); } } } @@ -1024,7 +1084,7 @@ uschar *response_der; int response_der_len; DEBUG(D_tls) - debug_printf("Received TLS status request (OCSP stapling); %s response.", + debug_printf("Received TLS status request (OCSP stapling); %s response\n", cbinfo->u_ocsp.server.response ? "have" : "lack"); tls_in.ocsp = OCSP_NOT_RESP; @@ -1066,8 +1126,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"); @@ -1077,7 +1136,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"); @@ -1087,7 +1146,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"); @@ -1118,7 +1177,7 @@ 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); @@ -1240,7 +1299,7 @@ else cbinfo->dhparam = dhparam; cbinfo->server_cipher_list = NULL; cbinfo->host = host; -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT cbinfo->event_action = NULL; #endif @@ -1321,6 +1380,7 @@ else DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); /* Initialize with DH parameters if supplied */ +/* Initialize ECDH temp key parameter selection */ if ( !init_dh(*ctxp, dhparam, host) || !init_ecdh(*ctxp, host) @@ -1472,26 +1532,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, @@ -1511,8 +1563,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 @@ -1521,15 +1573,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); } @@ -1538,20 +1591,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) @@ -1587,7 +1640,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 */ @@ -1793,7 +1846,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 */ @@ -1815,7 +1868,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; @@ -2067,7 +2120,7 @@ if (request_ocsp) } #endif -#ifdef EXPERIMENTAL_EVENT +#ifndef DISABLE_EVENT client_static_cbinfo->event_action = tb->event_action; #endif @@ -2698,6 +2751,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) {