X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/c4b4086235b1d5e21fcf1ad72a1b05813e15dcbd..refs/tags/exim-4.97:/src/src/tls.c diff --git a/src/src/tls.c b/src/src/tls.c index c497ac307..b6c5d70e1 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 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 */ /* 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 @@ -100,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; @@ -138,15 +140,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 @@ -185,8 +201,8 @@ for (;;) { if ((fd1 = open(CCS filename, O_RDONLY | O_NOFOLLOW)) < 0) { s = US"open file"; goto bad; } - DEBUG(D_tls) debug_printf("watch file '%s'\n", filename); - EV_SET(&kev[++kev_used], + DEBUG(D_tls) debug_printf("watch file '%s':\t%d\n", filename, fd1); + EV_SET(&kev[kev_used++], (uintptr_t)fd1, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, @@ -196,8 +212,8 @@ for (;;) NULL); cnt++; } - DEBUG(D_tls) debug_printf("watch dir '%s'\n", s); - EV_SET(&kev[++kev_used], + DEBUG(D_tls) debug_printf("watch dir '%s':\t%d\n", s, fd2); + EV_SET(&kev[kev_used++], (uintptr_t)fd2, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, @@ -209,7 +225,7 @@ for (;;) if (!(S_ISLNK(sb.st_mode))) break; - t = store_get(1024, FALSE); + t = store_get(1024, GET_UNTAINTED); Ustrncpy(t, s, 1022); j = Ustrlen(s); t[j++] = '/'; @@ -320,6 +336,7 @@ if (tls_watch_fd < 0) return; /* Close the files we had open for kevent */ for (int i = 0; i < kev_used; i++) { + DEBUG(D_tls) debug_printf("closing watch fd: %d\n", (int) kev[i].ident); (void) close((int) kev[i].ident); kev[i].ident = (uintptr_t)-1; } @@ -341,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; @@ -435,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" @@ -647,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"); } } @@ -673,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; @@ -790,6 +821,96 @@ return status == 0; +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; + +DEBUG(D_tls) if (conn_args->host_lbserver) + debug_printf("TLS: lbserver '%s'\n", conn_args->host_lbserver); + +# ifdef EXIM_HAVE_SHA2 +exim_sha_init(h, HASH_SHA2_256); +# else +exim_sha_init(h, HASH_SHA1); +# endif +exim_sha_update_string(h, conn_args->host_lbserver); +# ifdef SUPPORT_DANE +if (conn_args->dane) + exim_sha_update(h, CUS &conn_args->tlsa_dnsa, sizeof(dns_answer)); +# 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 +exim_sha_update_string(h, ob->tls_alpn); +# 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*/