summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2014-05-20 21:25:10 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2014-05-20 21:25:10 +0100
commite51c7be22dfccad376659a1a46cee93c9979bbf7 (patch)
tree3a6facf5bd5b51f1b3e21c62736ae04bc7504099 /src
parent2e6afa4f11972312d3dbb9bb1d4f4bf585a3cdd2 (diff)
Support optional server certificate name checking. Bug 1479
Enable EXPERIMENTAL_CERTNAMES to include.
Diffstat (limited to 'src')
-rw-r--r--src/src/EDITME4
-rw-r--r--src/src/config.h.defaults1
-rw-r--r--src/src/exim.c3
-rw-r--r--src/src/functions.h7
-rw-r--r--src/src/tls-gnu.c131
-rw-r--r--src/src/tls-openssl.c69
-rw-r--r--src/src/tls.c74
-rw-r--r--src/src/tlscert-gnu.c5
-rw-r--r--src/src/tlscert-openssl.c10
-rw-r--r--src/src/transports/smtp.c7
-rw-r--r--src/src/transports/smtp.h15
11 files changed, 279 insertions, 47 deletions
diff --git a/src/src/EDITME b/src/src/EDITME
index 7d58af744..83ca43cd1 100644
--- a/src/src/EDITME
+++ b/src/src/EDITME
@@ -485,6 +485,10 @@ EXIM_MONITOR=eximon.bin
# Uncomment the following line to enable Experimental Proxy Protocol
# EXPERIMENTAL_PROXY=yes
+# Uncomment the following line to enable support for checking certiticate
+# ownership
+# EXPERIMENTAL_CERTNAMES=yes
+
###############################################################################
# THESE ARE THINGS YOU MIGHT WANT TO SPECIFY #
diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
index 69df842e7..3ab73d861 100644
--- a/src/src/config.h.defaults
+++ b/src/src/config.h.defaults
@@ -165,6 +165,7 @@ it's a default value. */
/* EXPERIMENTAL features */
#define EXPERIMENTAL_BRIGHTMAIL
+#define EXPERIMENTAL_CERTNAMES
#define EXPERIMENTAL_DCC
#define EXPERIMENTAL_DMARC
#define EXPERIMENTAL_OCSP
diff --git a/src/src/exim.c b/src/src/exim.c
index ded12fa34..bca6cc8a4 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -831,6 +831,9 @@ fprintf(f, "Support for:");
#ifdef EXPERIMENTAL_REDIS
fprintf(f, " Experimental_Redis");
#endif
+#ifdef EXPERIMENTAL_CERTNAMES
+ fprintf(f, " Experimental_Certnames");
+#endif
fprintf(f, "\n");
fprintf(f, "Lookups (built-in):");
diff --git a/src/src/functions.h b/src/src/functions.h
index 741b632a9..a6257a913 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -59,10 +59,13 @@ extern int tls_ungetc(int);
extern int tls_write(BOOL, const uschar *, size_t);
extern uschar *tls_validate_require_cipher(void);
extern void tls_version_report(FILE *);
-#ifndef USE_GNUTLS
+# ifndef USE_GNUTLS
extern BOOL tls_openssl_options_parse(uschar *, long *);
-#endif
+# endif
extern uschar * tls_field_from_dn(uschar *, uschar *);
+# ifdef EXPERIMENTAL_CERTNAMES
+extern BOOL tls_is_name_for_cert(uschar *, void *);
+# endif
#endif /*SUPPORT_TLS*/
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index 3c926c0d4..af43686e4 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -60,7 +60,12 @@ Changes:
/* Values for verify_requirement */
-enum peer_verify_requirement { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED };
+enum peer_verify_requirement
+ { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED
+#ifdef EXPERIMENTAL_CERTNAMES
+ ,VERIFY_WITHHOST
+#endif
+ };
/* 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
@@ -95,6 +100,7 @@ typedef struct exim_gnutls_state {
const uschar *tls_verify_certificates;
const uschar *tls_crl;
const uschar *tls_require_ciphers;
+
uschar *exp_tls_certificate;
uschar *exp_tls_privatekey;
uschar *exp_tls_sni;
@@ -102,6 +108,9 @@ typedef struct exim_gnutls_state {
uschar *exp_tls_crl;
uschar *exp_tls_require_ciphers;
uschar *exp_tls_ocsp_file;
+#ifdef EXPERIMENTAL_CERTNAMES
+ uschar *exp_tls_verify_cert_hostnames;
+#endif
tls_support *tlsp; /* set in tls_init() */
@@ -117,6 +126,9 @@ static const exim_gnutls_state_st exim_gnutls_state_init = {
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+#ifdef EXPERIMENTAL_CERTNAMES
+ NULL,
+#endif
NULL,
NULL, 0, 0, 0, 0,
};
@@ -178,18 +190,18 @@ before, for now. */
#define expand_check_tlsvar(Varname) expand_check(state->Varname, US #Varname, &state->exp_##Varname)
#if GNUTLS_VERSION_NUMBER >= 0x020c00
-#define HAVE_GNUTLS_SESSION_CHANNEL_BINDING
-#define HAVE_GNUTLS_SEC_PARAM_CONSTANTS
-#define HAVE_GNUTLS_RND
+# define HAVE_GNUTLS_SESSION_CHANNEL_BINDING
+# define HAVE_GNUTLS_SEC_PARAM_CONSTANTS
+# define HAVE_GNUTLS_RND
/* The security fix we provide with the gnutls_allow_auto_pkcs11 option
* (4.82 PP/09) introduces a compatibility regression. The symbol simply
* isn't available sometimes, so this needs to become a conditional
* compilation; the sanest way to deal with this being a problem on
* older OSes is to block it in the Local/Makefile with this compiler
* definition */
-#ifndef AVOID_GNUTLS_PKCS11
-#define HAVE_GNUTLS_PKCS11
-#endif /* AVOID_GNUTLS_PKCS11 */
+# ifndef AVOID_GNUTLS_PKCS11
+# define HAVE_GNUTLS_PKCS11
+# endif /* AVOID_GNUTLS_PKCS11 */
#endif
@@ -294,10 +306,16 @@ tls_error(when, msg, state->host);
* Set various Exim expansion vars *
*************************************************/
-#define exim_gnutls_cert_err(Label) do { \
- if (rc != GNUTLS_E_SUCCESS) { \
- DEBUG(D_tls) debug_printf("TLS: cert problem: %s: %s\n", (Label), gnutls_strerror(rc)); \
- return rc; } } while (0)
+#define exim_gnutls_cert_err(Label) \
+ do \
+ { \
+ if (rc != GNUTLS_E_SUCCESS) \
+ { \
+ DEBUG(D_tls) debug_printf("TLS: cert problem: %s: %s\n", \
+ (Label), gnutls_strerror(rc)); \
+ return rc; \
+ } \
+ } while (0)
static int
import_cert(const gnutls_datum * cert, gnutls_x509_crt_t * crtp)
@@ -1220,7 +1238,7 @@ if (cert_list == NULL || cert_list_size == 0)
{
DEBUG(D_tls) debug_printf("TLS: no certificate from peer (%p & %d)\n",
cert_list, cert_list_size);
- if (state->verify_requirement == VERIFY_REQUIRED)
+ if (state->verify_requirement >= VERIFY_REQUIRED)
return tls_error(US"certificate verification failed",
"no certificate received from peer", state->host);
return OK;
@@ -1232,17 +1250,23 @@ if (ct != GNUTLS_CRT_X509)
const char *ctn = gnutls_certificate_type_get_name(ct);
DEBUG(D_tls)
debug_printf("TLS: peer cert not X.509 but instead \"%s\"\n", ctn);
- if (state->verify_requirement == VERIFY_REQUIRED)
+ if (state->verify_requirement >= VERIFY_REQUIRED)
return tls_error(US"certificate verification not possible, unhandled type",
ctn, state->host);
return OK;
}
-#define exim_gnutls_peer_err(Label) do { \
- if (rc != GNUTLS_E_SUCCESS) { \
- DEBUG(D_tls) debug_printf("TLS: peer cert problem: %s: %s\n", (Label), gnutls_strerror(rc)); \
- if (state->verify_requirement == VERIFY_REQUIRED) { return tls_error((Label), gnutls_strerror(rc), state->host); } \
- return OK; } } while (0)
+#define exim_gnutls_peer_err(Label) \
+ do { \
+ if (rc != GNUTLS_E_SUCCESS) \
+ { \
+ DEBUG(D_tls) debug_printf("TLS: peer cert problem: %s: %s\n", \
+ (Label), gnutls_strerror(rc)); \
+ if (state->verify_requirement >= VERIFY_REQUIRED) \
+ return tls_error((Label), gnutls_strerror(rc), state->host); \
+ return OK; \
+ } \
+ } while (0)
rc = import_cert(&cert_list[0], &crt);
exim_gnutls_peer_err(US"cert 0");
@@ -1306,7 +1330,9 @@ else
/* Handle the result of verification. INVALID seems to be set as well
as REVOKED, but leave the test for both. */
-if (rc < 0 || verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED))
+if (rc < 0 ||
+ verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)
+ )
{
state->peer_cert_verified = FALSE;
if (!*error)
@@ -1314,21 +1340,42 @@ if (rc < 0 || verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED))
? "certificate revoked" : "certificate invalid";
DEBUG(D_tls)
- debug_printf("TLS certificate verification failed (%s): peerdn=%s\n",
+ debug_printf("TLS certificate verification failed (%s): peerdn=\"%s\"\n",
*error, state->peerdn ? state->peerdn : US"<unset>");
- if (state->verify_requirement == VERIFY_REQUIRED)
+ if (state->verify_requirement >= VERIFY_REQUIRED)
{
- gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE);
+ gnutls_alert_send(state->session,
+ GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE);
return FALSE;
}
DEBUG(D_tls)
debug_printf("TLS verify failure overridden (host in tls_try_verify_hosts)\n");
}
+
else
{
+#ifdef EXPERIMENTAL_CERTNAMES
+ if (state->verify_requirement == VERIFY_WITHHOST)
+ {
+ int sep = 0;
+ uschar * list = state->exp_tls_verify_cert_hostnames;
+ uschar * name;
+ while (name = string_nextinlist(&list, &sep, NULL, 0))
+ if (gnutls_x509_crt_check_hostname(state->tlsp->peercert, CS name))
+ break;
+ if (!name)
+ {
+ 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;
+ }
+ }
+#endif
state->peer_cert_verified = TRUE;
- DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=%s\n",
+ DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=\"%s\"\n",
state->peerdn ? state->peerdn : US"<unset>");
}
@@ -1517,19 +1564,22 @@ optional, set up appropriately. */
if (verify_check_host(&tls_verify_hosts) == OK)
{
- DEBUG(D_tls) debug_printf("TLS: a client certificate will be required.\n");
+ DEBUG(D_tls)
+ debug_printf("TLS: a client certificate will be required.\n");
state->verify_requirement = VERIFY_REQUIRED;
gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUIRE);
}
else if (verify_check_host(&tls_try_verify_hosts) == OK)
{
- DEBUG(D_tls) debug_printf("TLS: a client certificate will be requested but not required.\n");
+ DEBUG(D_tls)
+ debug_printf("TLS: a client certificate will be requested but not required.\n");
state->verify_requirement = VERIFY_OPTIONAL;
gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUEST);
}
else
{
- DEBUG(D_tls) debug_printf("TLS: a client certificate will not be requested.\n");
+ DEBUG(D_tls)
+ debug_printf("TLS: a client certificate will not be requested.\n");
state->verify_requirement = VERIFY_NONE;
gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_IGNORE);
}
@@ -1699,19 +1749,40 @@ if (( state->exp_tls_verify_certificates
verify_check_host(&ob->tls_verify_hosts) == OK
)
{
- DEBUG(D_tls) debug_printf("TLS: server certificate verification required.\n");
- state->verify_requirement = VERIFY_REQUIRED;
+#ifdef EXPERIMENTAL_CERTNAMES
+ if (ob->tls_verify_cert_hostnames)
+ {
+ DEBUG(D_tls)
+ debug_printf("TLS: server cert incl. hostname verification required.\n");
+ state->verify_requirement = VERIFY_WITHHOST;
+ if (!expand_check(ob->tls_verify_cert_hostnames,
+ US"tls_verify_cert_hostnames",
+ &state->exp_tls_verify_cert_hostnames))
+ return FAIL;
+ if (state->exp_tls_verify_cert_hostnames)
+ DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
+ state->exp_tls_verify_cert_hostnames);
+ }
+ else
+#endif
+ {
+ 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)
{
- DEBUG(D_tls) debug_printf("TLS: server certificate verification optional.\n");
+ DEBUG(D_tls)
+ debug_printf("TLS: server certificate verification optional.\n");
state->verify_requirement = VERIFY_OPTIONAL;
gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUEST);
}
else
{
- DEBUG(D_tls) debug_printf("TLS: server certificate verification not required.\n");
+ DEBUG(D_tls)
+ debug_printf("TLS: server certificate verification not required.\n");
state->verify_requirement = VERIFY_NONE;
gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_IGNORE);
}
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 3000b8fcb..1d6b91470 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -23,7 +23,7 @@ functions from the OpenSSL library. */
#include <openssl/err.h>
#include <openssl/rand.h>
#ifdef EXPERIMENTAL_OCSP
-#include <openssl/ocsp.h>
+# include <openssl/ocsp.h>
#endif
#ifdef EXPERIMENTAL_OCSP
@@ -32,7 +32,7 @@ functions from the OpenSSL library. */
#endif
#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
-#define EXIM_HAVE_OPENSSL_TLSEXT
+# define EXIM_HAVE_OPENSSL_TLSEXT
#endif
/* Structure for collecting random data for seeding. */
@@ -107,6 +107,10 @@ 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
} tls_ext_ctx_cb;
/* should figure out a cleanup of API to handle state preserved per
@@ -268,8 +272,7 @@ verify_callback(int state, X509_STORE_CTX *x509ctx,
X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
static uschar txt[256];
-X509_NAME_oneline(X509_get_subject_name(cert),
- CS txt, sizeof(txt));
+X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
if (state == 0)
{
@@ -306,8 +309,43 @@ else if (X509_STORE_CTX_get_error_depth(x509ctx) != 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
+ {
+ int sep = 0;
+ uschar * list = verify_cert_hostnames;
+ uschar * name;
+ while (name = string_nextinlist(&list, &sep, NULL, 0))
+ if (X509_check_host(cert, name, 0, 0))
+ break;
+ if (!name)
+ {
+ log_write(0, LOG_MAIN,
+ "SSL verify error: certificate name mismatch: \"%s\"\n", txt);
+ return 0; /* reject */
+ }
+ }
+# else
+ if (!tls_is_name_for_cert(verify_cert_hostnames, cert))
+ {
+ log_write(0, LOG_MAIN,
+ "SSL verify error: certificate name mismatch: \"%s\"\n", txt);
+ return 0; /* reject */
+ }
+# endif
+#endif
+
DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
*calledp ? "" : " authenticated", txt);
if (!*calledp) tlsp->certificate_verified = TRUE;
@@ -955,8 +993,8 @@ return i;
* Initialize for TLS *
*************************************************/
-/* Called from both server and client code, to do preliminary initialization of
-the library.
+/* Called from both server and client code, to do preliminary initialization
+of the library. We allocate and return a context structure.
Arguments:
host connected host, if client; NULL if server
@@ -965,6 +1003,7 @@ Arguments:
privatekey private key
ocsp_file file of stapling info (server); flag for require ocsp (client)
addr address if client; NULL if server (for some randomness)
+ cbp place to put allocated context
Returns: OK/DEFER/FAIL
*/
@@ -1118,6 +1157,10 @@ else /* client */
# endif
#endif
+#ifdef EXPERIMENTAL_CERTNAMES
+cbinfo->verify_cert_hostnames = NULL;
+#endif
+
/* Set up the RSA callback */
SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback);
@@ -1545,6 +1588,7 @@ if (expciphers != NULL)
/* 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 */
+
if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) ||
(verify_check_host(&ob->tls_verify_hosts) == OK))
{
@@ -1552,6 +1596,19 @@ if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) ||
ob->tls_crl, host, FALSE, verify_callback_client)) != OK)
return rc;
client_verify_optional = FALSE;
+
+#ifdef EXPERIMENTAL_CERTNAMES
+ if (ob->tls_verify_cert_hostnames)
+ {
+ if (!expand_check(ob->tls_verify_cert_hostnames,
+ US"tls_verify_cert_hostnames",
+ &client_static_cbinfo->verify_cert_hostnames))
+ return FAIL;
+ if (client_static_cbinfo->verify_cert_hostnames)
+ DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
+ client_static_cbinfo->verify_cert_hostnames);
+ }
+#endif
}
else if (verify_check_host(&ob->tls_try_verify_hosts) == OK)
{
diff --git a/src/src/tls.c b/src/src/tls.c
index 75c46e9e4..b5ef96595 100644
--- a/src/src/tls.c
+++ b/src/src/tls.c
@@ -255,6 +255,80 @@ return list;
}
+#ifdef EXPERIMENTAL_CERTNAMES
+/* Compare a domain name with a possibly-wildcarded name. Wildcards
+are restricted to a single one, as the first element of patterns
+having at least three dot-separated elements. Case-independent.
+Return TRUE for a match
+*/
+static BOOL
+is_name_match(const uschar * name, const uschar * pat)
+{
+uschar * cp;
+return *pat == '*' /* possible wildcard match */
+ ? *++pat == '.' /* starts star, dot */
+ && !Ustrchr(++pat, '*') /* has no more stars */
+ && Ustrchr(pat, '.') /* and has another dot. */
+ && (cp = Ustrchr(name, '.'))/* The name has at least one dot */
+ && strcmpic(++cp, pat) == 0 /* and we only compare after it. */
+ : !Ustrchr(pat+1, '*')
+ && strcmpic(name, pat) == 0;
+}
+
+/* Compare a list of names with the dnsname elements
+of the Subject Alternate Name, if any, and the
+Subject otherwise.
+
+Arguments:
+ namelist names to compare
+ cert certificate
+
+Returns:
+ TRUE/FALSE
+*/
+
+BOOL
+tls_is_name_for_cert(uschar * namelist, void * cert)
+{
+uschar * altnames = tls_cert_subject_altname(cert, US"dns");
+uschar * subjdn;
+uschar * certname;
+int cmp_sep = 0;
+uschar * cmpname;
+
+if ((altnames = tls_cert_subject_altname(cert, US"dns")))
+ {
+ int alt_sep = '\n';
+ while (cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0))
+ {
+ uschar * an = altnames;
+ while (certname = string_nextinlist(&an, &alt_sep, NULL, 0))
+ if (is_name_match(cmpname, certname))
+ return TRUE;
+ }
+ }
+
+else if ((subjdn = tls_cert_subject(cert, NULL)))
+ {
+ int sn_sep = ',';
+ uschar * sn;
+
+ dn_to_list(subjdn);
+ while (cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0))
+ {
+ uschar * sn = subjdn;
+ while (certname = string_nextinlist(&sn, &sn_sep, NULL, 0))
+ if ( *certname++ == 'C'
+ && *certname++ == 'N'
+ && *certname++ == '='
+ && is_name_match(cmpname, certname)
+ )
+ return TRUE;
+ }
+ }
+return FALSE;
+}
+#endif
/* vi: aw ai sw=2
*/
diff --git a/src/src/tlscert-gnu.c b/src/src/tlscert-gnu.c
index 9b9c83d8b..737637302 100644
--- a/src/src/tlscert-gnu.c
+++ b/src/src/tlscert-gnu.c
@@ -268,7 +268,7 @@ for(index = 0;; index++)
{
siz = 0;
switch(ret = gnutls_x509_crt_get_subject_alt_name(
- (gnutls_x509_crt_t)cert, index, NULL, &siz, NULL))
+ (gnutls_x509_crt_t)cert, index, NULL, &siz, NULL))
{
case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE:
return list; /* no more elements; normal exit */
@@ -286,7 +286,8 @@ for(index = 0;; index++)
return g_err("gs1", __FUNCTION__, ret);
ele[siz] = '\0';
- if (match != -1 && match != ret)
+ if ( match != -1 && match != ret /* wrong type of SAN */
+ || Ustrlen(ele) != siz) /* contains a NUL */
continue;
switch (ret)
{
diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c
index 29095782a..0614b4025 100644
--- a/src/src/tlscert-openssl.c
+++ b/src/src/tlscert-openssl.c
@@ -237,6 +237,7 @@ uschar sep = '\n';
uschar * tag = US"";
uschar * ele;
int match = -1;
+int len;
if (!san) return NULL;
@@ -262,19 +263,26 @@ while (sk_GENERAL_NAME_num(san) > 0)
case GEN_DNS:
tag = US"DNS";
ele = ASN1_STRING_data(namePart->d.dNSName);
+ len = ASN1_STRING_length(namePart->d.dNSName);
break;
case GEN_URI:
tag = US"URI";
ele = ASN1_STRING_data(namePart->d.uniformResourceIdentifier);
+ len = ASN1_STRING_length(namePart->d.uniformResourceIdentifier);
break;
case GEN_EMAIL:
tag = US"MAIL";
ele = ASN1_STRING_data(namePart->d.rfc822Name);
+ len = ASN1_STRING_length(namePart->d.rfc822Name);
break;
default:
continue; /* ignore unrecognised types */
}
- list = string_append_listele(list, sep,
+ if (ele[len]) /* not nul-terminated */
+ ele = string_copyn(ele, len);
+
+ if (strnlen(CS ele, len) == len) /* ignore any with embedded nul */
+ list = string_append_listele(list, sep,
match == -1 ? string_sprintf("%s=%s", tag, ele) : ele);
}
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index 71a9f2376..c175d2ffe 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -163,6 +163,10 @@ optionlist smtp_transport_options[] = {
(void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) },
{ "tls_try_verify_hosts", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_try_verify_hosts) },
+#ifdef EXPERIMENTAL_CERTNAMES
+ { "tls_verify_cert_hostnames", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block,tls_verify_cert_hostnames)},
+#endif
{ "tls_verify_certificates", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_verify_certificates) },
{ "tls_verify_hosts", opt_stringptr,
@@ -245,6 +249,9 @@ smtp_transport_options_block smtp_transport_option_defaults = {
TRUE, /* tls_tempfail_tryclear */
NULL, /* tls_verify_hosts */
NULL /* tls_try_verify_hosts */
+# ifdef EXPERIMENTAL_CERTNAMES
+ ,NULL /* tls_verify_cert_hostnames */
+# endif
#endif
#ifndef DISABLE_DKIM
,NULL, /* dkim_canon */
diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h
index c7de0091a..a481943bb 100644
--- a/src/src/transports/smtp.h
+++ b/src/src/transports/smtp.h
@@ -55,7 +55,7 @@ typedef struct {
BOOL keepalive;
BOOL lmtp_ignore_quota;
BOOL retry_include_ip_address;
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
uschar *tls_certificate;
uschar *tls_crl;
uschar *tls_privatekey;
@@ -69,18 +69,21 @@ typedef struct {
BOOL tls_tempfail_tryclear;
uschar *tls_verify_hosts;
uschar *tls_try_verify_hosts;
- #endif
- #ifndef DISABLE_DKIM
+# ifdef EXPERIMENTAL_CERTNAMES
+ uschar *tls_verify_cert_hostnames;
+# endif
+#endif
+#ifndef DISABLE_DKIM
uschar *dkim_domain;
uschar *dkim_private_key;
uschar *dkim_selector;
uschar *dkim_canon;
uschar *dkim_sign_headers;
uschar *dkim_strict;
- #endif
- #ifdef EXPERIMENTAL_TPDA
+#endif
+#ifdef EXPERIMENTAL_TPDA
uschar *tpda_host_defer_action;
- #endif
+#endif
} smtp_transport_options_block;
/* Data for reading the private options. */