Description: TLS: use RFC 6125 rules for certificate name checks when CNAMES are present. Bug 2594 Origin: upstream https://git.exim.org/exim.git/commit/0851a3bbf4667081d47f5d85b6b3a5cb33cbdba6 Bug: https://bugs.exim.org/show_bug.cgi?id=2594 Forwarded: not-needed Last-Update: 2021-03-02 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -41,10 +41,15 @@ JH/10 OpenSSL: Fix aggregation of messag JH/11 Harden plaintext authenticator against a badly misconfigured client-send string. Previously it was possible to cause undefined behaviour in a library routine (usually a crash). Found by "zerons". +JH/06 Bug 2594: Change the name used for certificate name checks in the smtp + transport. Previously it was the name on the DNS A-record; use instead + the head of the CNAME chain leading there (if there is one). This seems + to align better with RFC 6125. + JH/18 GnuTLS: fix $tls_out_ocsp under hosts_request_ocsp. Previously the verification result was not updated unless hosts_require_ocsp applied. JH/20 Bug 2389: fix server advertising of usable certificates, under GnuTLS in --- a/src/host.c +++ b/src/host.c @@ -1966,10 +1966,17 @@ host_item *last = NULL; BOOL temp_error = FALSE; #if HAVE_IPV6 int af; #endif +#ifndef DISABLE_TLS +/* Copy the host name at this point to the value which is used for +TLS certificate name checking, before anything modifies it. */ + +host->certname = host->name; +#endif + /* Make sure DNS options are set as required. This appears to be necessary in some circumstances when the get..byname() function actually calls the DNS. */ dns_init((flags & HOST_FIND_QUALIFY_SINGLE) != 0, (flags & HOST_FIND_SEARCH_PARENTS) != 0, @@ -2132,10 +2139,13 @@ for (i = 1; i <= times; else { host_item *next = store_get(sizeof(host_item)); next->name = host->name; +#ifndef DISABLE_TLS + next->certname = host->certname; +#endif next->mx = host->mx; next->address = text_address; next->port = PORT_NONE; next->status = hstatus_unknown; next->why = hwhy_unknown; @@ -2150,16 +2160,16 @@ for (i = 1; i <= times; /* If no hosts were found, the address field in the original host block will be NULL. If temp_error is set, at least one of the lookups gave a temporary error, so we pass that back. */ -if (host->address == NULL) +if (!host->address) { uschar *msg = #ifndef STAND_ALONE - (message_id[0] == 0 && smtp_in != NULL)? - string_sprintf("no IP address found for host %s (during %s)", host->name, + message_id[0] == 0 && smtp_in + ? string_sprintf("no IP address found for host %s (during %s)", host->name, smtp_get_connection_info()) : #endif string_sprintf("no IP address found for host %s", host->name); HDEBUG(D_host_lookup) debug_printf("%s\n", msg); @@ -2277,10 +2287,17 @@ dns_record *rr; host_item *thishostlast = NULL; /* Indicates not yet filled in anything */ BOOL v6_find_again = FALSE; BOOL dnssec_fail = FALSE; int i; +#ifndef DISABLE_TLS +/* Copy the host name at this point to the value which is used for +TLS certificate name checking, before any CNAME-following modifies it. */ + +host->certname = host->name; +#endif + /* If allow_ip is set, a name which is an IP address returns that value as its address. This is used for MX records when allow_mx_to_ip is set, for those sites that feel they have to flaunt the RFC rules. */ if (allow_ip && string_is_ip_address(host->name, NULL) != 0) --- a/src/structs.h +++ b/src/structs.h @@ -77,18 +77,21 @@ host addresses is done using this struct typedef enum {DS_UNK=-1, DS_NO, DS_YES} dnssec_status_t; typedef struct host_item { struct host_item *next; - const uschar *name; /* Host name */ - const uschar *address; /* IP address in text form */ - int port; /* port value in host order (if SRV lookup) */ - int mx; /* MX value if found via MX records */ - int sort_key; /* MX*1000 plus random "fraction" */ - int status; /* Usable, unusable, or unknown */ - int why; /* Why host is unusable */ - int last_try; /* Time of last try if known */ + const uschar *name; /* Host name */ +#ifndef DISABLE_TLS + const uschar *certname; /* Name used for certificate checks */ +#endif + const uschar *address; /* IP address in text form */ + int port; /* port value in host order (if SRV lookup) */ + int mx; /* MX value if found via MX records */ + int sort_key; /* MX*1000 plus random "fraction" */ + int status; /* Usable, unusable, or unknown */ + int why; /* Why host is unusable */ + int last_try; /* Time of last try if known */ dnssec_status_t dnssec; } host_item; /* Chain of rewrite rules, read from the rewrite config, or parsed from the rewrite_headers field of a transport. */ --- a/src/tls-gnu.c +++ b/src/tls-gnu.c @@ -2191,13 +2191,13 @@ tls_client_setup_hostname_checks(host_it { if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK) { state->exp_tls_verify_cert_hostnames = #ifdef SUPPORT_I18N - string_domain_utf8_to_alabel(host->name, NULL); + string_domain_utf8_to_alabel(host->certname, NULL); #else - host->name; + host->certname; #endif DEBUG(D_tls) debug_printf("TLS: server cert verification includes hostname: \"%s\".\n", state->exp_tls_verify_cert_hostnames); } --- a/src/tls-openssl.c +++ b/src/tls-openssl.c @@ -309,18 +309,18 @@ typedef struct tls_ext_ctx_cb { X509_STORE *verify_store; /* non-null if status requested */ BOOL verify_required; } client; } u_ocsp; #endif - uschar *dhparam; + uschar * dhparam; /* these are cached from first expand */ - uschar *server_cipher_list; + uschar * server_cipher_list; /* only passed down to tls_error: */ - host_item *host; + host_item * host; const uschar * verify_cert_hostnames; #ifndef DISABLE_EVENT - uschar * event_action; + uschar * event_action; #endif } tls_ext_ctx_cb; /* should figure out a cleanup of API to handle state preserved per implementation, for various reasons, which can be void * in the APIs. @@ -2359,13 +2359,13 @@ if ((rc = setup_certs(ctx, ob->tls_verif if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK) { cbinfo->verify_cert_hostnames = #ifdef SUPPORT_I18N - string_domain_utf8_to_alabel(host->name, NULL); + string_domain_utf8_to_alabel(host->certname, NULL); #else - host->name; + host->certname; #endif DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", cbinfo->verify_cert_hostnames); } return OK;