Docs: clarify TLS cert name verification
[exim.git] / src / src / tls.c
index 32b29ee3e4ded6062bb64dae30400ca092700393..8f4344c6c56b4ca45bb2624853d023e0e132cee1 100644 (file)
@@ -5,6 +5,7 @@
 /* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* This module provides TLS (aka SSL) support for Exim. The code for OpenSSL is
 based on a patch that was originally contributed by Steve Haslam. It was
@@ -25,11 +26,6 @@ functions from the OpenSSL or GNU TLS libraries. */
 #endif
 
 
-/* Forward decl. */
-static void tls_client_resmption_key(tls_support *, smtp_connect_args *,
-  smtp_transport_options_block *);
-
-
 #if defined(MACRO_PREDEF) && !defined(DISABLE_TLS)
 # include "macro_predef.h"
 # ifdef USE_GNUTLS
@@ -105,7 +101,8 @@ Returns:    TRUE if OK; result may still be NULL after forced failure
 */
 
 static BOOL
-expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr)
+expand_check(const uschar * s, const uschar * name,
+  uschar ** result, uschar ** errstr)
 {
 if (!s)
   *result = NULL;
@@ -361,6 +358,8 @@ tls_watch_invalidate();
 #endif
 
 tls_server_creds_invalidate();
+
+/* _expire is for a time-limited selfsign server cert */
 tls_creds_expire = (lifetime = tls_server_creds_init())
   ? time(NULL) + lifetime : 0;
 
@@ -455,6 +454,10 @@ tzset();
 /*************************************************
 *        Many functions are package-specific     *
 *************************************************/
+/* Forward decl. */
+static void tls_client_resmption_key(tls_support *, smtp_connect_args *,
+  smtp_transport_options_block *);
+
 
 #ifdef USE_GNUTLS
 # include "tls-gnu.c"
@@ -667,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");
     }
   }
 
@@ -693,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;
@@ -849,6 +860,57 @@ DEBUG(D_tls) debug_printf("TLS: resume session index %s\n", tlsp->resume_index);
 #endif
 }
 
+
+
+/* Start TLS as a client for an ajunct connection, eg. readsocket
+Return boolean success.
+*/
+
+BOOL
+tls_client_adjunct_start(host_item * host, client_conn_ctx * cctx,
+  const uschar * sni, uschar ** errmsg)
+{
+union sockaddr_46 interface_sock;
+EXIM_SOCKLEN_T size = sizeof(interface_sock);
+smtp_connect_args conn_args = {.host = host };
+tls_support tls_dummy = { .sni = NULL };
+uschar * errstr;
+
+if (getsockname(cctx->sock, (struct sockaddr *) &interface_sock, &size) == 0)
+  conn_args.sending_ip_address = host_ntoa(-1, &interface_sock, NULL, NULL);
+else
+  {
+  *errmsg = string_sprintf("getsockname failed: %s", strerror(errno));
+  return FALSE;
+  }
+
+/* To handle SNI we need to emulate more of a real transport because the
+base tls code assumes that is where the SNI string lives. */
+
+if (*sni)
+  {
+  transport_instance * tb;
+  smtp_transport_options_block * ob;
+
+  conn_args.tblock = tb = store_get(sizeof(*tb), GET_UNTAINTED);
+  memset(tb, 0, sizeof(*tb));
+
+  tb->options_block = ob = store_get(sizeof(*ob), GET_UNTAINTED);
+  memcpy(ob, &smtp_transport_option_defaults, sizeof(*ob));
+
+  ob->tls_sni = sni;
+  }
+
+if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr))
+  {
+  *errmsg = string_sprintf("TLS connect failed: %s", errstr);
+  return FALSE;
+  }
+return TRUE;
+}
+
+
+
 #endif /*!DISABLE_TLS*/
 #endif /*!MACRO_PREDEF*/