[exim-cvs] SNI for ${readsocket }

Top Page
Delete this message
Reply to this message
Author: Exim Git Commits Mailing List
Date:  
To: exim-cvs
Subject: [exim-cvs] SNI for ${readsocket }
Gitweb: https://git.exim.org/exim.git/commitdiff/6fdf76d0eae42ce4507fe317f095572100c5d6b8
Commit:     6fdf76d0eae42ce4507fe317f095572100c5d6b8
Parent:     8ff2ba119ba654e9238f157f94bf10ed640ed877
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Mon Mar 13 00:43:01 2023 +0000
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Mon Mar 13 15:41:09 2023 +0000


    SNI for ${readsocket }
---
 doc/doc-docbook/spec.xfpt        |  5 ++++
 doc/doc-txt/NewStuff             |  2 ++
 src/src/functions.h              |  2 ++
 src/src/lookups/readsock.c       | 31 ++++++------------------
 src/src/tls.c                    | 51 ++++++++++++++++++++++++++++++++++++++++
 test/confs/1149                  |  2 ++
 test/log/1149                    |  1 +
 test/scripts/1100-Basic-TLS/1149 |  1 +
 test/stdout/1149                 |  1 +
 9 files changed, 72 insertions(+), 24 deletions(-)


diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 0ba62ce5e..bdc0eb30e 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -10495,6 +10495,11 @@ Defines whether or not a write-shutdown is done on the connection after
sending the request. Values are &"yes"& (the default) or &"no"&
(preferred, eg. by some webservers).

+.next
+&*sni*&
+Controls the use of Server Name Identification on the connection.
+Any nonempty value will be the SNI sent; TLS will be forced.
+
.next
&*tls*&
Controls the use of TLS on the connection.
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index d12246e03..84db8ea58 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -29,6 +29,8 @@ Version 4.97

10. A commandline option to print just the message IDs of the queue

+ 11. An option for the ${readsocket } expansion to set an SNI for TLS.
+
Version 4.96
------------

diff --git a/src/src/functions.h b/src/src/functions.h
index 5fbb426ec..896122a69 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -54,6 +54,8 @@ extern uschar * tls_cert_fprt_sha256(void *);
 extern void    tls_clean_env(void);
 extern BOOL    tls_client_start(client_conn_ctx *, smtp_connect_args *,
           void *, tls_support *, uschar **);
+extern BOOL    tls_client_adjunct_start(host_item *, client_conn_ctx *,
+          const uschar *, uschar **);
 extern void    tls_client_creds_reload(BOOL);


 extern void    tls_close(void *, int);
diff --git a/src/src/lookups/readsock.c b/src/src/lookups/readsock.c
index b1ea42c7f..a3f87108a 100644
--- a/src/src/lookups/readsock.c
+++ b/src/src/lookups/readsock.c
@@ -13,7 +13,7 @@


static int
internal_readsock_open(client_conn_ctx * cctx, const uschar * sspec,
- int timeout, BOOL do_tls, uschar ** errmsg)
+ int timeout, uschar * do_tls, uschar ** errmsg)
{
const uschar * server_name;
host_item host;
@@ -116,27 +116,8 @@ else

 #ifndef DISABLE_TLS
 if (do_tls)
-  {
-  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));
+  if (!tls_client_adjunct_start(&host, cctx, do_tls, errmsg))
     goto bad;
-    }
-
-  if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr))
-    {
-    *errmsg = string_sprintf("TLS connect failed: %s", errstr);
-    goto bad;
-    }
-  }
 #endif


 DEBUG(D_expand|D_lookup) debug_printf_indent("  connected to socket %s\n", sspec);
@@ -187,8 +168,8 @@ client_conn_ctx * cctx = handle;
 int sep = ',';
 struct {
     BOOL do_shutdown:1;
-    BOOL do_tls:1;
     BOOL cache:1;
+    uschar * do_tls;    /* NULL, empty-string, or SNI */
 } lf = {.do_shutdown = TRUE};
 uschar * eol = NULL;
 int timeout = 5;
@@ -207,8 +188,10 @@ if (opts) for (uschar * s; s = string_nextinlist(&opts, &sep, NULL, 0); )
   else if (Ustrncmp(s, "shutdown=", 9) == 0)
     lf.do_shutdown = Ustrcmp(s + 9, "no") != 0;
 #ifndef DISABLE_TLS
-  else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0)
-    lf.do_tls = TRUE;
+  else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0 && !lf.do_tls)
+    lf.do_tls = US"";
+  else if (Ustrncmp(s, "sni=", 4) == 0)
+    lf.do_tls = s + 4;
 #endif
   else if (Ustrncmp(s, "eol=", 4) == 0)
     eol = string_unprinting(s + 4);
diff --git a/src/src/tls.c b/src/src/tls.c
index ba7c2de38..825313a9a 100644
--- a/src/src/tls.c
+++ b/src/src/tls.c
@@ -852,6 +852,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*/


diff --git a/test/confs/1149 b/test/confs/1149
index 737753109..dfe5b33cb 100644
--- a/test/confs/1149
+++ b/test/confs/1149
@@ -13,4 +13,6 @@ tls_advertise_hosts = *
tls_certificate = DIR/aux-fixed/cert1
tls_privatekey = DIR/aux-fixed/cert1

+acl_smtp_helo = accept logwrite = HELO <$sender_helo_name> SNI <$tls_in_sni>
+
# End
diff --git a/test/log/1149 b/test/log/1149
index bc6ac7f77..efcef1b23 100644
--- a/test/log/1149
+++ b/test/log/1149
@@ -1,3 +1,4 @@

******** SERVER ********
1999-03-02 09:44:33 exim x.yz daemon started: pid=p1234, no queue runs, listening for SMTPS on port PORT_D
+1999-03-02 09:44:33 HELO <tester> SNI <fubar>
diff --git a/test/scripts/1100-Basic-TLS/1149 b/test/scripts/1100-Basic-TLS/1149
index 501a9c9da..29702b049 100644
--- a/test/scripts/1100-Basic-TLS/1149
+++ b/test/scripts/1100-Basic-TLS/1149
@@ -8,6 +8,7 @@ exim -DSERVER=server -tls-on-connect -bd -oX PORT_D
millisleep 500
exim -be
1 >>${readsocket{inet:thisloop:PORT_D}{QUIT\n}{2s:tls=yes}}<<
+2 >>${readsocket{inet:thisloop:PORT_D}{EHLO tester\n}{1s:tls=yes:sni=fubar}}<<
****
millisleep 500
#
diff --git a/test/stdout/1149 b/test/stdout/1149
index a3eab5117..c67e31cfe 100644
--- a/test/stdout/1149
+++ b/test/stdout/1149
@@ -1,4 +1,5 @@
> 1 >>220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000

221 myhost.test.ex closing connection
<<
+> Failed: socket read timed out
>