From 37688315a566d2bfaeae040ee1cbaae3102efced Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sat, 22 Apr 2023 00:21:25 +0100 Subject: Docs: clarify TLS cert name verification --- doc/doc-docbook/spec.xfpt | 3 ++- src/src/tls-gnu.c | 2 +- src/src/tls-openssl.c | 5 +++++ src/src/tls.c | 16 ++++++++++++---- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index d2d40728b..b8f6f939d 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -26161,7 +26161,8 @@ This option give a list of hosts for which, while verifying the server certificate, checks will be included on the host name (note that this will generally be the result of a DNS MX lookup) -versus Subject and Subject-Alternate-Name fields. Wildcard names are permitted +versus the Subject-Alternate-Name (or, if none, Subject-Name) fields. +Wildcard names are permitted, limited to being the initial component of a 3-or-more component FQDN. There is no equivalent checking on client certificates. diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index f3f70d2e0..76176a64e 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2620,7 +2620,7 @@ else ) { DEBUG(D_tls) - debug_printf("TLS certificate verification failed: cert name mismatch\n"); + debug_printf("TLS certificate verification failed: cert name mismatch (per GnuTLS)\n"); if (state->verify_requirement >= VERIFY_REQUIRED) goto badcert; return TRUE; diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 9d0ab2fdf..cd715cc18 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1192,6 +1192,8 @@ else uschar * name; int rc; while ((name = string_nextinlist(&list, &sep, NULL, 0))) + { + DEBUG(D_tls|D_lookup) debug_printf_indent("%s suitable for cert, per OpenSSL?", name); if ((rc = X509_check_host(cert, CCS name, 0, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS, @@ -1203,8 +1205,11 @@ else tlsp == &tls_out ? deliver_host_address : sender_host_address); name = NULL; } + DEBUG(D_tls|D_lookup) debug_printf_indent(" yes\n"); break; } + else DEBUG(D_tls|D_lookup) debug_printf_indent(" no\n"); + } if (!name) #else if (!tls_is_name_for_cert(verify_cert_hostnames, cert)) diff --git a/src/src/tls.c b/src/src/tls.c index 825313a9a..8f4344c6c 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -670,21 +670,24 @@ Returns: BOOL tls_is_name_for_cert(const uschar * namelist, void * cert) { -uschar * altnames = tls_cert_subject_altname(cert, US"dns"); -uschar * subjdn; -uschar * certname; +uschar * altnames, * subjdn, * certname, * cmpname; int cmp_sep = 0; -uschar * cmpname; if ((altnames = tls_cert_subject_altname(cert, US"dns"))) { int alt_sep = '\n'; + DEBUG(D_tls|D_lookup) debug_printf_indent("cert has SAN\n"); while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0))) { const uschar * an = altnames; + DEBUG(D_tls|D_lookup) debug_printf_indent(" %s in SANs?", cmpname); while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0))) if (is_name_match(cmpname, certname)) + { + DEBUG(D_tls|D_lookup) debug_printf_indent(" yes (matched %s)\n", certname); return TRUE; + } + DEBUG(D_tls|D_lookup) debug_printf_indent(" no (end of SAN list)\n"); } } @@ -696,13 +699,18 @@ else if ((subjdn = tls_cert_subject(cert, NULL))) while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0))) { const uschar * sn = subjdn; + DEBUG(D_tls|D_lookup) debug_printf_indent(" %s in SN?", cmpname); while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0))) if ( *certname++ == 'C' && *certname++ == 'N' && *certname++ == '=' && is_name_match(cmpname, certname) ) + { + DEBUG(D_tls|D_lookup) debug_printf_indent(" yes (matched %s)\n", certname); return TRUE; + } + DEBUG(D_tls|D_lookup) debug_printf_indent(" no (end of CN)\n"); } } return FALSE; -- cgit v1.2.3