.table2
.row &%gnutls_compat_mode%& "use GnuTLS compatibility mode"
.row &%gnutls_allow_auto_pkcs11%& "allow GnuTLS to autoload PKCS11 modules"
+.row &%hosts_require_alpn%& "mandatory ALPN"
.row &%openssl_options%& "adjust OpenSSL compatibility options"
.row &%tls_advertise_hosts%& "advertise TLS to these hosts"
+.row &%tls_alpn%& "acceptable protocol names"
.row &%tls_certificate%& "location of server certificate"
.row &%tls_crl%& "certificate revocation list"
.row &%tls_dh_max_bits%& "clamp D-H bit count suggestion"
If the &%smtp_connection%& log selector is not set, this option has no effect.
+.new
+.option hosts_require_alpn main "host list&!!" unset
+.cindex ALPN "require negotiation in server"
+.cindex TLS ALPN
+.cindex TLS "Application Layer Protocol Names"
+If the TLS library supports ALPN
+then a successful negotiation of ALPN will be required for any client
+matching the list, for TLS to be used.
+See also the &%tls_alpn%& option.
+
+&*Note*&: prevention of fallback to in-clear connection is not
+managed by this option, and should be done separately.
+.wen
+
.option hosts_proxy main "host list&!!" unset
.cindex proxy "proxy protocol"
is not required the &%tls_advertise_hosts%& option should be set empty.
+.new
+.option tls_alpn main "string list&!!" "smtp : esmtp"
+.cindex TLS "Application Layer Protocol Names"
+.cindex TLS ALPN
+.cindex ALPN "set acceptable names for server"
+If this option is set,
+the TLS library supports ALPN,
+and the client offers either more than
+ALPN name or a name which does not match the list,
+the TLS connection is declined.
+.wen
+
+
.option tls_certificate main "string list&!!" unset
.cindex "TLS" "server certificate; location of"
.cindex "certificate" "server, location of"
TLS session for any host that matches this list.
&%tls_verify_certificates%& should also be set for the transport.
+.new
+.option hosts_require_alpn smtp "host list&!!" unset
+.cindex ALPN "require negotiation in client"
+.cindex TLS ALPN
+.cindex TLS "Application Layer Protocol Names"
+If the TLS library supports ALPN
+then a successful negotiation of ALPN will be required for any host
+matching the list, for TLS to be used.
+See also the &%tls_alpn%& option.
+
+&*Note*&: prevention of fallback to in-clear connection is not
+managed by this option; see &%hosts_require_tls%&.
+.wen
+
.option hosts_require_dane smtp "host list&!!" unset
.cindex DANE "transport options"
.cindex DANE "requiring for certain servers"
transport. For details see section &<<SECTproxySOCKS>>&.
+.new
+.option tls_alpn smtp string&!! unset
+.cindex TLS "Application Layer Protocol Names"
+.cindex TLS ALPN
+.cindex ALPN "set name in client"
+If this option is set
+and the TLS library supports ALPN,
+the value given is used.
+
+As of writing no value has been standardised for email use.
+The authors suggest using &"smtp"&.
+.wen
+
+
+
.option tls_certificate smtp string&!! unset
.cindex "TLS" "client certificate, location of"
.cindex "certificate" "client, location of"
0.5.10. (Its presence predates the current API which Exim uses, so if Exim
built, then you have SNI support).
+.new
+.cindex TLS ALPN
+.cindex ALPN "general information"
+.cindex TLS "Application Layer Protocol Names"
+There is a TLS feature related to SNI
+called Application Layer Protocol Name (ALPN).
+This is intended to declare, or select, what protocol layer will be using a TLS
+connection.
+The client for the connection proposes a set of protocol names, and
+the server responds with a selected one.
+It is not, as of 2021, commonly used for SMTP connections.
+However, to guard against misirected or malicious use of web clients
+(which often do use ALPN) against MTA ports, Exim by default check that
+there is no incompatible ALPN specified by a client for a TLS connection.
+If there is, the connection is rejected.
+
+As a client Exim does not supply ALPN by default.
+The behaviour of both client and server can be configured using the options
+&%tls_alpn%& and &%hosts_require_alpn%&.
+There are no variables providing observability.
+Some feature-specific logging may appear on denied connections, but this
+depends on the behavious of the peer
+(not all peers can send a feature-specific TLS Alert).
+
+This feature is available when Exim is built with
+OpenSSL 1.1.0 or later or GnuTLS 3.2.0 or later.
+.wen
+
.section "Multiple messages on the same encrypted TCP/IP connection" &&&
16. Main option "hosts_require_helo", requiring HELO or EHLO before MAIL.
17. A main config option "allow_insecure_tainted_data" allows to turn
- taint errors into warnings.
+
+18. TLS ALPN handling. By default, refuse TLS connections that try to specify
+ a non-smtp (eg. http) use. Options for customising.
Version 4.94
hosts_randomize boolean false manualroute 4.00
false smtp 3.14
hosts_require_auth host list unset smtp 4.00
+hosts_require_alpn host list unset main 4.95
+ smtp 4.95
hosts_require_dane host list unset smtp 4.91 (4.85 experimental)
hosts_require_helo host list "*" main 4.95
hosts_require_ocsp host list unset smtp 4.82 if experimental_ocsp
timeout_frozen_after time 0s main 3.20
timezone string + main 3.15
tls_advertise_hosts host list * main 3.20
-tls_advertise_requiretls host list * main 4.92 if experimental_requiretls
+tls_alpn string* unset main 4.95
+ smtp 4.95
tls_certificate string* unset main 3.20
unset smtp 3.20
tls_dh_max_bits integer 2236 main 4.80
#ifndef DISABLE_TLS
BOOL gnutls_compat_mode = FALSE;
BOOL gnutls_allow_auto_pkcs11 = FALSE;
+uschar *hosts_require_alpn = NULL;
uschar *openssl_options = NULL;
const pcre *regex_STARTTLS = NULL;
uschar *tls_advertise_hosts = US"*";
+uschar *tls_alpn = US"smtp:esmtp";
uschar *tls_certificate = NULL;
uschar *tls_crl = NULL;
/* This default matches NSS DH_MAX_P_BITS value at current time (2012), because
#ifndef DISABLE_TLS
extern BOOL gnutls_compat_mode; /* Less security, more compatibility */
extern BOOL gnutls_allow_auto_pkcs11; /* Let GnuTLS autoload PKCS11 modules */
+extern uschar *hosts_require_alpn; /* Mandatory ALPN successful nogitiation */
extern uschar *openssl_options; /* OpenSSL compatibility options */
extern const pcre *regex_STARTTLS; /* For recognizing STARTTLS settings */
+extern uschar *tls_alpn; /* ALPN names acceptable */
extern uschar *tls_certificate; /* Certificate file */
extern uschar *tls_crl; /* CRL File */
extern int tls_dh_max_bits; /* don't accept higher lib suggestions */
#ifdef SUPPORT_PROXY
{ "hosts_proxy", opt_stringptr, {&hosts_proxy} },
#endif
+ { "hosts_require_alpn", opt_stringptr, {&hosts_require_alpn} },
{ "hosts_require_helo", opt_stringptr, {&hosts_require_helo} },
{ "hosts_treat_as_local", opt_stringptr, {&hosts_treat_as_local} },
#ifdef LOOKUP_IBASE
{ "timezone", opt_stringptr, {&timezone_string} },
{ "tls_advertise_hosts", opt_stringptr, {&tls_advertise_hosts} },
#ifndef DISABLE_TLS
+ { "tls_alpn", opt_stringptr, {&tls_alpn} },
{ "tls_certificate", opt_stringptr, {&tls_certificate} },
{ "tls_crl", opt_stringptr, {&tls_crl} },
{ "tls_dh_max_bits", opt_int, {&tls_dh_max_bits} },
#endif
#ifdef EXIM_HAVE_ALPN
-static BOOL server_seen_alpn = FALSE;
+static int server_seen_alpn = -1; /* count of names */
#endif
#ifdef EXIM_HAVE_TLS_RESUME
static gnutls_datum_t server_sessticket_key;
static int
-tls_error_gnu(const uschar *prefix, int err, const host_item *host,
+tls_error_gnu(exim_gnutls_state_st * state, const uschar *prefix, int err,
uschar ** errstr)
{
-return tls_error(prefix, US gnutls_strerror(err), host, errstr);
+return tls_error(prefix,
+ state && err == GNUTLS_E_FATAL_ALERT_RECEIVED
+ ? US gnutls_alert_get_name(gnutls_alert_get(state->session))
+ : US gnutls_strerror(err),
+ state ? state->host : NULL,
+ errstr);
}
static int
if (!gnutls_allow_auto_pkcs11)
if ((rc = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL)))
- return tls_error_gnu(US"gnutls_pkcs11_init", rc, NULL, errstr);
+ return tls_error_gnu(NULL, US"gnutls_pkcs11_init", rc, errstr);
#endif
#ifndef GNUTLS_AUTO_GLOBAL_INIT
if ((rc = gnutls_global_init()))
- return tls_error_gnu(US"gnutls_global_init", rc, NULL, errstr);
+ return tls_error_gnu(UNULL, S"gnutls_global_init", rc, errstr);
#endif
#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0
DEBUG(D_tls) debug_printf("Initialising GnuTLS server params\n");
if ((rc = gnutls_dh_params_init(&dh_server_params)))
- return tls_error_gnu(US"gnutls_dh_params_init", rc, host, errstr);
+ return tls_error_gnu(NULL, US"gnutls_dh_params_init", rc, errstr);
if (!expand_check(tls_dhparam, US"tls_dhparam", &exp_tls_dhparam, errstr))
return DEFER;
if (m.data)
{
if ((rc = gnutls_dh_params_import_pkcs3(dh_server_params, &m, GNUTLS_X509_FMT_PEM)))
- return tls_error_gnu(US"gnutls_dh_params_import_pkcs3", rc, host, errstr);
+ return tls_error_gnu(NULL, US"gnutls_dh_params_import_pkcs3", rc, errstr);
DEBUG(D_tls) debug_printf("Loaded fixed standard D-H parameters\n");
return OK;
}
rc = gnutls_dh_params_import_pkcs3(dh_server_params, &m, GNUTLS_X509_FMT_PEM);
store_free(m.data);
if (rc)
- return tls_error_gnu(US"gnutls_dh_params_import_pkcs3", rc, host, errstr);
+ return tls_error_gnu(NULL, US"gnutls_dh_params_import_pkcs3", rc, errstr);
DEBUG(D_tls) debug_printf("read D-H parameters from file \"%s\"\n", filename);
}
debug_printf("requesting generation of %d bit Diffie-Hellman prime ...\n",
dh_bits_gen);
if ((rc = gnutls_dh_params_generate2(dh_server_params, dh_bits_gen)))
- return tls_error_gnu(US"gnutls_dh_params_generate2", rc, host, errstr);
+ return tls_error_gnu(NULL, US"gnutls_dh_params_generate2", rc, errstr);
/* gnutls_dh_params_export_pkcs3() will tell us the exact size, every time,
and I confirmed that a NULL call to get the size first is how the GnuTLS
if ( (rc = gnutls_dh_params_export_pkcs3(dh_server_params,
GNUTLS_X509_FMT_PEM, m.data, &sz))
&& rc != GNUTLS_E_SHORT_MEMORY_BUFFER)
- return tls_error_gnu(US"gnutls_dh_params_export_pkcs3(NULL) sizing",
- rc, host, errstr);
+ return tls_error_gnu(NULL, US"gnutls_dh_params_export_pkcs3(NULL) sizing",
+ rc, errstr);
m.size = sz;
if (!(m.data = store_malloc(m.size)))
return tls_error_sys(US"memory allocation failed", errno, NULL, errstr);
m.data, &sz)))
{
store_free(m.data);
- return tls_error_gnu(US"gnutls_dh_params_export_pkcs3() real", rc, host, errstr);
+ return tls_error_gnu(NULL, US"gnutls_dh_params_export_pkcs3() real", rc, errstr);
}
m.size = sz; /* shrink by 1, probably */
return rc;
err:
- rc = tls_error_gnu(where, rc, NULL, errstr);
+ rc = tls_error_gnu(state, where, rc, errstr);
goto out;
}
int rc = gnutls_certificate_set_x509_key_file(state->lib_state.x509_cred,
CCS certfile, CCS keyfile, GNUTLS_X509_FMT_PEM);
if (rc < 0)
- return tls_error_gnu(
+ return tls_error_gnu(state,
string_sprintf("cert/key setup: cert=%s key=%s", certfile, keyfile),
- rc, host, errstr);
+ rc, errstr);
return -rc;
}
/* Make a note that we saw a status-request */
static int
tls_server_clienthello_ext(void * ctx, unsigned tls_id,
- const unsigned char *data, unsigned size)
+ const uschar * data, unsigned size)
{
/* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml */
switch (tls_id)
{
- case 5: /* status_request */
+ 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;
+ /* The format of "data" here doesn't seem to be documented, but appears
+ to be a 2-byte field with a (redundant, given the "size" arg) total length
+ then a sequence of one-byte size then string (not nul-term) names. The
+ latter is as described in OpenSSL documentation. */
+
+ DEBUG(D_tls) debug_printf("Seen ALPN extension from client (s=%u):", size);
+ for (const uschar * s = data+2; s-data < size-1; s += *s + 1)
+ {
+ server_seen_alpn++;
+ DEBUG(D_tls) debug_printf(" '%.*s'", (int)*s, s+1);
+ }
+ DEBUG(D_tls) debug_printf("\n");
+ if (server_seen_alpn > 1)
+ {
+ DEBUG(D_tls) debug_printf("TLS: too many ALPNs presented in handshake\n");
+ return GNUTLS_E_NO_APPLICATION_PROTOCOL;
+ }
break;
#endif
}
if ((rc = gnutls_certificate_set_ocsp_status_request_file2(
state->lib_state.x509_cred, CCS ofile, gnutls_cert_index,
ocsp_fmt)) < 0)
- return tls_error_gnu(
+ return tls_error_gnu(state,
US"gnutls_certificate_set_ocsp_status_request_file2",
- rc, NULL, errstr);
+ rc, errstr);
DEBUG(D_tls)
debug_printf(" %d response%s loaded\n", rc, rc>1 ? "s":"");
if ((rc = gnutls_certificate_set_ocsp_status_request_function2(
state->lib_state.x509_cred, gnutls_cert_index,
server_ocsp_stapling_cb, ofile)))
- return tls_error_gnu(
+ return tls_error_gnu(state,
US"gnutls_certificate_set_ocsp_status_request_function2",
- rc, NULL, errstr);
+ rc, errstr);
else
# endif
{
}
if (cert_count < 0)
- return tls_error_gnu(US"setting certificate trust", cert_count, host, errstr);
+ return tls_error_gnu(state, US"setting certificate trust", cert_count, errstr);
DEBUG(D_tls)
debug_printf("Added %d certificate authorities\n", cert_count);
DEBUG(D_tls) debug_printf("loading CRL file = %s\n", crl);
if ((cert_count = gnutls_certificate_set_x509_crl_file(state->lib_state.x509_cred,
CS crl, GNUTLS_X509_FMT_PEM)) < 0)
- return tls_error_gnu(US"gnutls_certificate_set_x509_crl_file",
- cert_count, state->host, errstr);
+ return tls_error_gnu(state, US"gnutls_certificate_set_x509_crl_file",
+ cert_count, errstr);
DEBUG(D_tls) debug_printf("Processed %d CRLs\n", cert_count);
return OK;
{
if ((rc = gnutls_certificate_allocate_credentials(
(gnutls_certificate_credentials_t *) &state->lib_state.x509_cred)))
- return tls_error_gnu(US"gnutls_certificate_allocate_credentials",
- rc, host, errstr);
+ return tls_error_gnu(state, US"gnutls_certificate_allocate_credentials",
+ rc, errstr);
creds_basic_init(state->lib_state.x509_cred, !host);
}
if ((rc = gnutls_credentials_set(state->session,
GNUTLS_CRD_CERTIFICATE, state->lib_state.x509_cred)))
- return tls_error_gnu(US"gnutls_credentials_set", rc, host, errstr);
+ return tls_error_gnu(state, US"gnutls_credentials_set", rc, errstr);
return OK;
}
state->tls_crl = tls_crl;
}
if (rc)
- return tls_error_gnu(US"gnutls_init", rc, host, errstr);
+ return tls_error_gnu(state, US"gnutls_init", rc, errstr);
state->tls_require_ciphers = require_ciphers;
state->host = host;
sz = Ustrlen(state->tlsp->sni);
if ((rc = gnutls_server_name_set(state->session,
GNUTLS_NAME_DNS, state->tlsp->sni, sz)))
- return tls_error_gnu(US"gnutls_server_name_set", rc, host, errstr);
+ return tls_error_gnu(state, US"gnutls_server_name_set", rc, errstr);
}
}
else if (state->tls_sni)
}
if ((rc = creds_load_pristring(state, p, &errpos)))
- return tls_error_gnu(string_sprintf(
+ return tls_error_gnu(state, string_sprintf(
"gnutls_priority_init(%s) failed at offset %ld, \"%.6s..\"",
p, errpos - CS p, errpos),
- rc, host, errstr);
+ rc, errstr);
}
else
{
if ((rc = gnutls_priority_set(state->session, state->lib_state.pri_cache)))
- return tls_error_gnu(US"gnutls_priority_set", rc, host, errstr);
+ return tls_error_gnu(state, US"gnutls_priority_set", rc, errstr);
/* This also sets the server ticket expiration time to the same, and
the STEK rotation time to 3x. */
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_gnu((Label), rc, state->host, errstr); \
+ return tls_error_gnu(state, (Label), rc, errstr); \
return OK; \
} \
} while (0)
DEBUG(D_tls) debug_printf("Session resumed\n");
}
}
-#endif
+#endif /* EXIM_HAVE_TLS_RESUME */
#ifdef EXIM_HAVE_ALPN
+/* Expand and convert an Exim list to a gnutls_datum list. False return for fail.
+NULL plist return for silent no-ALPN.
+*/
+
+static BOOL
+tls_alpn_plist(const uschar * tls_alpn, const gnutls_datum_t ** plist, unsigned * plen,
+ uschar ** errstr)
+{
+uschar * exp_alpn;
+
+if (!expand_check(tls_alpn, US"tls_alpn", &exp_alpn, errstr))
+ return FALSE;
+
+if (!exp_alpn)
+ {
+ DEBUG(D_tls) debug_printf("Setting TLS ALPN forced to fail, not sending\n");
+ *plist = NULL;
+ }
+else
+ {
+ const uschar * list = exp_alpn;
+ int sep = 0;
+ unsigned cnt = 0;
+ gnutls_datum_t * p;
+ uschar * s;
+
+ while (string_nextinlist(&list, &sep, NULL, 0)) cnt++;
+
+ p = store_get(sizeof(gnutls_datum_t) * cnt, is_tainted(exp_alpn));
+ list = exp_alpn;
+ for (int i = 0; s = string_nextinlist(&list, &sep, NULL, 0); i++)
+ { p[i].data = s; p[i].size = Ustrlen(s); }
+ *plist = (*plen = cnt) ? p : NULL;
+ }
+return TRUE;
+}
+
static void
-tls_server_set_acceptable_alpns(exim_gnutls_state_st * state)
+tls_server_set_acceptable_alpns(exim_gnutls_state_st * state, uschar ** errstr)
{
int rc;
-gnutls_datum_t protocols[2] = {[0] = {.data = US"smtp", .size = 4},
- [1] = {.data = US"esmtp", .size = 5}};
+const gnutls_datum_t * plist;
+unsigned plen;
-/* 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));
+if (tls_alpn_plist(tls_alpn, &plist, &plen, errstr) && plist)
+ {
+ /* This seems to be only mandatory if the client sends an ALPN extension;
+ not trying ALPN is ok. Need to decide how to support server-side must-alpn. */
+
+ server_seen_alpn = 0;
+ if (!(rc = gnutls_alpn_set_protocols(state->session, plist, plen,
+ GNUTLS_ALPN_MANDATORY)))
+ 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
+#endif /* EXIM_HAVE_ALPN */
/* ------------------------------------------------------------------------ */
/* Exported functions */
}
#ifdef EXIM_HAVE_ALPN
-tls_server_set_acceptable_alpns(state);
+tls_server_set_acceptable_alpns(state, errstr);
#endif
#ifdef EXIM_HAVE_TLS_RESUME
}
else
{
- tls_error_gnu(US"gnutls_handshake", rc, NULL, errstr);
+ tls_error_gnu(state, US"gnutls_handshake", rc, errstr);
(void) gnutls_alert_send_appropriate(state->session, rc);
gnutls_deinit(state->session);
gnutls_certificate_free_credentials(state->lib_state.x509_cred);
DEBUG(D_tls) post_handshake_debug(state);
#ifdef EXIM_HAVE_ALPN
-if (server_seen_alpn)
+if (server_seen_alpn > 0)
{
- /* 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(D_tls)
+ { /* The client offered ALPN. See what was negotiated. */
+ gnutls_datum_t p = {.size = 0};
+ int rc = gnutls_alpn_get_selected_protocol(state->session, &p);
+ if (!rc)
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));
+ else
+ debug_printf("getting alpn protocol: %s\n", US gnutls_strerror(rc));
+ }
}
+else if (server_seen_alpn == 0)
+ if (verify_check_host(&hosts_require_alpn) == OK)
+ {
+ gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_NO_APPLICATION_PROTOCOL);
+ tls_error(US"handshake", US"ALPN required but not negotiated", NULL, errstr);
+ return FAIL;
+ }
+ else
+ DEBUG(D_tls) debug_printf("TLS: no ALPN presented in handshake\n");
+else
+ DEBUG(D_tls) debug_printf("TLS: was not watching for ALPN\n");
#endif
/* Verify after the fact */
#endif
}
+if (ob->tls_alpn)
+#ifdef EXIM_HAVE_ALPN
+ {
+ const gnutls_datum_t * plist;
+ unsigned plen;
+
+ if (!tls_alpn_plist(ob->tls_alpn, &plist, &plen, errstr))
+ return FALSE;
+ if (plist)
+ if (gnutls_alpn_set_protocols(state->session, plist, plen, 0) != 0)
+ {
+ tls_error(US"alpn init", NULL, state->host, errstr);
+ return FALSE;
+ }
+ else
+ DEBUG(D_tls) debug_printf("Setting TLS ALPN '%s'\n", ob->tls_alpn);
+ }
+#else
+ log_write(0, LOG_MAIN, "ALPN unusable with this GnuTLS library version; ignoring \"%s\"\n",
+ ob->tls_alpn);
+#endif
+
{
int dh_min_bits = ob->tls_dh_min_bits;
if (dh_min_bits < EXIM_CLIENT_DH_MIN_MIN_BITS)
if ((rc = gnutls_ocsp_status_request_enable_client(state->session,
NULL, 0, NULL)) != OK)
{
- tls_error_gnu(US"cert-status-req", rc, state->host, errstr);
+ tls_error_gnu(state, US"cert-status-req", rc, errstr);
return FALSE;
}
tlsp->ocsp = OCSP_NOT_RESP;
tls_error(US"gnutls_handshake", US"timed out", state->host, errstr);
}
else
- tls_error_gnu(US"gnutls_handshake", rc, state->host, errstr);
+ tls_error_gnu(state, US"gnutls_handshake", rc, errstr);
return FALSE;
}
gnutls_free(printed.data);
}
else
- (void) tls_error_gnu(US"ocsp decode", rc, state->host, errstr);
+ (void) tls_error_gnu(state, US"ocsp decode", rc, errstr);
if (idx == 0 && rc)
- (void) tls_error_gnu(US"ocsp decode", rc, state->host, errstr);
+ (void) tls_error_gnu(state, US"ocsp decode", rc, errstr);
}
if (gnutls_ocsp_status_request_is_checked(state->session, 0) == 0)
tls_client_resume_posthandshake(state, tlsp, host);
#endif
+#ifdef EXIM_HAVE_ALPN
+if (ob->tls_alpn) /* We requested. See what was negotiated. */
+ {
+ gnutls_datum_t p = {.size = 0};
+
+ if (gnutls_alpn_get_selected_protocol(state->session, &p) == 0)
+ { DEBUG(D_tls) debug_printf("ALPN negotiated: '%.*s'\n", (int)p.size, p.data); }
+ else if (verify_check_given_host(CUSS &ob->hosts_require_alpn, host) == OK)
+ {
+ gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_NO_APPLICATION_PROTOCOL);
+ tls_error(US"handshake", US"ALPN required but not negotiated", state->host, errstr);
+ return FALSE;
+ }
+ else
+ DEBUG(D_tls) debug_printf("No ALPN negotiated");
+ }
+#endif
+
/* Sets various Exim expansion variables; may need to adjust for ACL callouts */
extract_exim_vars_from_tls_state(state);
#ifdef EXIM_HAVE_OPENSSL_TLSEXT
static SSL_CTX *server_sni = NULL;
#endif
+#ifdef EXIM_HAVE_ALPN
+static BOOL server_seen_alpn = FALSE;
+#endif
static char ssl_errstring[256];
* 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 */
+/* Called on server if tls_alpn nonblank after expansion,
+when client offers ALPN, after the SNI callback.
+If set and not matching the list then we dump the connection */
static int
tls_server_alpn_cb(SSL *ssl, const uschar ** out, uschar * outlen,
{
const exim_openssl_state_st * state = arg;
+server_seen_alpn = TRUE;
DEBUG(D_tls)
{
debug_printf("Received TLS ALPN offer:");
if (pos + 1 + siz > inlen) siz = inlen - pos - 1;
debug_printf(" '%.*s'", siz, in + pos + 1);
}
- debug_printf("\n");
+ debug_printf(". Our list: '%s'\n", tls_alpn);
}
/* 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 */
+ const uschar * list = tls_alpn;
+ int sep = 0;
+ for (uschar * name; name = string_nextinlist(&list, &sep, NULL, 0); )
+ if (Ustrncmp(in+1, name, 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 */
+/* More than one name from clilent, or name did not match our list. */
+
+/* This will be fatal to the TLS conn; would be nice to kill TCP also.
+Maybe as an option in future; for now leave control to the config (must-tls). */
+
+DEBUG(D_tls) debug_printf("TLS ALPN rejected\n");
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
#endif /* EXIM_HAVE_ALPN */
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);
+ if (tls_alpn && *tls_alpn)
+ {
+ uschar * exp_alpn;
+ if ( expand_check(tls_alpn, US"tls_alpn", &exp_alpn, errstr)
+ && *exp_alpn && !isblank(*exp_alpn))
+ {
+ tls_alpn = exp_alpn; /* subprocess so ok to overwrite */
+ SSL_CTX_set_alpn_select_cb(ctx, tls_server_alpn_cb, state);
+ }
+ else
+ tls_alpn = NULL;
+ }
# endif
}
# ifndef DISABLE_OCSP
}
#endif
+#ifdef EXIM_HAVE_ALPN
+/* If require-alpn, check server_seen_alpn here. Else abort TLS */
+if (!tls_alpn || !*tls_alpn)
+ { DEBUG(D_tls) debug_printf("TLS: was not watching for ALPN\n"); }
+else if (!server_seen_alpn)
+ if (verify_check_host(&hosts_require_alpn) == OK)
+ {
+ /* We'd like to send a definitive Alert but OpenSSL provides no facility */
+ SSL_shutdown(ssl);
+ tls_error(US"handshake", NULL, US"ALPN required but not negotiated", errstr);
+ return FAIL;
+ }
+ else
+ { DEBUG(D_tls) debug_printf("TLS: no ALPN presented in handshake\n"); }
+else DEBUG(D_tls)
+ {
+ const uschar * name;
+ unsigned len;
+ SSL_get0_alpn_selected(ssl, &name, &len);
+ debug_printf("ALPN negotiated: '%.*s'\n", (int)*name, name+1);
+ }
+#endif
+
+
/* TLS has been set up. Record data for the connection,
adjust the input functions to read via TLS, and initialize things. */
#endif /* !DISABLE_TLS_RESUME */
+#ifdef EXIM_HAVE_ALPN
+/* Expand and convert an Exim list to an ALPN list. False return for fail.
+NULL plist return for silent no-ALPN.
+*/
+
+static BOOL
+tls_alpn_plist(const uschar * tls_alpn, const uschar ** plist, unsigned * plen,
+ uschar ** errstr)
+{
+uschar * exp_alpn;
+
+if (!expand_check(tls_alpn, US"tls_alpn", &exp_alpn, errstr))
+ return FALSE;
+
+if (!exp_alpn)
+ {
+ DEBUG(D_tls) debug_printf("Setting TLS ALPN forced to fail, not sending\n");
+ *plist = NULL;
+ }
+else
+ {
+ /* The server implementation only accepts exactly one protocol name
+ but it's little extra code complexity in the client. */
+
+ const uschar * list = exp_alpn;
+ uschar * p = store_get(Ustrlen(exp_alpn), is_tainted(exp_alpn)), * s, * t;
+ int sep = 0;
+ uschar len;
+
+ for (t = p; s = string_nextinlist(&list, &sep, NULL, 0); t += len)
+ {
+ *t++ = len = (uschar) Ustrlen(s);
+ memcpy(t, s, len);
+ }
+ *plist = (*plen = t - p) ? p : NULL;
+ }
+return TRUE;
+}
+#endif /* EXIM_HAVE_ALPN */
+
+
/*************************************************
* Start a TLS session in a client *
*************************************************/
}
}
+if (ob->tls_alpn)
+#ifdef EXIM_HAVE_ALPN
+ {
+ const uschar * plist;
+ unsigned plen;
+
+ if (!tls_alpn_plist(ob->tls_alpn, &plist, &plen, errstr))
+ return FALSE;
+ if (plist)
+ if (SSL_set_alpn_protos(exim_client_ctx->ssl, plist, plen) != 0)
+ {
+ tls_error(US"alpn init", host, NULL, errstr);
+ return FALSE;
+ }
+ else
+ DEBUG(D_tls) debug_printf("Setting TLS ALPN '%s'\n", ob->tls_alpn);
+ }
+#else
+ log_write(0, LOG_MAIN, "ALPN unusable with this OpenSSL library version; ignoring \"%s\"\n",
+ ob->alpn);
+#endif
+
#ifdef SUPPORT_DANE
if (conn_args->dane)
if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK)
tls_client_resume_posthandshake(exim_client_ctx, tlsp);
#endif
+#ifdef EXIM_HAVE_ALPN
+if (ob->tls_alpn) /* We requested. See what was negotiated. */
+ {
+ const uschar * name;
+ unsigned len;
+
+ SSL_get0_alpn_selected(exim_client_ctx->ssl, &name, &len);
+ if (len > 0)
+ { DEBUG(D_tls) debug_printf("ALPN negotiated %u: '%.*s'\n", len, (int)*name, name+1); }
+ else if (verify_check_given_host(CUSS &ob->hosts_require_alpn, host) == OK)
+ {
+ /* Would like to send a relevant fatal Alert, but OpenSSL has no API */
+ tls_error(US"handshake", host, US"ALPN required but not negotiated", errstr);
+ return FALSE;
+ }
+ }
+#endif
+
#ifdef SSL_get_extms_support
tlsp->ext_master_secret = SSL_get_extms_support(exim_client_ctx->ssl) == 1;
#endif
return FALSE;
}
-
/* Environment cleanup: The GnuTLS library uses SSLKEYLOGFILE in the environment
and writes a file by that name. Our OpenSSL code does the same, using keying
info from the library API.
#if !defined(DISABLE_TLS) && !defined(DISABLE_OCSP)
{ "hosts_request_ocsp", opt_stringptr, LOFF(hosts_request_ocsp) },
#endif
+ { "hosts_require_alpn", opt_stringptr, LOFF(hosts_require_alpn) },
{ "hosts_require_auth", opt_stringptr, LOFF(hosts_require_auth) },
#ifndef DISABLE_TLS
# ifdef SUPPORT_DANE
{ "socks_proxy", opt_stringptr, LOFF(socks_proxy) },
#endif
#ifndef DISABLE_TLS
+ { "tls_alpn", opt_stringptr, LOFF(tls_alpn) },
{ "tls_certificate", opt_stringptr, LOFF(tls_certificate) },
{ "tls_crl", opt_stringptr, LOFF(tls_crl) },
{ "tls_dh_min_bits", opt_int, LOFF(tls_dh_min_bits) },
uschar *dscp;
uschar *serialize_hosts;
uschar *hosts_try_auth;
+ uschar *hosts_require_alpn;
uschar *hosts_require_auth;
uschar *hosts_try_chunking;
#ifdef SUPPORT_DANE
uschar *socks_proxy;
#endif
#ifndef DISABLE_TLS
+ uschar *tls_alpn;
uschar *tls_certificate;
uschar *tls_crl;
uschar *tls_privatekey;
--- /dev/null
+# Exim test configuration 1115
+# ALPN
+
+SERVER =
+CONTROL =
+
+.include DIR/aux-var/tls_conf_prefix
+
+primary_hostname = myhost.test.ex
+
+# ----- Main settings -----
+
+domainlist local_domains = test.ex : *.test.ex
+
+acl_smtp_rcpt = accept
+
+tls_advertise_hosts = *
+tls_certificate = DIR/aux-fixed/cert1
+
+.ifdef STRICT
+tls_alpn = STRICT
+.endif
+.ifdef REQUIRE
+hosts_require_alpn = *
+.endif
+
+
+# ------ ACL ------
+
+begin acl
+
+# ----- Routers -----
+
+begin routers
+
+client:
+ driver = accept
+ condition = ${if eq {SERVER}{server} {no}{yes}}
+ transport = send_to_server
+
+server:
+ driver = redirect
+ data = :blackhole:
+
+
+# ----- Transports -----
+
+begin transports
+
+send_to_server:
+ driver = smtp
+ allow_localhost
+ hosts = HOSTIPV4
+ port = PORT_D
+ tls_verify_certificates = DIR/aux-fixed/cert1
+ tls_verify_cert_hostnames = :
+
+ hosts_require_tls = *
+ hosts_try_fastopen = :
+ tls_alpn = CONTROL
+.ifdef REQUIRE
+ hosts_require_alpn = *
+.endif
+
+# ----- Retry -----
+
+
+begin retry
+
+* * F,5d,10s
+
+
+# End
--- /dev/null
+1115
\ No newline at end of file
--- /dev/null
+1115
\ No newline at end of file
--- /dev/null
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaZ-0005vi-00 == b@test.ex R=client T=send_to_server defer (-37) H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]: TLS session: (SSL_connect): error: <<detail omitted>>
+1999-03-02 09:44:33 10HmaZ-0005vi-00 removed by CALLER
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbA-0005vi-00 == c@test.ex R=client T=send_to_server defer (-37) H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]: TLS session: (SSL_connect): error: <<detail omitted>>
+1999-03-02 09:44:33 10HmbA-0005vi-00 removed by CALLER
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbB-0005vi-00 => d@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbD-0005vi-00 => e@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbE-0005vi-00"
+1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbF-0005vi-00 => f@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbG-0005vi-00"
+1999-03-02 09:44:33 10HmbF-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbH-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbH-0005vi-00 => g@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbI-0005vi-00"
+1999-03-02 09:44:33 10HmbH-0005vi-00 Completed
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <a@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 TLS error on connection from the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] (SSL_accept): error: <<detail omitted>>
+1999-03-02 09:44:33 TLS error on connection from the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] (SSL_accept): error: <<detail omitted>>
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbB-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <d@test.ex> R=server
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbD-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbE-0005vi-00 => :blackhole: <e@test.ex> R=server
+1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbF-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbG-0005vi-00 => :blackhole: <f@test.ex> R=server
+1999-03-02 09:44:33 10HmbG-0005vi-00 Completed
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 10HmbI-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbH-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbI-0005vi-00 => :blackhole: <g@test.ex> R=server
+1999-03-02 09:44:33 10HmbI-0005vi-00 Completed
--- /dev/null
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => client_require@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaZ-0005vi-00 == client_require_fail@test.ex R=client T=send_to_server defer (-37) H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]: TLS session: (handshake): ALPN required but not negotiated
+1999-03-02 09:44:33 10HmaZ-0005vi-00 removed by CALLER
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbA-0005vi-00 => server_require_good@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbB-0005vi-00"
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbC-0005vi-00 H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] TLS error on connection (recv): A TLS fatal alert has been received: No supported application protocol could be negotiated
+1999-03-02 09:44:33 10HmbC-0005vi-00 == server_require_bad@test.ex R=client T=send_to_server defer (-37) H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]: TLS session: error on first read
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <client_require@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 TLS error on connection from the.local.host.name [ip4.ip4.ip4.ip4] (recv): A TLS fatal alert has been received: No supported application protocol could be negotiated
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbA-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbB-0005vi-00 => :blackhole: <server_require_good@test.ex> R=server
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 TLS error on connection from the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] (handshake): ALPN required but not negotiated
--- /dev/null
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => client_require@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaZ-0005vi-00 == client_require_fail@test.ex R=client T=send_to_server defer (-37) H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]: TLS session: (handshake): ALPN required but not negotiated
+1999-03-02 09:44:33 10HmaZ-0005vi-00 removed by CALLER
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbA-0005vi-00 => server_require_good@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes C="250 OK id=10HmbB-0005vi-00"
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbC-0005vi-00 H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]: Remote host closed connection in response to EHLO myhost.test.ex
+1999-03-02 09:44:33 10HmbC-0005vi-00 == server_require_bad@test.ex R=client T=send_to_server defer (-18) H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]: Remote host closed connection in response to EHLO myhost.test.ex
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <client_require@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no S=sss id=E10HmbA-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbB-0005vi-00 => :blackhole: <server_require_good@test.ex> R=server
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 TLS error on connection from the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] (handshake): ALPN required but not negotiated
s/session: \K\((SSL_connect|gnutls_handshake)\): timed out/(tls lib connect fn): timed out/;
s/TLS error on connection from .*\K\((SSL_accept|gnutls_handshake)\): timed out/(tls lib accept fn): timed out/;
s/TLS error on connection from .*\K(SSL_accept: TCP connection closed by peer|\(gnutls_handshake\): The TLS connection was non-properly terminated.)/(tls lib accept fn): TCP connection closed by peer/;
+ s/TLS session: \K\(gnutls_handshake\): No supported application protocol could be negotiated/(SSL_connect): error: <<detail omitted>>/;
+ s/\(gnutls_handshake\): No common application protocol could be negotiated./(SSL_accept): error: <<detail omitted>>/;
}
# ======== mail ========
--- /dev/null
+# TLS: ALPN
+gnutls
+exim -DSERVER=server -bd -oX PORT_D
+****
+#
+# Basic: is good ALPN set on tpt acceptable to server
+exim -DCONTROL=smtp -odf a@test.ex
+Test message.
+****
+#
+# Bad ALPN rejected
+exim -DCONTROL=http -odf b@test.ex
+****
+exim -Mrm $msg1
+****
+sudo rm -f DIR/spool/db/retry
+#
+# Multiple ALPN rejected
+exim -DCONTROL=smtp:smtp -odf c@test.ex
+****
+exim -Mrm $msg1
+****
+sudo rm -f DIR/spool/db/retry
+#
+# Empty client option is ok
+exim -DCONTROL="" -odf d@test.ex
+****
+# Content-free client option is ok
+exim -DCONTROL=" " -odf e@test.ex
+****
+killdaemon
+#
+# Server can be told to ignore (bad) ALPN from client
+exim -DSERVER=server -DSTRICT="" -bd -oX PORT_D
+****
+exim -DCONTROL=http -odf f@test.ex
+****
+killdaemon
+#
+# Server can be told custom names list
+exim -DSERVER=server -DSTRICT='${if eq {$sender_host_address}{HOSTIPV4} {smtp:weird} {smtp}}' -bd -oX PORT_D
+****
+exim -DCONTROL=weird -odf g@test.ex
+****
+killdaemon
+#
+#
+no_msglog_check
+no_stdout_check
+millisleep 500
--- /dev/null
+# TLS: ALPN: mandatory
+# Separated from the OpenSSL equivalent as we deliberately send a Fatal Alert, and that gets logged by the receiver
+# OpenSSL does not provides the facility.
+gnutls
+exim -DSERVER=server -bd -oX PORT_D
+****
+# Client requires ALPN (success)
+exim -DCONTROL=smtp -DREQUIRE=y -odf client_require@test.ex
+****
+killdaemon
+#
+# Server can be told to ignore (bad) ALPN from client
+exim -DSERVER=server -DSTRICT="" -bd -oX PORT_D
+****
+# Client requires ALPN (fail)
+exim -DCONTROL=http -DREQUIRE=y -odf client_require_fail@test.ex
+****
+exim -Mrm $msg1
+****
+sudo rm -f DIR/spool/db/retry
+killdaemon
+#
+#
+# Server can be told ALPN mandatory
+exim -DSERVER=server -DREQUIRE=y -bd -oX PORT_D
+****
+# Client supplies ALPN, good
+exim -DCONTROL=smtp -odf server_require_good@test.ex
+****
+# Client does not supply ALPN, fails
+exim -odf server_require_bad@test.ex
+****
+killdaemon
+#
+no_msglog_check
+no_stdout_check
--- /dev/null
+# TLS: ALPN: mandatory
+#
+# Plain server
+exim -DSERVER=server -bd -oX PORT_D
+****
+# Client requires ALPN (success)
+exim -DCONTROL=smtp -DREQUIRE=y -odf client_require@test.ex
+****
+killdaemon
+#
+# Server can be told to ignore (bad) ALPN from client
+exim -DSERVER=server -DSTRICT="" -bd -oX PORT_D
+****
+# Client requires ALPN (fail)
+exim -DCONTROL=http -DREQUIRE=y -odf client_require_fail@test.ex
+****
+exim -Mrm $msg1
+****
+sudo rm -f DIR/spool/db/retry
+killdaemon
+#
+#
+# Server can be told ALPN mandatory
+exim -DSERVER=server -DREQUIRE=y -bd -oX PORT_D
+****
+# Client supplies ALPN, good
+exim -DCONTROL=smtp -odf server_require_good@test.ex
+****
+# Client does not supply ALPN, fails
+exim -odf server_require_bad@test.ex
+****
+killdaemon
+#
+no_msglog_check
+no_stdout_check
hosts_max_try_hardlimit = 50
no_hosts_override
no_hosts_randomize
+hosts_require_alpn =
hosts_require_auth =
hosts_try_auth =
hosts_try_chunking = *