X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/9f6b3bf5187562bac4c96e3ed6a17740d01489fa..f168d6a2c93ee92d87744ce09c45a0ec666f889c:/src/src/tls.c diff --git a/src/src/tls.c b/src/src/tls.c index c48394cdb..e6203b768 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -2,9 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* 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,10 +101,14 @@ 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) + { + f.expand_string_forcedfail = FALSE; *result = NULL; + } else if ( !(*result = expand_string(US s)) /* need to clean up const more */ && !f.expand_string_forcedfail ) @@ -143,15 +143,29 @@ static BOOL tls_set_one_watch(const uschar * filename) # ifdef EXIM_HAVE_INOTIFY { +uschar buf[PATH_MAX]; +ssize_t len; uschar * s; if (Ustrcmp(filename, "system,cache") == 0) return TRUE; - if (!(s = Ustrrchr(filename, '/'))) return FALSE; + +for (unsigned loop = 20; + (len = readlink(CCS filename, CS buf, sizeof(buf))) >= 0; ) + { /* a symlink */ + if (--loop == 0) { errno = ELOOP; return FALSE; } + filename = buf[0] == '/' + ? string_copyn(buf, (unsigned)len) /* mem released by tls_set_watch */ + : string_sprintf("%.*s/%.*s", (int)(s - filename), filename, (int)len, buf); + s = Ustrrchr(filename, '/'); + } +if (errno != EINVAL) + return FALSE; /* other error */ + +/* not a symlink */ s = string_copyn(filename, s - filename); /* mem released by tls_set_watch */ -DEBUG(D_tls) debug_printf("watch dir '%s'\n", s); -/*XXX unclear what effect symlinked files will have for inotify */ +DEBUG(D_tls) debug_printf("watch dir '%s'\n", s); if (inotify_add_watch(tls_watch_fd, CCS s, IN_ONESHOT | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF @@ -347,6 +361,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; @@ -441,6 +457,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" @@ -653,21 +673,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"); } } @@ -679,13 +702,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; @@ -800,6 +828,7 @@ static void tls_client_resmption_key(tls_support * tlsp, smtp_connect_args * conn_args, smtp_transport_options_block * ob) { +#ifndef DISABLE_TLS_RESUME hctx * h = &tlsp->resume_hctx; blob b; gstring * g; @@ -807,32 +836,84 @@ gstring * g; DEBUG(D_tls) if (conn_args->host_lbserver) debug_printf("TLS: lbserver '%s'\n", conn_args->host_lbserver); -#ifdef EXIM_HAVE_SHA2 +# ifdef EXIM_HAVE_SHA2 exim_sha_init(h, HASH_SHA2_256); -#else +# else exim_sha_init(h, HASH_SHA1); -#endif +# endif exim_sha_update_string(h, conn_args->host_lbserver); -#ifdef SUPPORT_DANE +# ifdef SUPPORT_DANE if (conn_args->dane) exim_sha_update(h, CUS &conn_args->tlsa_dnsa, sizeof(dns_answer)); -#endif +# endif exim_sha_update_string(h, conn_args->host->address); exim_sha_update(h, CUS &conn_args->host->port, sizeof(conn_args->host->port)); exim_sha_update_string(h, conn_args->sending_ip_address); exim_sha_update_string(h, openssl_options); exim_sha_update_string(h, ob->tls_require_ciphers); exim_sha_update_string(h, tlsp->sni); -#ifdef EXIM_HAVE_ALPN +# ifdef EXIM_HAVE_ALPN exim_sha_update_string(h, ob->tls_alpn); -#endif +# endif exim_sha_finish(h, &b); for (g = string_get(b.len*2+1); b.len-- > 0; ) g = string_fmt_append(g, "%02x", *b.data++); tlsp->resume_index = string_from_gstring(g); 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*/