X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Ftls-openssl.c;h=944e33bc92e99dedcc5a0d530b25719289f9a36f;hb=a466d09b249d5018ecb0f1ec45adba545c02c75b;hp=7e424f4f1a0f878fea4bc883bcd7e550696957ac;hpb=133d2546c36766081aef8b8fc7c642862b83ea2e;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 7e424f4f1..944e33bc9 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -38,6 +38,13 @@ functions from the OpenSSL library. */ #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) # define EXIM_HAVE_OPENSSL_TLSEXT #endif +#if OPENSSL_VERSION_NUMBER >= 0x010100000L +# define EXIM_HAVE_OPENSSL_CHECKHOST +#endif +#if OPENSSL_VERSION_NUMBER >= 0x010000000L \ + && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L +# define EXIM_HAVE_OPENSSL_CHECKHOST +#endif #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP) # warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile" @@ -116,11 +123,8 @@ typedef struct tls_ext_ctx_cb { uschar *server_cipher_list; /* only passed down to tls_error: */ host_item *host; - -#ifdef EXPERIMENTAL_CERTNAMES uschar * verify_cert_hostnames; -#endif -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT uschar * event_action; #endif } tls_ext_ctx_cb; @@ -167,27 +171,28 @@ Returns: OK/DEFER/FAIL static int tls_error(uschar *prefix, host_item *host, uschar *msg) { -if (msg == NULL) +if (!msg) { ERR_error_string(ERR_get_error(), ssl_errstring); msg = (uschar *)ssl_errstring; } -if (host == NULL) +if (host) + { + log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection (%s): %s", + host->name, host->address, prefix, msg); + return FAIL; + } +else { uschar *conn_info = smtp_get_connection_info(); if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; + /* I'd like to get separated H= here, but too hard for now */ log_write(0, LOG_MAIN, "TLS error on %s (%s): %s", conn_info, prefix, msg); return DEFER; } -else - { - log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s): %s", - host->name, host->address, prefix, msg); - return FAIL; - } } @@ -287,6 +292,10 @@ verify_callback(int state, X509_STORE_CTX *x509ctx, X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); int depth = X509_STORE_CTX_get_error_depth(x509ctx); static uschar txt[256]; +#ifdef EXPERIMENTAL_EVENT +uschar * ev; +uschar * yield; +#endif X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); @@ -296,7 +305,6 @@ if (state == 0) depth, X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), txt); - tlsp->certificate_verified = FALSE; *calledp = TRUE; if (!*optionalp) { @@ -321,18 +329,20 @@ else if (depth != 0) ERR_clear_error(); } #endif -#ifdef EXPERIMENTAL_TPDA - if (tlsp == &tls_out && client_static_cbinfo->event_action) +#ifdef EXPERIMENTAL_EVENT + ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action; + if (ev) { tlsp->peercert = X509_dup(cert); - if (tpda_raise_event(client_static_cbinfo->event_action, - US"tls:cert", string_sprintf("%d", depth)) == DEFER) + if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth)))) { log_write(0, LOG_MAIN, "SSL verify denied by event-action: " - "depth=%d cert=%s", depth, txt); - tlsp->certificate_verified = FALSE; + "depth=%d cert=%s: %s", depth, txt, yield); *calledp = TRUE; - return 0; /* reject */ + if (!*optionalp) + return 0; /* reject */ + DEBUG(D_tls) debug_printf("Event-action verify failure overridden " + "(host in tls_try_verify_hosts)\n"); } X509_free(tlsp->peercert); tlsp->peercert = NULL; @@ -341,21 +351,21 @@ else if (depth != 0) } else { -#ifdef EXPERIMENTAL_CERTNAMES uschar * verify_cert_hostnames; -#endif tlsp->peerdn = txt; tlsp->peercert = X509_dup(cert); -#ifdef EXPERIMENTAL_CERTNAMES if ( tlsp == &tls_out && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames))) /* client, wanting hostname check */ -# if OPENSSL_VERSION_NUMBER >= 0x010100000L || OPENSSL_VERSION_NUMBER >= 0x010002000L +# if EXIM_HAVE_OPENSSL_CHECKHOST # ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS # define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0 +# endif +# ifndef X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS +# define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0 # endif { int sep = 0; @@ -364,7 +374,8 @@ else int rc; while ((name = string_nextinlist(&list, &sep, NULL, 0))) if ((rc = X509_check_host(cert, name, 0, - X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS))) + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS + | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS))) { if (rc < 0) { @@ -377,7 +388,11 @@ else { log_write(0, LOG_MAIN, "SSL verify error: certificate name mismatch: \"%s\"\n", txt); - return 0; /* reject */ + *calledp = TRUE; + if (!*optionalp) + return 0; /* reject */ + DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " + "tls_try_verify_hosts)\n"); } } # else @@ -385,24 +400,27 @@ else { log_write(0, LOG_MAIN, "SSL verify error: certificate name mismatch: \"%s\"\n", txt); - return 0; /* reject */ + *calledp = TRUE; + if (!*optionalp) + return 0; /* reject */ + DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " + "tls_try_verify_hosts)\n"); } # endif -#endif /*EXPERIMENTAL_CERTNAMES*/ -#ifdef EXPERIMENTAL_TPDA - if (tlsp == &tls_out) - { - if (tpda_raise_event(client_static_cbinfo->event_action, - US"tls:cert", US"0") == DEFER) +#ifdef EXPERIMENTAL_EVENT + ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action; + if (ev) + if ((yield = event_raise(ev, US"tls:cert", US"0"))) { log_write(0, LOG_MAIN, "SSL verify denied by event-action: " - "depth=0 cert=%s", txt); - tlsp->certificate_verified = FALSE; + "depth=0 cert=%s: %s", txt, yield); *calledp = TRUE; - return 0; /* reject */ + if (!*optionalp) + return 0; /* reject */ + DEBUG(D_tls) debug_printf("Event-action verify failure overridden " + "(host in tls_try_verify_hosts)\n"); } - } #endif DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n", @@ -437,8 +455,9 @@ verify_callback_client_dane(int state, X509_STORE_CTX * x509ctx) { X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); static uschar txt[256]; -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT int depth = X509_STORE_CTX_get_error_depth(x509ctx); +uschar * yield; #endif X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); @@ -447,14 +466,14 @@ DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", txt); tls_out.peerdn = txt; tls_out.peercert = X509_dup(cert); -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT if (client_static_cbinfo->event_action) { - if (tpda_raise_event(client_static_cbinfo->event_action, - US"tls:cert", string_sprintf("%d", depth)) == DEFER) + if ((yield = event_raise(client_static_cbinfo->event_action, + US"tls:cert", string_sprintf("%d", depth)))) { log_write(0, LOG_MAIN, "DANE verify denied by event-action: " - "depth=%d cert=%s", depth, txt); + "depth=%d cert=%s: %s", depth, txt, yield); tls_out.certificate_verified = FALSE; return 0; /* reject */ } @@ -1139,7 +1158,7 @@ else cbinfo->dhparam = dhparam; cbinfo->server_cipher_list = NULL; cbinfo->host = host; -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT cbinfo->event_action = NULL; #endif @@ -1263,9 +1282,7 @@ else /* client */ # endif #endif -#ifdef EXPERIMENTAL_CERTNAMES cbinfo->verify_cert_hostnames = NULL; -#endif /* Set up the RSA callback */ @@ -1349,41 +1366,65 @@ if (!expand_check(certs, US"tls_verify_certificates", &expcerts)) if (expcerts != NULL && *expcerts != '\0') { - struct stat statbuf; - 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) + if (Ustrcmp(expcerts, "system") == 0) { - log_write(0, LOG_MAIN|LOG_PANIC, - "failed to stat %s for certificates", expcerts); - return DEFER; + /* Tell the library to use its compiled-in location for the system default + CA bundle, only */ + + if (!SSL_CTX_set_default_verify_paths(sctx)) + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); } else { - uschar *file, *dir; - if ((statbuf.st_mode & S_IFMT) == S_IFDIR) - { file = NULL; dir = expcerts; } - else - { file = expcerts; dir = NULL; } + struct stat statbuf; - /* If a certificate file is empty, the next function fails with an - unhelpful error message. If we skip it, we get the correct behaviour (no - certificates are recognized, but the error message is still misleading (it - says no certificate was supplied.) But this is better. */ + /* 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 ((file == NULL || 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); + if (!SSL_CTX_set_default_verify_paths(sctx)) + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); - /* Load the list of CAs for which we will accept certs, for sending - to the client. XXX only for file source, not dir? */ - if (file != NULL) + if (Ustat(expcerts, &statbuf) < 0) { - STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file); -DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", - sk_X509_NAME_num(names)); - SSL_CTX_set_client_CA_list(sctx, names); + log_write(0, LOG_MAIN|LOG_PANIC, + "failed to stat %s for certificates", expcerts); + return DEFER; + } + else + { + uschar *file, *dir; + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + { file = NULL; dir = expcerts; } + else + { file = expcerts; dir = NULL; } + + /* If a certificate file is empty, the next function fails with an + unhelpful error message. If we skip it, we get the correct behaviour (no + 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)) + return tls_error(US"SSL_CTX_load_verify_locations", host, NULL); + + /* Load the list of CAs for which we will accept certs, for sending + to the client. This is only for the one-file tls_verify_certificates + variant. + If a list isn't loaded into the server, but + some verify locations are set, the server end appears to make + a wildcard reqest for client certs. + Meanwhile, the client library as deafult behaviour *ignores* the list + we send over the wire - see man SSL_CTX_set_client_cert_cb. + Because of this, and that the dir variant is likely only used for + the public-CA bundle (not for a private CA), not worth fixing. + */ + if (file != NULL) + { + STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file); + DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", + sk_X509_NAME_num(names)); + SSL_CTX_set_client_CA_list(sctx, names); + } } } @@ -1637,10 +1678,7 @@ return OK; static int tls_client_basic_ctx_init(SSL_CTX * ctx, - host_item * host, smtp_transport_options_block * ob -#ifdef EXPERIMENTAL_CERTNAMES - , tls_ext_ctx_cb * cbinfo -#endif + host_item * host, smtp_transport_options_block * ob, tls_ext_ctx_cb * cbinfo ) { int rc; @@ -1648,35 +1686,27 @@ int rc; set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only the specified host patterns if one of them is defined */ -if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) || - (verify_check_host(&ob->tls_verify_hosts) == OK)) - { - if ((rc = setup_certs(ctx, ob->tls_verify_certificates, - ob->tls_crl, host, FALSE, verify_callback_client)) != OK) - return rc; +if ( ( !ob->tls_verify_hosts + && (!ob->tls_try_verify_hosts || !*ob->tls_try_verify_hosts) + ) + || (verify_check_given_host(&ob->tls_verify_hosts, host) == OK) + ) client_verify_optional = FALSE; +else if (verify_check_given_host(&ob->tls_try_verify_hosts, host) == OK) + client_verify_optional = TRUE; +else + return OK; -#ifdef EXPERIMENTAL_CERTNAMES - if (ob->tls_verify_cert_hostnames) - { - if (!expand_check(ob->tls_verify_cert_hostnames, - US"tls_verify_cert_hostnames", - &cbinfo->verify_cert_hostnames)) - return FAIL; - if (cbinfo->verify_cert_hostnames) - DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", - cbinfo->verify_cert_hostnames); - } -#endif - } -else if (verify_check_host(&ob->tls_try_verify_hosts) == OK) +if ((rc = setup_certs(ctx, ob->tls_verify_certificates, + ob->tls_crl, host, client_verify_optional, verify_callback_client)) != OK) + return rc; + +if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK) { - if ((rc = setup_certs(ctx, ob->tls_verify_certificates, - ob->tls_crl, host, TRUE, verify_callback_client)) != OK) - return rc; - client_verify_optional = TRUE; + cbinfo->verify_cert_hostnames = host->name; + DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", + cbinfo->verify_cert_hostnames); } - return OK; } @@ -1799,15 +1829,15 @@ tls_out.tlsa_usage = 0; } # endif - if ((require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp, - NULL, host->name, host->address, NULL) == OK)) + if ((require_ocsp = + verify_check_given_host(&ob->hosts_require_ocsp, host) == OK)) request_ocsp = TRUE; else # ifdef EXPERIMENTAL_DANE if (!request_ocsp) # endif - request_ocsp = verify_check_this_host(&ob->hosts_request_ocsp, - NULL, host->name, host->address, NULL) == OK; + request_ocsp = + verify_check_given_host(&ob->hosts_request_ocsp, host) == OK; } #endif @@ -1853,11 +1883,8 @@ else #endif - if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob -#ifdef EXPERIMENTAL_CERTNAMES - , client_static_cbinfo -#endif - )) != OK) + if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob, client_static_cbinfo)) + != OK) return rc; if ((client_ssl = SSL_new(client_ctx)) == NULL) @@ -1908,11 +1935,9 @@ if (request_ocsp) { /* Re-eval now $tls_out_tlsa_usage is populated. If this means we avoid the OCSP request, we wasted the setup cost in tls_init(). */ - require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp, - NULL, host->name, host->address, NULL) == OK; - request_ocsp = require_ocsp ? TRUE - : verify_check_this_host(&ob->hosts_request_ocsp, - NULL, host->name, host->address, NULL) == OK; + require_ocsp = verify_check_given_host(&ob->hosts_require_ocsp, host) == OK; + request_ocsp = require_ocsp + || verify_check_given_host(&ob->hosts_request_ocsp, host) == OK; } } # endif @@ -1925,8 +1950,8 @@ if (request_ocsp) } #endif -#ifdef EXPERIMENTAL_TPDA -client_static_cbinfo->event_action = tb->tpda_event_action; +#ifdef EXPERIMENTAL_EVENT +client_static_cbinfo->event_action = tb->event_action; #endif /* There doesn't seem to be a built-in timeout on connection. */