[exim-cvs] TLS: as server, reject connections with ALPN ind…

Startseite
Nachricht löschen
Nachricht beantworten
Autor: Exim Git Commits Mailing List
Datum:  
To: exim-cvs
Betreff: [exim-cvs] TLS: as server, reject connections with ALPN indicating non-smtp use
Gitweb: https://git.exim.org/exim.git/commitdiff/f50a063dc0b96ac95b3a7bc0aebad3b3f2534c02
Commit:     f50a063dc0b96ac95b3a7bc0aebad3b3f2534c02
Parent:     dab495e070dc243e03e146dc46ddbb3edba4f7ce
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Tue Jun 22 23:04:59 2021 +0100
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Tue Jun 22 23:04:59 2021 +0100


    TLS: as server, reject connections with ALPN indicating non-smtp use
---
 doc/doc-txt/ChangeLog |  2 ++
 src/src/tls-gnu.c     | 72 ++++++++++++++++++++++++++++++++++++++++++++++++---
 src/src/tls-openssl.c | 54 +++++++++++++++++++++++++++++++++++---
 3 files changed, 122 insertions(+), 6 deletions(-)


diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index 59211fb..90645e0 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -314,6 +314,8 @@ JH/54 DMARC: recent versions of the OpenDMARC library appear to have broken
       This affects 1.4.1-1 on Fedora 33 (1.3.2-3 is functional); and has
       been reported on other platforms.


+JH/55 TLS: as server, reject connections with ALPN indicating non-smtp use.
+

Exim version 4.94
-----------------
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index 954fd76..1affba3 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -119,6 +119,10 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
# endif
#endif

+#if GNUTLS_VERSION_NUMBER >= 0x030200
+# define EXIM_HAVE_ALPN
+#endif
+
#ifndef DISABLE_OCSP
# include <gnutls/ocsp.h>
#endif
@@ -278,10 +282,14 @@ static BOOL gnutls_buggy_ocsp = FALSE;
static BOOL exim_testharness_disable_ocsp_validity_check = FALSE;
#endif

+#ifdef EXIM_HAVE_ALPN
+static BOOL server_seen_alpn = FALSE;
+#endif
#ifdef EXIM_HAVE_TLS_RESUME
static gnutls_datum_t server_sessticket_key;
#endif

+
/* ------------------------------------------------------------------------ */
/* macros */

@@ -1062,10 +1070,18 @@ tls_server_clienthello_ext(void * ctx, unsigned tls_id,
   const unsigned char *data, unsigned size)
 {
 /* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml */
-if (tls_id == 5)    /* status_request */
+switch (tls_id)
   {
-  DEBUG(D_tls) debug_printf("Seen status_request extension from client\n");
-  tls_in.ocsp = OCSP_NOT_RESP;
+  case 5:    /* status_request */
+    DEBUG(D_tls) debug_printf("Seen status_request extension from client\n");
+    tls_in.ocsp = OCSP_NOT_RESP;
+    break;
+#ifdef EXIM_HAVE_ALPN
+  case 16:    /* Application Layer Protocol Notification */
+    DEBUG(D_tls) debug_printf("Seen ALPN extension from client\n");
+    server_seen_alpn = TRUE;
+    break;
+#endif
   }
 return 0;
 }
@@ -2791,6 +2807,26 @@ if (gnutls_session_is_resumed(state->session))
   }
 }
 #endif
+
+
+#ifdef EXIM_HAVE_ALPN
+static void
+tls_server_set_acceptable_alpns(exim_gnutls_state_st * state)
+{
+int rc;
+gnutls_datum_t protocols[2] = {[0] = {.data = US"smtp", .size = 4},
+                   [1] = {.data = US"esmtp", .size = 5}};
+
+/* Set non-mandatory set of protocol names */
+if (!(rc = gnutls_alpn_set_protocols(state->session, protocols, 2, 0)))
+  gnutls_handshake_set_hook_function(state->session,
+    GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, tls_server_hook_cb);
+else
+  DEBUG(D_tls)
+    debug_printf("setting alpn protocols: %s\n", US gnutls_strerror(rc));
+}
+#endif
+
 /* ------------------------------------------------------------------------ */
 /* Exported functions */


@@ -2847,6 +2883,10 @@ DEBUG(D_tls) debug_printf("initialising GnuTLS as a server\n");
#endif
}

