From aa2a70baf1a7ae2d6c579094a188c1d30d3d5fd5 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sat, 8 Nov 2014 13:24:21 +0000 Subject: [PATCH] Fix smtp transport certificate-verification option matching to use correct host Fix certificate name verification done with tls_try_verify_hosts Affected tls_verify_hosts, tls_try_verify_hosts, tls_verify_cert_hostnames. --- doc/doc-txt/experimental-spec.txt | 2 +- src/src/tls-gnu.c | 72 ++++++++++++++---------- src/src/tls-openssl.c | 39 +++++++------ test/confs/5440 | 19 +++++++ test/confs/5450 | 18 ++++++ test/log/5440 | 12 ++-- test/log/5450 | 18 ++++-- test/scripts/5440-certnames-GnuTLS/5440 | 4 ++ test/scripts/5450-certnames-OpenSSL/5450 | 4 ++ test/stderr/5420 | 4 +- 10 files changed, 128 insertions(+), 64 deletions(-) diff --git a/doc/doc-txt/experimental-spec.txt b/doc/doc-txt/experimental-spec.txt index f6529c6e2..d57cbf903 100644 --- a/doc/doc-txt/experimental-spec.txt +++ b/doc/doc-txt/experimental-spec.txt @@ -1173,7 +1173,7 @@ component FQDN). The equivalent check on the server for client certificates is not implemented. At least one major email provider is using a client certificate which fails this check. They do not retry either without -hte client certificate or in clear. +the client certificate or in clear. It is possible to duplicate the effect of this checking by creative use of Events. diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 093b3a375..9d72ebc66 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -75,11 +75,7 @@ Changes: /* Values for verify_requirement */ enum peer_verify_requirement - { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED -#ifdef EXPERIMENTAL_CERTNAMES - ,VERIFY_WITHHOST -#endif - }; + { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED }; /* This holds most state for server or client; with this, we can set up an outbound TLS-enabled connection in an ACL callout, while not stomping all @@ -1390,7 +1386,7 @@ if (rc < 0 || else { #ifdef EXPERIMENTAL_CERTNAMES - if (state->verify_requirement == VERIFY_WITHHOST) + if (state->exp_tls_verify_cert_hostnames) { int sep = 0; uschar * list = state->exp_tls_verify_cert_hostnames; @@ -1402,9 +1398,13 @@ else { DEBUG(D_tls) debug_printf("TLS certificate verification failed: cert name mismatch\n"); - gnutls_alert_send(state->session, - GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE); - return FALSE; + if (state->verify_requirement >= VERIFY_REQUIRED) + { + gnutls_alert_send(state->session, + GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE); + return FALSE; + } + return TRUE; } } #endif @@ -1771,6 +1771,23 @@ return OK; +#ifdef EXPERIMENTAL_CERTNAMES +static void +tls_client_setup_hostname_checks(host_item * host, exim_gnutls_state_st * state, + smtp_transport_options_block * ob) +{ +if (verify_check_this_host(&ob->tls_verify_cert_hostnames, NULL, + host->name, host->address, NULL) == OK) + { + state->exp_tls_verify_cert_hostnames = host->name; + DEBUG(D_tls) + debug_printf("TLS: server cert verification includes hostname: \"%s\".\n", + state->exp_tls_verify_cert_hostnames); + } +} +#endif + + /************************************************* * Start a TLS session in a client * *************************************************/ @@ -1837,35 +1854,28 @@ if ((rc = tls_init(host, ob->tls_certificate, ob->tls_privatekey, set but both tls_verify_hosts and tls_try_verify_hosts are unset. Check only the specified host patterns if one of them is defined */ -if (( state->exp_tls_verify_certificates - && !ob->tls_verify_hosts - && !ob->tls_try_verify_hosts - ) - || - verify_check_host(&ob->tls_verify_hosts) == OK +if ( ( state->exp_tls_verify_certificates + && !ob->tls_verify_hosts + && !ob->tls_try_verify_hosts + ) + || verify_check_this_host(&ob->tls_verify_hosts, NULL, + host->name, host->address, NULL) == OK ) { #ifdef EXPERIMENTAL_CERTNAMES - if (verify_check_host(&ob->tls_verify_cert_hostnames) == OK) - { - DEBUG(D_tls) - debug_printf("TLS: server cert incl. hostname verification required.\n"); - state->verify_requirement = VERIFY_WITHHOST; - state->exp_tls_verify_cert_hostnames = host->name; - DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", - state->exp_tls_verify_cert_hostnames); - } - else + tls_client_setup_hostname_checks(host, state, ob); #endif - { - DEBUG(D_tls) - debug_printf("TLS: server certificate verification required.\n"); - state->verify_requirement = VERIFY_REQUIRED; - } + DEBUG(D_tls) + debug_printf("TLS: server certificate verification required.\n"); + state->verify_requirement = VERIFY_REQUIRED; gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUIRE); } -else if (verify_check_host(&ob->tls_try_verify_hosts) == OK) +else if (verify_check_this_host(&ob->tls_try_verify_hosts, NULL, + host->name, host->address, NULL) == OK) { +#ifdef EXPERIMENTAL_CERTNAMES + tls_client_setup_hostname_checks(host, state, ob); +#endif DEBUG(D_tls) debug_printf("TLS: server certificate verification optional.\n"); state->verify_requirement = VERIFY_OPTIONAL; diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 628860044..b1094b1c2 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1683,31 +1683,30 @@ 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) + || (verify_check_this_host(&ob->tls_verify_hosts, NULL, + host->name, host->address, NULL) == OK) + ) client_verify_optional = FALSE; +else if (verify_check_this_host(&ob->tls_try_verify_hosts, NULL, + host->name, host->address, NULL) == OK) + client_verify_optional = TRUE; +else + return OK; + +if ((rc = setup_certs(ctx, ob->tls_verify_certificates, + ob->tls_crl, host, client_verify_optional, verify_callback_client)) != OK) + return rc; #ifdef EXPERIMENTAL_CERTNAMES - if (verify_check_host(&ob->tls_verify_cert_hostnames) == OK) - { - cbinfo->verify_cert_hostnames = host->name; - 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 (verify_check_this_host(&ob->tls_verify_cert_hostnames, NULL, + host->name, host->address, NULL) == 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); } - +#endif return OK; } diff --git a/test/confs/5440 b/test/confs/5440 index 01ba52532..95c434549 100644 --- a/test/confs/5440 +++ b/test/confs/5440 @@ -88,6 +88,11 @@ client_s: retry_use_local_part transport = send_to_server_req_passname +client_t: + driver = accept + local_parts = usert + retry_use_local_part + transport = send_to_server_req_failcarryon # ----- Transports ----- @@ -172,4 +177,18 @@ send_to_server_req_passname: tls_verify_cert_hostnames = * tls_verify_hosts = * +# this will fail to verify the cert name but carry on (try-verify mode) +# fail because the cert is "server1.example.com" and the test system is something else +send_to_server_req_failcarryon: + driver = smtp + allow_localhost + hosts = HOSTNAME + port = PORT_D + tls_certificate = CERT2 + tls_privatekey = CERT2 + + tls_verify_certificates = CA1 + tls_verify_cert_hostnames = * + tls_try_verify_hosts = * + # End diff --git a/test/confs/5450 b/test/confs/5450 index dd42a3fb1..953b47423 100644 --- a/test/confs/5450 +++ b/test/confs/5450 @@ -88,6 +88,12 @@ client_s: retry_use_local_part transport = send_to_server_req_passname +client_t: + driver = accept + local_parts = usert + retry_use_local_part + transport = send_to_server_req_failcarryon + # ----- Transports ----- @@ -172,4 +178,16 @@ send_to_server_req_passname: tls_verify_cert_hostnames = * tls_verify_hosts = * +send_to_server_req_failcarryon: + driver = smtp + allow_localhost + hosts = HOSTNAME + port = PORT_D + tls_certificate = CERT2 + tls_privatekey = CERT2 + + tls_verify_certificates = CA1 + tls_verify_cert_hostnames = * + tls_try_verify_hosts = * + # End diff --git a/test/log/5440 b/test/log/5440 index f084e82a9..44cec64b1 100644 --- a/test/log/5440 +++ b/test/log/5440 @@ -1,17 +1,21 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 Start queue run: pid=pppp -qf 1999-03-02 09:44:33 10HmaX-0005vi-00 H=the.local.host.name [ip4.ip4.ip4.ip4] TLS error on connection (certificate verification failed) 1999-03-02 09:44:33 10HmaX-0005vi-00 TLS session failure: delivering unencrypted to the.local.host.name [ip4.ip4.ip4.ip4] (not in hosts_require_tls) -1999-03-02 09:44:33 10HmaX-0005vi-00 => userr@test.ex R=client_r T=send_to_server_req_failname H=the.local.host.name [ip4.ip4.ip4.ip4] C="250 OK id=10HmaZ-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 => userr@test.ex R=client_r T=send_to_server_req_failname H=the.local.host.name [ip4.ip4.ip4.ip4] C="250 OK id=10HmbA-0005vi-00" 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed -1999-03-02 09:44:33 10HmaY-0005vi-00 => users@test.ex R=client_s T=send_to_server_req_passname H=server1.example.com [ip4.ip4.ip4.ip4] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=yes DN="CN=server1.example.com" C="250 OK id=10HmbA-0005vi-00" +1999-03-02 09:44:33 10HmaY-0005vi-00 => users@test.ex R=client_s T=send_to_server_req_passname H=server1.example.com [ip4.ip4.ip4.ip4] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=yes DN="CN=server1.example.com" C="250 OK id=10HmbB-0005vi-00" 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed +1999-03-02 09:44:33 10HmaZ-0005vi-00 => usert@test.ex R=client_t T=send_to_server_req_failcarryon H=the.local.host.name [ip4.ip4.ip4.ip4] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no DN="CN=server1.example.com" C="250 OK id=10HmbC-0005vi-00" +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed 1999-03-02 09:44:33 End queue run: pid=pppp -qf ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 1999-03-02 09:44:33 TLS error on connection from the.local.host.name [ip4.ip4.ip4.ip4] (recv): A TLS fatal alert has been received.: Certificate is bad 1999-03-02 09:44:33 TLS error on connection from the.local.host.name [ip4.ip4.ip4.ip4] (send): The specified session has been invalidated for some reason. -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=yes DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" S=sss id=E10HmaY-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=yes DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" S=sss id=E10HmaY-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=yes DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" S=sss id=E10HmaZ-0005vi-00@myhost.test.ex diff --git a/test/log/5450 b/test/log/5450 index d56307a19..2cd0960b2 100644 --- a/test/log/5450 +++ b/test/log/5450 @@ -1,28 +1,34 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 Start queue run: pid=pppp -qf 1999-03-02 09:44:33 10HmaX-0005vi-00 SSL verify error: depth=0 error=unable to get local issuer certificate cert=/CN=server1.example.com 1999-03-02 09:44:33 10HmaX-0005vi-00 H=the.local.host.name [ip4.ip4.ip4.ip4] TLS error on connection (SSL_connect): error: <> 1999-03-02 09:44:33 10HmaX-0005vi-00 TLS session failure: delivering unencrypted to the.local.host.name [ip4.ip4.ip4.ip4] (not in hosts_require_tls) -1999-03-02 09:44:33 10HmaX-0005vi-00 => userq@test.ex R=client_q T=send_to_server_req_fail H=the.local.host.name [ip4.ip4.ip4.ip4] C="250 OK id=10HmbA-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 => userq@test.ex R=client_q T=send_to_server_req_fail H=the.local.host.name [ip4.ip4.ip4.ip4] C="250 OK id=10HmbB-0005vi-00" 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed 1999-03-02 09:44:33 10HmaY-0005vi-00 SSL verify error: certificate name mismatch: "/CN=server1.example.com" 1999-03-02 09:44:33 10HmaY-0005vi-00 H=the.local.host.name [ip4.ip4.ip4.ip4] TLS error on connection (SSL_connect): error: <> 1999-03-02 09:44:33 10HmaY-0005vi-00 TLS session failure: delivering unencrypted to the.local.host.name [ip4.ip4.ip4.ip4] (not in hosts_require_tls) -1999-03-02 09:44:33 10HmaY-0005vi-00 => userr@test.ex R=client_r T=send_to_server_req_failname H=the.local.host.name [ip4.ip4.ip4.ip4] C="250 OK id=10HmbB-0005vi-00" +1999-03-02 09:44:33 10HmaY-0005vi-00 => userr@test.ex R=client_r T=send_to_server_req_failname H=the.local.host.name [ip4.ip4.ip4.ip4] C="250 OK id=10HmbC-0005vi-00" 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed -1999-03-02 09:44:33 10HmaZ-0005vi-00 => users@test.ex R=client_s T=send_to_server_req_passname H=server1.example.com [ip4.ip4.ip4.ip4] X=TLSv1:AES256-SHA:256 CV=yes DN="/CN=server1.example.com" C="250 OK id=10HmbC-0005vi-00" +1999-03-02 09:44:33 10HmaZ-0005vi-00 => users@test.ex R=client_s T=send_to_server_req_passname H=server1.example.com [ip4.ip4.ip4.ip4] X=TLSv1:AES256-SHA:256 CV=yes DN="/CN=server1.example.com" C="250 OK id=10HmbD-0005vi-00" 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed +1999-03-02 09:44:33 10HmbA-0005vi-00 SSL verify error: certificate name mismatch: "/CN=server1.example.com" + +1999-03-02 09:44:33 10HmbA-0005vi-00 => usert@test.ex R=client_t T=send_to_server_req_failcarryon H=the.local.host.name [ip4.ip4.ip4.ip4] X=TLSv1:AES256-SHA:256 CV=no DN="/CN=server1.example.com" C="250 OK id=10HmbE-0005vi-00" +1999-03-02 09:44:33 10HmbA-0005vi-00 Completed 1999-03-02 09:44:33 End queue run: pid=pppp -qf ******** SERVER ******** 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 1999-03-02 09:44:33 TLS error on connection from the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] (SSL_accept): error: <> 1999-03-02 09:44:33 TLS client disconnected cleanly (rejected our certificate?) -1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex 1999-03-02 09:44:33 TLS error on connection from the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] (SSL_accept): error: <> 1999-03-02 09:44:33 TLS client disconnected cleanly (rejected our certificate?) -1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaY-0005vi-00@myhost.test.ex -1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLSv1:AES256-SHA:256 CV=yes DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" S=sss id=E10HmaZ-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtp S=sss id=E10HmaY-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLSv1:AES256-SHA:256 CV=yes DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" S=sss id=E10HmaZ-0005vi-00@myhost.test.ex +1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLSv1:AES256-SHA:256 CV=yes DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" S=sss id=E10HmbA-0005vi-00@myhost.test.ex diff --git a/test/scripts/5440-certnames-GnuTLS/5440 b/test/scripts/5440-certnames-GnuTLS/5440 index 2a61eb1d0..f43d18de4 100644 --- a/test/scripts/5440-certnames-GnuTLS/5440 +++ b/test/scripts/5440-certnames-GnuTLS/5440 @@ -10,6 +10,10 @@ Testing exim users@test.ex Testing **** +# this will fail to verify the cert name but carry on (try-verify mode) +exim usert@test.ex +Testing +**** exim -qf **** killdaemon diff --git a/test/scripts/5450-certnames-OpenSSL/5450 b/test/scripts/5450-certnames-OpenSSL/5450 index 5359096b1..3b2221c9c 100644 --- a/test/scripts/5450-certnames-OpenSSL/5450 +++ b/test/scripts/5450-certnames-OpenSSL/5450 @@ -13,6 +13,10 @@ Testing exim users@test.ex Testing **** +# this will fail to verify the cert name but carry on (try-verify mode) +exim usert@test.ex +Testing +**** exim -qf **** killdaemon diff --git a/test/stderr/5420 b/test/stderr/5420 index 64dfc0b12..464cb2c4d 100644 --- a/test/stderr/5420 +++ b/test/stderr/5420 @@ -81,8 +81,8 @@ expanding: ${if eq {$address_data}{userz}{*}{:}} 127.0.0.1 in hosts_verify_avoid_tls? no (end of list) SMTP>> STARTTLS SMTP<< 220 TLS go ahead - in tls_verify_hosts? no (option unset) - in tls_try_verify_hosts? no (option unset) +127.0.0.1 in tls_verify_hosts? no (option unset) +127.0.0.1 in tls_try_verify_hosts? no (option unset) SMTP>> EHLO myhost.test.ex SMTP<< 250-myhost.test.ex Hello the.local.host.name [ip4.ip4.ip4.ip4] 250-SIZE 52428800 -- 2.30.2