+#ifdef EXIM_HAVE_ALPN
+tls_server_set_acceptable_alpns(state);
+#endif
+
#ifdef EXIM_HAVE_TLS_RESUME
tls_server_resume_prehandshake(state);
#endif
@@ -2962,6 +3002,32 @@ tls_server_resume_posthandshake(state);

DEBUG(D_tls) post_handshake_debug(state);

+#ifdef EXIM_HAVE_ALPN
+if (server_seen_alpn)
+  {
+  /* The client offered ALPN.  We were set up with a nonmandatory list;
+  see what was negotiated.  We require a match now, given that something
+  was offered. */
+  gnutls_datum_t p = {.size = 0};
+  int rc = gnutls_alpn_get_selected_protocol(state->session, &p);
+  if (!rc || rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+    {
+    if (p.size == 0)
+      {
+      *errstr = US"ALPN rejected";
+      return FAIL;
+      }
+    else
+      DEBUG(D_tls)
+    debug_printf("ALPN negotiated: %.*s\n", (int)p.size, p.data);
+    }
+  else
+    DEBUG(D_tls)
+      debug_printf("getting alpn protocol: %s\n", US gnutls_strerror(rc));
+
+  }
+#endif
+
 /* Verify after the fact */


 if (!verify_certificate(state, errstr))
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index cc72b2e..9692033 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -48,6 +48,7 @@ functions from the OpenSSL library. */
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
 # define EXIM_HAVE_OCSP_RESP_COUNT
 # define OPENSSL_AUTO_SHA256
+# define EXIM_HAVE_ALPN
 #else
 # define EXIM_HAVE_EPHEM_RSA_KEX
 # define EXIM_HAVE_RAND_PSEUDO
@@ -418,9 +419,6 @@ setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host,
     uschar ** errstr );


/* Callbacks */
-#ifdef EXIM_HAVE_OPENSSL_TLSEXT
-static int tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg);
-#endif
#ifndef DISABLE_OCSP
static int tls_server_stapling_cb(SSL *s, void *arg);
#endif
@@ -2137,6 +2135,53 @@ bad: return SSL_TLSEXT_ERR_ALERT_FATAL;



+#ifdef EXIM_HAVE_ALPN
+/*************************************************
+*        Callback to handle ALPN                 *
+*************************************************/
+
+/* SSL_CTX_set_alpn_select_cb() */
+/* Called on server when client offers ALPN, after the SNI callback.
+If set and not e?smtp then we dump the connection */
+
+static int
+tls_server_alpn_cb(SSL *ssl, const uschar ** out, uschar * outlen,
+  const uschar * in, unsigned int inlen, void * arg)
+{
+const exim_openssl_state_st * state = arg;
+
+DEBUG(D_tls)
+  {
+  debug_printf("Received TLS ALPN offer:");
+  for (int pos = 0, siz; pos < inlen; pos += siz+1)
+    {
+    siz = in[pos];
+    if (pos + 1 + siz > inlen) siz = inlen - pos - 1;
+    debug_printf(" '%.*s'", siz, in + pos + 1);
+    }
+  debug_printf("\n");
+  }
+
+/* Look for an acceptable ALPN */
+if (  inlen > 1        /* at least one name */
+   && in[0]+1 == inlen    /* filling the vector, so exactly one name */
+   && (  Ustrncmp(in+1, "smtp", in[0]) == 0
+      || Ustrncmp(in+1, "esmtp", in[0]) == 0
+   )  )
+  {
+  *out = in;            /* we checked for exactly one, so can just point to it */
+  *outlen = inlen;
+  return SSL_TLSEXT_ERR_OK;    /* use ALPN */
+  }
+
+/* Reject unacceptable ALPN */
+/* This will be fatal to the TLS conn; would be nice to kill TCP also */
+return SSL_TLSEXT_ERR_ALERT_FATAL;
+}
+#endif    /* EXIM_HAVE_ALPN */
+
+
+
 #ifndef DISABLE_OCSP


 /*************************************************
@@ -2604,6 +2649,9 @@ if (!host)        /* server */
   tls_certificate */
   SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb);
   SSL_CTX_set_tlsext_servername_arg(ctx, state);
+# ifdef EXIM_HAVE_ALPN
+  SSL_CTX_set_alpn_select_cb(ctx, tls_server_alpn_cb, state);
+# endif
   }
 # ifndef DISABLE_OCSP
 else            /* client */