Hi. The diff seems to work for the few people who tested it (thanks). Anyone wants to ok this?
Eric. Index: ca.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/ca.c,v retrieving revision 1.37 diff -u -p -r1.37 ca.c --- ca.c 31 Dec 2020 08:27:15 -0000 1.37 +++ ca.c 19 Jan 2021 11:09:54 -0000 @@ -69,6 +69,7 @@ static int ecdsae_do_verify(const unsign EC_KEY *); +static struct dict pkeys; static uint64_t reqid = 0; static void @@ -132,26 +133,29 @@ ca_init(void) struct pki *pki; const char *k; void *iter_dict; + char *hash; log_debug("debug: init private ssl-tree"); + dict_init(&pkeys); iter_dict = NULL; while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) { if (pki->pki_key == NULL) continue; - if ((in = BIO_new_mem_buf(pki->pki_key, - pki->pki_key_len)) == NULL) - fatalx("ca_launch: key"); - - if ((pkey = PEM_read_bio_PrivateKey(in, - NULL, NULL, NULL)) == NULL) - fatalx("ca_launch: PEM"); + in = BIO_new_mem_buf(pki->pki_key, pki->pki_key_len); + if (in == NULL) + fatalx("ca_init: key"); + pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL); + if (pkey == NULL) + fatalx("ca_init: PEM"); BIO_free(in); - pki->pki_pkey = pkey; - - freezero(pki->pki_key, pki->pki_key_len); - pki->pki_key = NULL; + hash = ssl_pubkey_hash(pki->pki_cert, pki->pki_cert_len); + if (dict_check(&pkeys, hash)) + EVP_PKEY_free(pkey); + else + dict_xset(&pkeys, hash, pkey); + free(hash); } } @@ -223,15 +227,15 @@ end: void ca_imsg(struct mproc *p, struct imsg *imsg) { + EVP_PKEY *pkey; RSA *rsa = NULL; EC_KEY *ecdsa = NULL; const void *from = NULL; unsigned char *to = NULL; struct msg m; - const char *pkiname; + const char *hash; size_t flen, tlen, padding; int buf_len; - struct pki *pki; int ret = 0; uint64_t id; int v; @@ -267,16 +271,15 @@ ca_imsg(struct mproc *p, struct imsg *im case IMSG_CA_RSA_PRIVDEC: m_msg(&m, imsg); m_get_id(&m, &id); - m_get_string(&m, &pkiname); + m_get_string(&m, &hash); m_get_data(&m, &from, &flen); m_get_size(&m, &tlen); m_get_size(&m, &padding); m_end(&m); - pki = dict_get(env->sc_pki_dict, pkiname); - if (pki == NULL || pki->pki_pkey == NULL || - (rsa = EVP_PKEY_get1_RSA(pki->pki_pkey)) == NULL) - fatalx("ca_imsg: invalid pki"); + pkey = dict_get(&pkeys, hash); + if (pkey == NULL || (rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) + fatalx("ca_imsg: invalid pkey hash"); if ((to = calloc(1, tlen)) == NULL) fatalx("ca_imsg: calloc"); @@ -306,14 +309,14 @@ ca_imsg(struct mproc *p, struct imsg *im case IMSG_CA_ECDSA_SIGN: m_msg(&m, imsg); m_get_id(&m, &id); - m_get_string(&m, &pkiname); + m_get_string(&m, &hash); m_get_data(&m, &from, &flen); m_end(&m); - pki = dict_get(env->sc_pki_dict, pkiname); - if (pki == NULL || pki->pki_pkey == NULL || - (ecdsa = EVP_PKEY_get1_EC_KEY(pki->pki_pkey)) == NULL) - fatalx("ca_imsg: invalid pki"); + pkey = dict_get(&pkeys, hash); + if (pkey == NULL || + (ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) + fatalx("ca_imsg: invalid pkey hash"); buf_len = ECDSA_size(ecdsa); if ((to = calloc(1, buf_len)) == NULL) @@ -350,12 +353,12 @@ rsae_send_imsg(int flen, const unsigned struct imsg imsg; int n, done = 0; const void *toptr; - char *pkiname; + char *hash; size_t tlen; struct msg m; uint64_t id; - if ((pkiname = RSA_get_ex_data(rsa, 0)) == NULL) + if ((hash = RSA_get_ex_data(rsa, 0)) == NULL) return (0); /* @@ -365,7 +368,7 @@ rsae_send_imsg(int flen, const unsigned m_create(p_ca, cmd, 0, 0, -1); reqid++; m_add_id(p_ca, reqid); - m_add_string(p_ca, pkiname); + m_add_string(p_ca, hash); m_add_data(p_ca, (const void *)from, (size_t)flen); m_add_size(p_ca, (size_t)RSA_size(rsa)); m_add_size(p_ca, (size_t)padding); @@ -536,13 +539,13 @@ ecdsae_send_enc_imsg(const unsigned char struct imsg imsg; int n, done = 0; const void *toptr; - char *pkiname; + char *hash; size_t tlen; struct msg m; uint64_t id; ECDSA_SIG *sig = NULL; - if ((pkiname = ECDSA_get_ex_data(eckey, 0)) == NULL) + if ((hash = ECDSA_get_ex_data(eckey, 0)) == NULL) return (0); /* @@ -552,7 +555,7 @@ ecdsae_send_enc_imsg(const unsigned char m_create(p_ca, IMSG_CA_ECDSA_SIGN, 0, 0, -1); reqid++; m_add_id(p_ca, reqid); - m_add_string(p_ca, pkiname); + m_add_string(p_ca, hash); m_add_data(p_ca, (const void *)dgst, (size_t)dgst_len); m_flush(p_ca); Index: config.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/config.c,v retrieving revision 1.53 diff -u -p -r1.53 config.c --- config.c 19 Jan 2021 09:16:20 -0000 1.53 +++ config.c 19 Jan 2021 11:47:07 -0000 @@ -252,6 +252,7 @@ purge_config(uint8_t what) if (what & PURGE_LISTENERS) { while ((l = TAILQ_FIRST(env->sc_listeners)) != NULL) { TAILQ_REMOVE(env->sc_listeners, l, entry); + free(l->pki); free(l); } free(env->sc_listeners); Index: dispatcher.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/dispatcher.c,v retrieving revision 1.1 diff -u -p -r1.1 dispatcher.c --- dispatcher.c 31 Dec 2020 08:27:15 -0000 1.1 +++ dispatcher.c 19 Jan 2021 11:04:32 -0000 @@ -154,6 +154,8 @@ dispatcher(void) { struct passwd *pw; + ca_engine_init(); + mda_postfork(); mta_postfork(); smtp_postfork(); @@ -195,8 +197,6 @@ dispatcher(void) config_peer(PROC_LKA); config_peer(PROC_CONTROL); config_peer(PROC_CA); - - ca_engine_init(); if (pledge("stdio inet unix recvfd sendfd", NULL) == -1) err(1, "pledge"); Index: iobuf.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/iobuf.c,v retrieving revision 1.14 diff -u -p -r1.14 iobuf.c --- iobuf.c 23 Jan 2021 16:11:11 -0000 1.14 +++ iobuf.c 25 Jan 2021 11:14:58 -0000 @@ -21,15 +21,14 @@ #include <errno.h> #include <limits.h> +#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> - #ifdef IO_TLS -#include <openssl/err.h> -#include <openssl/ssl.h> +#include <tls.h> #endif +#include <unistd.h> #include "iobuf.h" @@ -388,7 +387,7 @@ iobuf_flush(struct iobuf *io, int fd) #ifdef IO_TLS int -iobuf_flush_tls(struct iobuf *io, void *tls) +iobuf_flush_tls(struct iobuf *io, struct tls *tls) { ssize_t s; @@ -400,55 +399,42 @@ iobuf_flush_tls(struct iobuf *io, void * } ssize_t -iobuf_write_tls(struct iobuf *io, void *tls) +iobuf_write_tls(struct iobuf *io, struct tls *tls) { struct ioqbuf *q; ssize_t n; q = io->outq; - n = SSL_write(tls, q->buf + q->rpos, q->wpos - q->rpos); - if (n <= 0) { - switch (SSL_get_error(tls, n)) { - case SSL_ERROR_WANT_READ: - return (IOBUF_WANT_READ); - case SSL_ERROR_WANT_WRITE: - return (IOBUF_WANT_WRITE); - case SSL_ERROR_ZERO_RETURN: /* connection closed */ - return (IOBUF_CLOSED); - case SSL_ERROR_SYSCALL: - if (ERR_peek_last_error()) - return (IOBUF_TLSERROR); - return (IOBUF_ERROR); - default: - return (IOBUF_TLSERROR); - } - } + + n = tls_write(tls, q->buf + q->rpos, q->wpos - q->rpos); + if (n == TLS_WANT_POLLIN) + return (IOBUF_WANT_READ); + else if (n == TLS_WANT_POLLOUT) + return (IOBUF_WANT_WRITE); + else if (n == 0) + return (IOBUF_CLOSED); + else if (n == -1) + return (IOBUF_ERROR); + iobuf_drain(io, n); return (n); } ssize_t -iobuf_read_tls(struct iobuf *io, void *tls) +iobuf_read_tls(struct iobuf *io, struct tls *tls) { ssize_t n; - n = SSL_read(tls, io->buf + io->wpos, iobuf_left(io)); - if (n < 0) { - switch (SSL_get_error(tls, n)) { - case SSL_ERROR_WANT_READ: - return (IOBUF_WANT_READ); - case SSL_ERROR_WANT_WRITE: - return (IOBUF_WANT_WRITE); - case SSL_ERROR_SYSCALL: - if (ERR_peek_last_error()) - return (IOBUF_TLSERROR); - return (IOBUF_ERROR); - default: - return (IOBUF_TLSERROR); - } - } else if (n == 0) + n = tls_read(tls, io->buf + io->wpos, iobuf_left(io)); + if (n == TLS_WANT_POLLIN) + return (IOBUF_WANT_READ); + else if (n == TLS_WANT_POLLOUT) + return (IOBUF_WANT_WRITE); + else if (n == 0) return (IOBUF_CLOSED); + else if (n == -1) + return (IOBUF_ERROR); io->wpos += n; Index: iobuf.h =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/iobuf.h,v retrieving revision 1.5 diff -u -p -r1.5 iobuf.h --- iobuf.h 12 Jun 2019 17:42:53 -0000 1.5 +++ iobuf.h 25 Jan 2021 11:14:33 -0000 @@ -35,11 +35,12 @@ struct iobuf { struct ioqbuf *outqlast; }; +struct tls; + #define IOBUF_WANT_READ -1 #define IOBUF_WANT_WRITE -2 #define IOBUF_CLOSED -3 #define IOBUF_ERROR -4 -#define IOBUF_TLSERROR -5 int iobuf_init(struct iobuf *, size_t, size_t); void iobuf_clear(struct iobuf *); @@ -53,7 +54,7 @@ size_t iobuf_left(struct iobuf *); char *iobuf_data(struct iobuf *); char *iobuf_getline(struct iobuf *, size_t *); ssize_t iobuf_read(struct iobuf *, int); -ssize_t iobuf_read_tls(struct iobuf *, void *); +ssize_t iobuf_read_tls(struct iobuf *, struct tls *); size_t iobuf_queued(struct iobuf *); void* iobuf_reserve(struct iobuf *, size_t); @@ -62,6 +63,6 @@ int iobuf_queuev(struct iobuf *, const s int iobuf_fqueue(struct iobuf *, const char *, ...); int iobuf_vfqueue(struct iobuf *, const char *, va_list); int iobuf_flush(struct iobuf *, int); -int iobuf_flush_tls(struct iobuf *, void *); +int iobuf_flush_tls(struct iobuf *, struct tls *); ssize_t iobuf_write(struct iobuf *, int); -ssize_t iobuf_write_tls(struct iobuf *, void *); +ssize_t iobuf_write_tls(struct iobuf *, struct tls *); Index: ioev.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/ioev.c,v retrieving revision 1.43 diff -u -p -r1.43 ioev.c --- ioev.c 23 Jan 2021 16:11:11 -0000 1.43 +++ ioev.c 25 Jan 2021 11:16:47 -0000 @@ -27,16 +27,14 @@ #include <stdlib.h> #include <string.h> #include <stdio.h> +#ifdef IO_TLS +#include <tls.h> +#endif #include <unistd.h> #include "ioev.h" #include "iobuf.h" -#ifdef IO_TLS -#include <openssl/err.h> -#include <openssl/ssl.h> -#endif - enum { IO_STATE_NONE, IO_STATE_CONNECT, @@ -65,7 +63,9 @@ struct io { int flags; int state; struct event ev; - void *tls; + struct tls *tls; + char *name; + const char *error; /* only valid immediately on callback */ }; @@ -85,9 +85,7 @@ void io_frame_enter(const char *, struct void io_frame_leave(struct io *); #ifdef IO_TLS -void ssl_error(const char *); /* XXX external */ - -static const char* io_tls_error(void); +void io_dispatch_handshake_tls(int, short, void *); void io_dispatch_accept_tls(int, short, void *); void io_dispatch_connect_tls(int, short, void *); void io_dispatch_read_tls(int, short, void *); @@ -111,10 +109,9 @@ io_strio(struct io *io) ssl[0] = '\0'; #ifdef IO_TLS if (io->tls) { - (void)snprintf(ssl, sizeof ssl, " tls=%s:%s:%d", - SSL_get_version(io->tls), - SSL_get_cipher_name(io->tls), - SSL_get_cipher_bits(io->tls, NULL)); + (void)snprintf(ssl, sizeof ssl, " tls=%s:%s", + tls_conn_version(io->tls), + tls_conn_cipher(io->tls)); } #endif @@ -272,7 +269,7 @@ io_free(struct io *io) current = NULL; #ifdef IO_TLS - SSL_free(io->tls); + tls_free(io->tls); io->tls = NULL; #endif @@ -283,6 +280,7 @@ io_free(struct io *io) io->sock = -1; } + free(io->name); iobuf_clear(&io->iobuf); free(io); } @@ -397,7 +395,7 @@ io_error(struct io *io) return io->error; } -void * +struct tls * io_tls(struct io *io) { return io->tls; @@ -807,57 +805,85 @@ io_dispatch_connect(int fd, short ev, vo } #ifdef IO_TLS - -static const char* -io_tls_error(void) +int +io_connect_tls(struct io *io, struct tls *tls, const char *hostname) { - static char buf[128]; - unsigned long e; + int mode; + + mode = io->flags & IO_RW; + if (mode != IO_WRITE) + errx(1, "io_connect_tls: expect IO_WRITE mode"); + + if (io->tls) + errx(1, "io_connect_tls: TLS already started"); - e = ERR_peek_last_error(); - if (e) { - ERR_error_string(e, buf); - return (buf); + if (hostname) { + if ((io->name = strdup(hostname)) == NULL) + err(1, "io_connect_tls"); } - return ("No TLS error"); + io->tls = tls; + io->state = IO_STATE_CONNECT_TLS; + io_reset(io, EV_WRITE, io_dispatch_connect_tls); + + return (0); } int -io_start_tls(struct io *io, void *tls) +io_accept_tls(struct io *io, struct tls *tls) { int mode; mode = io->flags & IO_RW; - if (mode == 0 || mode == IO_RW) - errx(1, "io_start_tls(): full-duplex or unset"); + if (mode != IO_READ) + errx(1, "io_connect_tls: expect IO_READ mode"); if (io->tls) errx(1, "io_start_tls(): TLS already started"); io->tls = tls; + io->state = IO_STATE_ACCEPT_TLS; + io_reset(io, EV_READ, io_dispatch_accept_tls); + + return (0); +} - if (SSL_set_fd(io->tls, io->sock) == 0) { - ssl_error("io_start_tls:SSL_set_fd"); - return (-1); +void +io_dispatch_handshake_tls(int fd, short event, void *humppa) +{ + struct io *io = humppa; + int ret; + + io_frame_enter("io_dispatch_handshake_tls", io, event); + + if (event == EV_TIMEOUT) { + io_callback(io, IO_TIMEOUT); + goto leave; } - if (mode == IO_WRITE) { - io->state = IO_STATE_CONNECT_TLS; - SSL_set_connect_state(io->tls); - io_reset(io, EV_WRITE, io_dispatch_connect_tls); - } else { - io->state = IO_STATE_ACCEPT_TLS; - SSL_set_accept_state(io->tls); - io_reset(io, EV_READ, io_dispatch_accept_tls); + if ((ret = tls_handshake(io->tls)) == 0) { + io->state = IO_STATE_UP; + io_callback(io, IO_TLSREADY); + goto leave; + } + if (ret == TLS_WANT_POLLIN) + io_reset(io, EV_READ, io_dispatch_handshake_tls); + else if (ret == TLS_WANT_POLLOUT) + io_reset(io, EV_WRITE, io_dispatch_handshake_tls); + else { + io->error = tls_error(io->tls); + io_callback(io, IO_ERROR); } - return (0); + leave: + io_frame_leave(io); + return; } void io_dispatch_accept_tls(int fd, short event, void *humppa) { struct io *io = humppa; + struct tls *cctx = NULL; int ret; io_frame_enter("io_dispatch_accept_tls", io, event); @@ -867,28 +893,17 @@ io_dispatch_accept_tls(int fd, short eve goto leave; } - if ((ret = SSL_accept(io->tls)) > 0) { - io->state = IO_STATE_UP; - io_callback(io, IO_TLSREADY); + if ((ret = tls_accept_socket(io->tls, &cctx, io->sock)) == 0) { + io->tls = cctx; + io_reset(io, EV_READ|EV_WRITE, io_dispatch_handshake_tls); goto leave; } + io->error = tls_error(io->tls); + io_callback(io, IO_ERROR); - switch (SSL_get_error(io->tls, ret)) { - case SSL_ERROR_WANT_READ: - io_reset(io, EV_READ, io_dispatch_accept_tls); - break; - case SSL_ERROR_WANT_WRITE: - io_reset(io, EV_WRITE, io_dispatch_accept_tls); - break; - default: - io->error = io_tls_error(); - ssl_error("io_dispatch_accept_tls:SSL_accept"); - io_callback(io, IO_ERROR); - break; - } - - leave: + leave: io_frame_leave(io); + return; } void @@ -904,27 +919,15 @@ io_dispatch_connect_tls(int fd, short ev goto leave; } - if ((ret = SSL_connect(io->tls)) > 0) { - io->state = IO_STATE_UP; - io_callback(io, IO_TLSREADY); + if ((ret = tls_connect_socket(io->tls, io->sock, io->name)) == 0) { + io_reset(io, EV_READ|EV_WRITE, io_dispatch_handshake_tls); goto leave; } - switch (SSL_get_error(io->tls, ret)) { - case SSL_ERROR_WANT_READ: - io_reset(io, EV_READ, io_dispatch_connect_tls); - break; - case SSL_ERROR_WANT_WRITE: - io_reset(io, EV_WRITE, io_dispatch_connect_tls); - break; - default: - io->error = io_tls_error(); - ssl_error("io_dispatch_connect_ssl:SSL_connect"); - io_callback(io, IO_TLSERROR); - break; - } + io->error = tls_error(io->tls); + io_callback(io, IO_ERROR); - leave: + leave: io_frame_leave(io); } @@ -932,7 +935,7 @@ void io_dispatch_read_tls(int fd, short event, void *humppa) { struct io *io = humppa; - int n, saved_errno; + int n; io_frame_enter("io_dispatch_read_tls", io, event); @@ -943,7 +946,7 @@ io_dispatch_read_tls(int fd, short event again: iobuf_normalize(&io->iobuf); - switch ((n = iobuf_read_tls(&io->iobuf, (SSL*)io->tls))) { + switch ((n = iobuf_read_tls(&io->iobuf, io->tls))) { case IOBUF_WANT_READ: io_reset(io, EV_READ, io_dispatch_read_tls); break; @@ -954,20 +957,13 @@ again: io_callback(io, IO_DISCONNECTED); break; case IOBUF_ERROR: - saved_errno = errno; - io->error = strerror(errno); - errno = saved_errno; - io_callback(io, IO_ERROR); - break; - case IOBUF_TLSERROR: - io->error = io_tls_error(); - ssl_error("io_dispatch_read_tls:SSL_read"); + io->error = tls_error(io->tls); io_callback(io, IO_ERROR); break; default: io_debug("io_dispatch_read_tls(...) -> r=%d\n", n); io_callback(io, IO_DATAIN); - if (current == io && IO_READING(io) && SSL_pending(io->tls)) + if (current == io && IO_READING(io)) goto again; } @@ -979,7 +975,7 @@ void io_dispatch_write_tls(int fd, short event, void *humppa) { struct io *io = humppa; - int n, saved_errno; + int n; size_t w2, w; io_frame_enter("io_dispatch_write_tls", io, event); @@ -990,7 +986,7 @@ io_dispatch_write_tls(int fd, short even } w = io_queued(io); - switch ((n = iobuf_write_tls(&io->iobuf, (SSL*)io->tls))) { + switch ((n = iobuf_write_tls(&io->iobuf, io->tls))) { case IOBUF_WANT_READ: io_reset(io, EV_READ, io_dispatch_write_tls); break; @@ -1001,14 +997,7 @@ io_dispatch_write_tls(int fd, short even io_callback(io, IO_DISCONNECTED); break; case IOBUF_ERROR: - saved_errno = errno; - io->error = strerror(errno); - errno = saved_errno; - io_callback(io, IO_ERROR); - break; - case IOBUF_TLSERROR: - io->error = io_tls_error(); - ssl_error("io_dispatch_write_tls:SSL_write"); + io->error = tls_error(io->tls); io_callback(io, IO_ERROR); break; default: Index: ioev.h =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/ioev.h,v retrieving revision 1.18 diff -u -p -r1.18 ioev.h --- ioev.h 11 Sep 2019 04:19:19 -0000 1.18 +++ ioev.h 18 Jan 2021 19:58:19 -0000 @@ -18,7 +18,6 @@ enum { IO_CONNECTED = 0, /* connection successful */ IO_TLSREADY, /* TLS started successfully */ - IO_TLSERROR, /* XXX - needs more work */ IO_DATAIN, /* new data in input buffer */ IO_LOWAT, /* output queue running low */ IO_DISCONNECTED, /* error? */ @@ -30,6 +29,7 @@ enum { #define IO_OUT 0x02 struct io; +struct tls; void io_set_nonblocking(int); void io_set_nolinger(int); @@ -46,11 +46,12 @@ void io_pause(struct io *, int); void io_resume(struct io *, int); void io_reload(struct io *); int io_connect(struct io *, const struct sockaddr *, const struct sockaddr *); -int io_start_tls(struct io *, void *); +int io_connect_tls(struct io *, struct tls *, const char *); +int io_accept_tls(struct io *, struct tls *); const char* io_strio(struct io *); const char* io_strevent(int); const char* io_error(struct io *); -void* io_tls(struct io *); +struct tls* io_tls(struct io *); int io_fileno(struct io *); int io_paused(struct io *, int); Index: mta.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/mta.c,v retrieving revision 1.234 diff -u -p -r1.234 mta.c --- mta.c 21 Dec 2019 10:34:07 -0000 1.234 +++ mta.c 22 Jan 2021 09:00:21 -0000 @@ -38,10 +38,12 @@ #include <stdlib.h> #include <string.h> #include <time.h> +#include <tls.h> #include <unistd.h> #include "smtpd.h" #include "log.h" +#include "ssl.h" #define MAXERROR_PER_ROUTE 4 @@ -57,6 +59,7 @@ #define RELAY_ONHOLD 0x01 #define RELAY_HOLDQ 0x02 +static void mta_setup_dispatcher(struct dispatcher *); static void mta_handle_envelope(struct envelope *, const char *); static void mta_query_smarthost(struct envelope *); static void mta_on_smarthost(struct envelope *, const char *); @@ -138,6 +141,12 @@ int mta_is_blocked(struct mta_source *, static int mta_block_cmp(const struct mta_block *, const struct mta_block *); SPLAY_PROTOTYPE(mta_block_tree, mta_block, entry, mta_block_cmp); +/* + * This function is not publicy exported because it is a hack until libtls + * has a proper privsep setup + */ +void tls_config_use_fake_private_key(struct tls_config *config); + static struct mta_relay_tree relays; static struct mta_domain_tree domains; static struct mta_host_tree hosts; @@ -463,6 +472,70 @@ mta_imsg(struct mproc *p, struct imsg *i void mta_postfork(void) { + struct dispatcher *dispatcher; + const char *key; + void *iter; + + iter = NULL; + while (dict_iter(env->sc_dispatchers, &iter, &key, (void **)&dispatcher)) { + log_debug("%s: %s", __func__, key); + mta_setup_dispatcher(dispatcher); + } +} + +static void +mta_setup_dispatcher(struct dispatcher *dispatcher) +{ + struct dispatcher_remote *remote; + static const char *dheparams[] = { "none", "auto", "legacy" }; + struct tls_config *config; + struct pki *pki; + struct ca *ca; + + if (dispatcher->type != DISPATCHER_REMOTE) + return; + + remote = &dispatcher->u.remote; + + if ((config = tls_config_new()) == NULL) + fatal("smtpd: tls_config_new"); + + if (env->sc_tls_ciphers) { + if (tls_config_set_ciphers(config, env->sc_tls_ciphers) == -1) + err(1, "%s", tls_config_error(config)); + } + + if (remote->pki) { + pki = dict_get(env->sc_pki_dict, remote->pki); + if (pki == NULL) + err(1, "client pki \"%s\" not found ", remote->pki); + + tls_config_set_dheparams(config, dheparams[pki->pki_dhe]); + tls_config_use_fake_private_key(config); + if (tls_config_set_keypair_mem(config, pki->pki_cert, + pki->pki_cert_len, NULL, 0) == -1) + fatal("tls_config_set_keypair_mem"); + } + + if (remote->ca) { + ca = dict_get(env->sc_ca_dict, remote->ca); + if (tls_config_set_ca_mem(config, ca->ca_cert, ca->ca_cert_len) + == -1) + fatal("tls_config_set_ca_mem"); + } + else if (tls_config_set_ca_file(config, tls_default_ca_cert_file()) + == -1) + fatal("tls_config_set_ca_file"); + + if (remote->tls_noverify) { + tls_config_insecure_noverifycert(config); + tls_config_insecure_noverifyname(config); + tls_config_insecure_noverifytime(config); + } + else + tls_config_verify(config); + + remote->tls_config = config; } void Index: mta_session.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/mta_session.c,v retrieving revision 1.138 diff -u -p -r1.138 mta_session.c --- mta_session.c 21 Dec 2020 11:44:07 -0000 1.138 +++ mta_session.c 19 Jan 2021 10:09:32 -0000 @@ -43,6 +43,7 @@ #include <stdlib.h> #include <string.h> #include <time.h> +#include <tls.h> #include <unistd.h> #include "smtpd.h" @@ -153,11 +154,8 @@ static void mta_send(struct mta_session static ssize_t mta_queue_data(struct mta_session *); static void mta_response(struct mta_session *, char *); static const char * mta_strstate(int); -static void mta_cert_init(struct mta_session *); -static void mta_cert_init_cb(void *, int, const char *, const void *, size_t); -static void mta_cert_verify(struct mta_session *); -static void mta_cert_verify_cb(void *, int); -static void mta_tls_verified(struct mta_session *); +static void mta_tls_init(struct mta_session *); +static void mta_tls_started(struct mta_session *); static struct mta_session *mta_tree_pop(struct tree *, uint64_t); static const char * dsn_strret(enum dsn_ret); static const char * dsn_strnotify(uint8_t); @@ -974,7 +972,7 @@ mta_response(struct mta_session *s, char return; } - mta_cert_init(s); + mta_tls_init(s); break; case MTA_AUTH_PLAIN: @@ -1229,7 +1227,7 @@ mta_io(struct io *io, int evt, void *arg if (s->use_smtps) { io_set_write(io); - mta_cert_init(s); + mta_tls_init(s); } else { mta_enter_state(s, MTA_BANNER); @@ -1239,13 +1237,14 @@ mta_io(struct io *io, int evt, void *arg case IO_TLSREADY: log_info("%016"PRIx64" mta tls ciphers=%s", - s->id, ssl_to_text(io_tls(s->io))); + s->id, tls_to_text(io_tls(s->io))); s->flags |= MTA_TLS; + if (!s->relay->dispatcher->u.remote.tls_noverify) + s->flags |= MTA_TLS_VERIFIED; + mta_tls_started(s); mta_report_link_tls(s, - ssl_to_text(io_tls(s->io))); - - mta_cert_verify(s); + tls_to_text(io_tls(s->io))); break; case IO_DATAIN: @@ -1378,7 +1377,6 @@ mta_io(struct io *io, int evt, void *arg break; case IO_ERROR: - case IO_TLSERROR: log_debug("debug: mta: %p: IO error: %s", s, io_error(io)); if (s->state == MTA_STARTTLS && s->use_smtp_tls) { @@ -1579,152 +1577,42 @@ mta_error(struct mta_session *s, const c } static void -mta_cert_init(struct mta_session *s) -{ - const char *name; - int fallback; - - if (s->relay->pki_name) { - name = s->relay->pki_name; - fallback = 0; - } - else { - name = s->helo; - fallback = 1; - } - - if (cert_init(name, fallback, mta_cert_init_cb, s)) { - tree_xset(&wait_tls_init, s->id, s); - s->flags |= MTA_WAIT; - } -} - -static void -mta_cert_init_cb(void *arg, int status, const char *name, const void *cert, - size_t cert_len) +mta_tls_init(struct mta_session *s) { - struct mta_session *s = arg; - void *ssl; - char *xname = NULL, *xcert = NULL; - union { - struct in_addr in4; - struct in6_addr in6; - } addrbuf; - - if (s->flags & MTA_WAIT) - mta_tree_pop(&wait_tls_init, s->id); + struct tls_config *tls_config; + struct tls *tls; - if (status == CA_FAIL && s->relay->pki_name) { - log_info("%016"PRIx64" mta closing reason=ca-failure", s->id); + if ((tls = tls_client()) == NULL) { + log_info("%016"PRIx64" mta closing reason=tls-failure", s->id); mta_free(s); return; } - if (name) - xname = xstrdup(name); - if (cert) - xcert = xmemdup(cert, cert_len); - ssl = ssl_mta_init(xname, xcert, cert_len, env->sc_tls_ciphers); - free(xname); - free(xcert); - if (ssl == NULL) - fatal("mta: ssl_mta_init"); - - /* - * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not - * permitted in "HostName". - */ - if (s->relay->domain->as_host == 1) { - if (inet_pton(AF_INET, s->relay->domain->name, &addrbuf) != 1 && - inet_pton(AF_INET6, s->relay->domain->name, &addrbuf) != 1) { - log_debug("%016"PRIx64" mta tls setting SNI name=%s", - s->id, s->relay->domain->name); - if (SSL_set_tlsext_host_name(ssl, s->relay->domain->name) == 0) - log_warnx("%016"PRIx64" mta tls setting SNI failed", - s->id); - } - } - - io_start_tls(s->io, ssl); -} - -static void -mta_cert_verify(struct mta_session *s) -{ - const char *name; - int fallback; - - if (s->relay->ca_name) { - name = s->relay->ca_name; - fallback = 0; - } - else { - name = s->helo; - fallback = 1; - } - - if (cert_verify(io_tls(s->io), name, fallback, mta_cert_verify_cb, s)) { - tree_xset(&wait_tls_verify, s->id, s); - io_pause(s->io, IO_IN); - s->flags |= MTA_WAIT; - } -} - -static void -mta_cert_verify_cb(void *arg, int status) -{ - struct mta_session *s = arg; - int match, resume = 0; - X509 *cert; - - if (s->flags & MTA_WAIT) { - mta_tree_pop(&wait_tls_verify, s->id); - resume = 1; - } - - if (status == CERT_OK) { - cert = SSL_get_peer_certificate(io_tls(s->io)); - if (!cert) - status = CERT_NOCERT; - else { - match = 0; - (void)ssl_check_name(cert, s->mxname, &match); - X509_free(cert); - if (!match) { - log_info("%016"PRIx64" mta " - "ssl_check_name: no match for '%s' in cert", - s->id, s->mxname); - status = CERT_INVALID; - } - } - } - - if (status == CERT_OK) - s->flags |= MTA_TLS_VERIFIED; - else if (s->relay->flags & RELAY_TLS_VERIFY) { - errno = 0; - mta_error(s, "SSL certificate check failed"); + tls_config = s->relay->dispatcher->u.remote.tls_config; + if (tls_configure(tls, tls_config) == -1) { + log_info("%016"PRIx64" mta closing reason=tls-failure", s->id); + tls_free(tls); mta_free(s); return; } - mta_tls_verified(s); - if (resume) - io_resume(s->io, IO_IN); + io_connect_tls(s->io, tls, s->route->dst->ptrname); } static void -mta_tls_verified(struct mta_session *s) +mta_tls_started(struct mta_session *s) { - X509 *x; - - x = SSL_get_peer_certificate(io_tls(s->io)); - if (x) { - log_info("%016"PRIx64" mta " - "server-cert-check result=\"%s\"", - s->id, - (s->flags & MTA_TLS_VERIFIED) ? "success" : "failure"); - X509_free(x); + if (tls_peer_cert_provided(io_tls(s->io))) { + log_info("%016"PRIx64" mta " + "cert-check result=\"%s\" fingerprint=\"%s\"", + s->id, + (s->flags & MTA_TLS_VERIFIED) ? "valid" : "unverified", + tls_peer_cert_hash(io_tls(s->io))); + } + else { + log_info("%016"PRIx64" smtp " + "cert-check result=\"no certificate presented\"", + s->id); } if (s->use_smtps) { Index: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/parse.y,v retrieving revision 1.284 diff -u -p -r1.284 parse.y --- parse.y 23 Jan 2021 16:11:11 -0000 1.284 +++ parse.y 25 Jan 2021 07:19:15 -0000 @@ -128,13 +128,15 @@ enum listen_options { LO_PROXY = 0x008000, }; +#define PKI_MAX 32 static struct listen_opts { char *ifx; int family; in_port_t port; uint16_t ssl; char *filtername; - char *pki; + char *pki[PKI_MAX]; + int pkicount; char *ca; uint16_t auth; struct table *authtable; @@ -2316,12 +2318,11 @@ opt_if_listen : INET4 { listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE|F_TLS_VERIFY; } | PKI STRING { - if (listen_opts.options & LO_PKI) { - yyerror("pki already specified"); + if (listen_opts.pkicount == PKI_MAX) { + yyerror("too many pki specified"); YYERROR; } - listen_opts.options |= LO_PKI; - listen_opts.pki = $2; + listen_opts.pki[listen_opts.pkicount++] = $2; } | CA STRING { if (listen_opts.options & LO_CA) { @@ -3221,8 +3222,10 @@ create_if_listener(struct listen_opts *l if (lo->auth != 0 && !lo->ssl) errx(1, "invalid listen option: auth requires tls/smtps"); - if (lo->pki && !lo->ssl) + if (lo->pkicount && !lo->ssl) errx(1, "invalid listen option: pki requires tls/smtps"); + if (lo->pkicount == 0 && lo->ssl) + errx(1, "invalid listen option: pki required for tls/smtps"); flags = lo->flags; @@ -3259,6 +3262,8 @@ create_if_listener(struct listen_opts *l static void config_listener(struct listener *h, struct listen_opts *lo) { + int i; + h->fd = -1; h->port = lo->port; h->flags = lo->flags; @@ -3273,17 +3278,19 @@ config_listener(struct listener *h, str sizeof(h->filter_name)); } - h->pki_name[0] = '\0'; - if (lo->authtable != NULL) (void)strlcpy(h->authtable, lo->authtable->t_name, sizeof(h->authtable)); - if (lo->pki != NULL) { - if (!lowercase(h->pki_name, lo->pki, sizeof(h->pki_name))) { - log_warnx("pki name too long: %s", lo->pki); - fatalx(NULL); - } - if (dict_get(conf->sc_pki_dict, h->pki_name) == NULL) { - log_warnx("pki name not found: %s", lo->pki); + + h->pkicount = lo->pkicount; + if (h->pkicount) { + h->pki = calloc(h->pkicount, sizeof(*h->pki)); + if (h->pki == NULL) + fatal("calloc"); + } + for (i = 0; i < lo->pkicount; i++) { + h->pki[i] = dict_get(conf->sc_pki_dict, lo->pki[i]); + if (h->pki[i] == NULL) { + log_warnx("pki name not found: %s", lo->pki[i]); fatalx(NULL); } } Index: smtp.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtp.c,v retrieving revision 1.166 diff -u -p -r1.166 smtp.c --- smtp.c 10 Aug 2019 16:07:01 -0000 1.166 +++ smtp.c 22 Jan 2021 09:00:46 -0000 @@ -34,6 +34,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <tls.h> #include <unistd.h> #include <openssl/ssl.h> @@ -50,7 +51,7 @@ static void smtp_dropped(struct listener static int smtp_enqueue(void); static int smtp_can_accept(void); static void smtp_setup_listeners(void); -static int smtp_sni_callback(SSL *, int *, void *); +static void smtp_setup_listener_tls(struct listener *); int proxy_session(struct listener *listener, int sock, @@ -62,6 +63,11 @@ proxy_session(struct listener *listener, static void smtp_accepted(struct listener *, int, const struct sockaddr_storage *, struct io *); +/* + * This function are not publicy exported because it is a hack until libtls + * has a proper privsep setup + */ +void tls_config_use_fake_private_key(struct tls_config *config); #define SMTP_FD_RESERVE 5 static size_t sessions; @@ -145,6 +151,10 @@ smtp_setup_listeners(void) } fatal("smtpd: socket"); } + + if (l->flags & F_SSL) + smtp_setup_listener_tls(l); + opt = 1; if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) @@ -155,19 +165,77 @@ smtp_setup_listeners(void) } static void +smtp_setup_listener_tls(struct listener *l) +{ + static const char *dheparams[] = { "none", "auto", "legacy" }; + struct tls_config *config; + struct pki *pki; + struct ca *ca; + int i; + + if ((config = tls_config_new()) == NULL) + fatal("smtpd: tls_config_new"); + + if (env->sc_tls_ciphers && + tls_config_set_ciphers(config, env->sc_tls_ciphers) == -1) + err(1, "%s", tls_config_error(config)); + + pki = l->pki[0]; + if (pki == NULL) + fatal("no pki defined"); + + if (tls_config_set_dheparams(config, dheparams[pki->pki_dhe]) == -1) + fatal("tls_config_set_dheparams"); + + tls_config_use_fake_private_key(config); + for (i = 0; i < l->pkicount; i++) { + pki = l->pki[i]; + if (i == 0) { + if (tls_config_set_keypair_mem(config, pki->pki_cert, + pki->pki_cert_len, NULL, 0) == -1) + fatal("tls_config_set_keypair_mem"); + } else { + if (tls_config_add_keypair_mem(config, pki->pki_cert, + pki->pki_cert_len, NULL, 0) == -1) + fatal("tls_config_add_keypair_mem"); + } + } + free(l->pki); + l->pkicount = 0; + + if (l->ca_name[0]) { + ca = dict_get(env->sc_ca_dict, l->ca_name); + if (tls_config_set_ca_mem(config, ca->ca_cert, ca->ca_cert_len) + == -1) + fatal("tls_config_set_ca_mem"); + } + else if (tls_config_set_ca_file(config, tls_default_ca_cert_file()) + == -1) + fatal("tls_config_set_ca_file"); + + if (l->flags & F_TLS_VERIFY) + tls_config_verify_client(config); + else + tls_config_verify_client_optional(config); + + l->tls = tls_server(); + if (l->tls == NULL) + fatal("tls_server"); + if (tls_configure(l->tls, config) == -1) { + fatal("tls_configure: %s", tls_error(l->tls)); + } + tls_config_free(config); +} + + +static void smtp_setup_events(void) { struct listener *l; - struct pki *pki; - SSL_CTX *ssl_ctx; - void *iter; - const char *k; TAILQ_FOREACH(l, env->sc_listeners, entry) { - log_debug("debug: smtp: listen on %s port %d flags 0x%01x" - " pki \"%s\"" - " ca \"%s\"", ss_to_text(&l->ss), ntohs(l->port), - l->flags, l->pki_name, l->ca_name); + log_debug("debug: smtp: listen on %s port %d flags 0x%01x", + ss_to_text(&l->ss), ntohs(l->port), l->flags); io_set_nonblocking(l->fd); if (listen(l->fd, SMTPD_BACKLOG) == -1) @@ -178,14 +246,6 @@ smtp_setup_events(void) event_add(&l->ev, NULL); } - iter = NULL; - while (dict_iter(env->sc_pki_dict, &iter, &k, (void **)&pki)) { - if (!ssl_setup((SSL_CTX **)&ssl_ctx, pki, smtp_sni_callback, - env->sc_tls_ciphers)) - fatal("smtp_setup_events: ssl_setup failure"); - dict_xset(env->sc_ssl_dict, k, ssl_ctx); - } - purge_config(PURGE_PKI_KEYS); maxsessions = (getdtablesize() - getdtablecount()) / 2 - SMTP_FD_RESERVE; @@ -317,22 +377,6 @@ smtp_collect(void) env->sc_flags &= ~SMTPD_SMTP_DISABLED; smtp_resume(); } -} - -static int -smtp_sni_callback(SSL *ssl, int *ad, void *arg) -{ - const char *sn; - void *ssl_ctx; - - sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - if (sn == NULL) - return SSL_TLSEXT_ERR_NOACK; - ssl_ctx = dict_get(env->sc_ssl_dict, sn); - if (ssl_ctx == NULL) - return SSL_TLSEXT_ERR_NOACK; - SSL_set_SSL_CTX(ssl, ssl_ctx); - return SSL_TLSEXT_ERR_OK; } static void Index: smtp.h =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtp.h,v retrieving revision 1.3 diff -u -p -r1.3 smtp.h --- smtp.h 2 Sep 2019 20:05:21 -0000 1.3 +++ smtp.h 19 Jan 2021 09:41:16 -0000 @@ -46,6 +46,7 @@ struct smtp_params { /* TLS options */ int tls_req; /* requested TLS mode */ int tls_verify; /* need valid server certificate */ + const char *tls_servname; /* SNI */ /* SMTP options */ int lmtp; /* use LMTP protocol */ Index: smtp_client.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtp_client.c,v retrieving revision 1.14 diff -u -p -r1.14 smtp_client.c --- smtp_client.c 24 Apr 2020 11:34:07 -0000 1.14 +++ smtp_client.c 19 Jan 2021 09:44:34 -0000 @@ -187,7 +187,7 @@ smtp_cert_verified(struct smtp_client *p void smtp_set_tls(struct smtp_client *proto, void *ctx) { - io_start_tls(proto->io, ctx); + io_connect_tls(proto->io, ctx, proto->params.tls_servname); } void @@ -624,8 +624,13 @@ smtp_client_io(struct io *io, int evt, v case IO_TLSREADY: proto->flags |= FLAG_TLS; - io_pause(proto->io, IO_IN); - smtp_verify_server_cert(proto->tag, proto, io_tls(proto->io)); + if (proto->state == STATE_INIT) + smtp_client_state(proto, STATE_BANNER); + else { + /* Clear extensions before re-issueing an EHLO command. */ + proto->ext = 0; + smtp_client_state(proto, STATE_EHLO); + } break; case IO_DATAIN: @@ -646,10 +651,6 @@ smtp_client_io(struct io *io, int evt, v break; case IO_ERROR: - smtp_client_abort(proto, FAIL_CONN, io_error(io)); - break; - - case IO_TLSERROR: smtp_client_abort(proto, FAIL_CONN, io_error(io)); break; Index: smtp_session.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtp_session.c,v retrieving revision 1.428 diff -u -p -r1.428 smtp_session.c --- smtp_session.c 21 Dec 2020 11:44:07 -0000 1.428 +++ smtp_session.c 19 Jan 2021 11:06:22 -0000 @@ -38,6 +38,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <tls.h> #include <unistd.h> #include <vis.h> @@ -184,7 +185,8 @@ static void smtp_getnameinfo_cb(void *, static void smtp_getaddrinfo_cb(void *, int, struct addrinfo *); static void smtp_connected(struct smtp_session *); static void smtp_send_banner(struct smtp_session *); -static void smtp_tls_verified(struct smtp_session *); +static void smtp_tls_init(struct smtp_session *); +static void smtp_tls_started(struct smtp_session *); static void smtp_io(struct io *, int, void *); static void smtp_enter_state(struct smtp_session *, int); static void smtp_reply(struct smtp_session *, char *, ...); @@ -193,10 +195,6 @@ static void smtp_rfc4954_auth_plain(stru static void smtp_rfc4954_auth_login(struct smtp_session *, char *); static void smtp_free(struct smtp_session *, const char *); static const char *smtp_strstate(int); -static void smtp_cert_init(struct smtp_session *); -static void smtp_cert_init_cb(void *, int, const char *, const void *, size_t); -static void smtp_cert_verify(struct smtp_session *); -static void smtp_cert_verify_cb(void *, int); static void smtp_auth_failure_pause(struct smtp_session *); static void smtp_auth_failure_resume(int, short, void *); @@ -1066,17 +1064,26 @@ smtp_session_imsg(struct mproc *p, struc } static void -smtp_tls_verified(struct smtp_session *s) +smtp_tls_init(struct smtp_session *s) { - X509 *x; + io_set_read(s->io); + io_accept_tls(s->io, s->listener->tls); +} - x = SSL_get_peer_certificate(io_tls(s->io)); - if (x) { +static void +smtp_tls_started(struct smtp_session *s) +{ + if (tls_peer_cert_provided(io_tls(s->io))) { log_info("%016"PRIx64" smtp " - "client-cert-check result=\"%s\"", + "cert-check result=\"%s\" fingerprint=\"%s\"", s->id, - (s->flags & SF_VERIFIED) ? "success" : "failure"); - X509_free(x); + (s->flags & SF_VERIFIED) ? "verified" : "unchecked", + tls_peer_cert_hash(io_tls(s->io))); + } + else { + log_info("%016"PRIx64" smtp " + "cert-check result=\"no certificate presented\"", + s->id); } if (s->listener->flags & F_SMTPS) { @@ -1105,14 +1112,16 @@ smtp_io(struct io *io, int evt, void *ar case IO_TLSREADY: log_info("%016"PRIx64" smtp tls ciphers=%s", - s->id, ssl_to_text(io_tls(s->io))); + s->id, tls_to_text(io_tls(s->io))); - smtp_report_link_tls(s, ssl_to_text(io_tls(s->io))); + smtp_report_link_tls(s, tls_to_text(io_tls(s->io))); s->flags |= SF_SECURE; + if (s->listener->flags & F_TLS_VERIFY) + s->flags |= SF_VERIFIED; s->helo[0] = '\0'; - smtp_cert_verify(s); + smtp_tls_started(s); break; case IO_DATAIN: @@ -1193,7 +1202,7 @@ smtp_io(struct io *io, int evt, void *ar /* Wait for the client to start tls */ if (s->state == STATE_TLS) { - smtp_cert_init(s); + smtp_tls_init(s); break; } @@ -2071,7 +2080,7 @@ static void smtp_proceed_connected(struct smtp_session *s) { if (s->listener->flags & F_SMTPS) - smtp_cert_init(s); + smtp_tls_init(s); else smtp_send_banner(s); } @@ -2261,112 +2270,6 @@ smtp_mailaddr(struct mailaddr *maddr, ch } static void -smtp_cert_init(struct smtp_session *s) -{ - const char *name; - int fallback; - - if (s->listener->pki_name[0]) { - name = s->listener->pki_name; - fallback = 0; - } - else { - name = s->smtpname; - fallback = 1; - } - - if (cert_init(name, fallback, smtp_cert_init_cb, s)) - tree_xset(&wait_ssl_init, s->id, s); -} - -static void -smtp_cert_init_cb(void *arg, int status, const char *name, const void *cert, - size_t cert_len) -{ - struct smtp_session *s = arg; - void *ssl, *ssl_ctx; - - tree_pop(&wait_ssl_init, s->id); - - if (status == CA_FAIL) { - log_info("%016"PRIx64" smtp disconnected " - "reason=ca-failure", - s->id); - smtp_free(s, "CA failure"); - return; - } - - ssl_ctx = dict_get(env->sc_ssl_dict, name); - ssl = ssl_smtp_init(ssl_ctx, s->listener->flags & F_TLS_VERIFY); - io_set_read(s->io); - io_start_tls(s->io, ssl); -} - -static void -smtp_cert_verify(struct smtp_session *s) -{ - const char *name; - int fallback; - - if (s->listener->ca_name[0]) { - name = s->listener->ca_name; - fallback = 0; - } - else { - name = s->smtpname; - fallback = 1; - } - - if (cert_verify(io_tls(s->io), name, fallback, smtp_cert_verify_cb, s)) { - tree_xset(&wait_ssl_verify, s->id, s); - io_pause(s->io, IO_IN); - } -} - -static void -smtp_cert_verify_cb(void *arg, int status) -{ - struct smtp_session *s = arg; - const char *reason = NULL; - int resume; - - resume = tree_pop(&wait_ssl_verify, s->id) != NULL; - - switch (status) { - case CERT_OK: - reason = "cert-ok"; - s->flags |= SF_VERIFIED; - break; - case CERT_NOCA: - reason = "no-ca"; - break; - case CERT_NOCERT: - reason = "no-client-cert"; - break; - case CERT_INVALID: - reason = "cert-invalid"; - break; - default: - reason = "cert-check-failed"; - break; - } - - log_debug("smtp: %p: smtp_cert_verify_cb: %s", s, reason); - - if (!(s->flags & SF_VERIFIED) && (s->listener->flags & F_TLS_VERIFY)) { - log_info("%016"PRIx64" smtp disconnected " - " reason=%s", s->id, - reason); - smtp_free(s, "SSL certificate check failed"); - return; - } - - smtp_tls_verified(s); - if (resume) - io_resume(s->io, IO_IN); -} - -static void smtp_auth_failure_resume(int fd, short event, void *p) { struct smtp_session *s = p; @@ -2844,7 +2747,6 @@ static void smtp_message_begin(struct smtp_tx *tx) { struct smtp_session *s; - X509 *x; int (*m_printf)(struct smtp_tx *, const char *, ...); m_printf = smtp_message_printf; @@ -2879,13 +2781,11 @@ smtp_message_begin(struct smtp_tx *tx) tx->msgid); if (s->flags & SF_SECURE) { - x = SSL_get_peer_certificate(io_tls(s->io)); m_printf(tx, " (%s:%s:%d:%s)", - SSL_get_version(io_tls(s->io)), - SSL_get_cipher_name(io_tls(s->io)), - SSL_get_cipher_bits(io_tls(s->io), NULL), - (s->flags & SF_VERIFIED) ? "YES" : (x ? "FAIL" : "NO")); - X509_free(x); + tls_conn_version(io_tls(s->io)), + tls_conn_cipher(io_tls(s->io)), + tls_conn_cipher_strength(io_tls(s->io)), + (s->flags & SF_VERIFIED) ? "YES" : "NO"); if (s->listener->flags & F_RECEIVEDAUTH) { m_printf(tx, " auth=%s", Index: smtpc.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpc.c,v retrieving revision 1.13 diff -u -p -r1.13 smtpc.c --- smtpc.c 29 Dec 2020 12:17:54 -0000 1.13 +++ smtpc.c 4 Feb 2021 19:56:51 -0000 @@ -29,6 +29,7 @@ #include <stdlib.h> #include <string.h> #include <syslog.h> +#include <tls.h> #include <unistd.h> #include <openssl/ssl.h> @@ -48,8 +49,7 @@ static struct addrinfo *res0, *ai; static struct smtp_params params; static struct smtp_mail mail; static const char *servname = NULL; - -static SSL_CTX *ssl_ctx; +static struct tls_config *tls_config; static void usage(void) @@ -156,16 +156,20 @@ main(int argc, char **argv) mail.rcptcount = argc; } - ssl_init(); + tls_init(); event_init(); - ssl_ctx = ssl_ctx_create(NULL, NULL, 0, NULL); - if (!SSL_CTX_load_verify_locations(ssl_ctx, - X509_get_default_cert_file(), NULL)) - fatal("SSL_CTX_load_verify_locations"); - if (!SSL_CTX_set_ssl_version(ssl_ctx, SSLv23_client_method())) - fatal("SSL_CTX_set_ssl_version"); - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE , NULL); + tls_config = tls_config_new(); + if (tls_config == NULL) + fatal("tls_config_new"); + if (tls_config_set_ca_file(tls_config, tls_default_ca_cert_file()) == -1) + fatal("tls_set_ca_file"); + if (!params.tls_verify) { + tls_config_insecure_noverifycert(tls_config); + tls_config_insecure_noverifyname(tls_config); + tls_config_insecure_noverifytime(tls_config); + } else + tls_config_verify(tls_config); if (pledge("stdio inet dns tmppath", NULL) == -1) fatal("pledge"); @@ -282,6 +286,7 @@ parse_server(char *server) if (servname == NULL) servname = host; + params.tls_servname = servname; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; @@ -399,11 +404,16 @@ smtp_verify_server_cert(void *tag, struc void smtp_require_tls(void *tag, struct smtp_client *proto) { - SSL *ssl = NULL; + struct tls *tls; + + tls = tls_client(); + if (tls == NULL) + fatal("tls_client"); + + if (tls_configure(tls, tls_config) == -1) + fatal("tls_configure"); - if ((ssl = SSL_new(ssl_ctx)) == NULL) - fatal("SSL_new"); - smtp_set_tls(proto, ssl); + smtp_set_tls(proto, tls); } void Index: smtpd.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpd.c,v retrieving revision 1.336 diff -u -p -r1.336 smtpd.c --- smtpd.c 31 Dec 2020 08:27:15 -0000 1.336 +++ smtpd.c 19 Jan 2021 09:45:44 -0000 @@ -49,6 +49,7 @@ #include <string.h> #include <sysexits.h> #include <time.h> +#include <tls.h> #include <unistd.h> #include <openssl/ssl.h> @@ -609,7 +610,7 @@ main(int argc, char *argv[]) env->sc_opts |= opts; - ssl_init(); + tls_init(); if (parse_config(conf, conffile, opts)) exit(1); Index: smtpd.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpd.conf.5,v retrieving revision 1.257 diff -u -p -r1.257 smtpd.conf.5 --- smtpd.conf.5 13 Feb 2021 07:59:54 -0000 1.257 +++ smtpd.conf.5 13 Feb 2021 13:21:35 -0000 @@ -345,17 +345,9 @@ sending a single warning after four hour .It Ic ca Ar caname Cm cert Ar cafile Associate the Certificate Authority (CA) certificate file .Ar cafile -with host -.Ar caname , -and use that file as the CA certificate for that host. -.Ar caname -is the server's name, -derived from the default hostname -or set using either -.Pa /etc/mail/mailname -or using the -.Ic hostname -directive. +with ca entry +.Ar caname . +The ca entry can be referenced in listener rules and relay actions. .It Ic filter Ar chain-name Ic chain Brq Ar filter-name Op , Ar ... Register a chain of filters .Ar chain-name , @@ -479,6 +471,8 @@ use the certificate associated with .Ic pki directive) to prove a mail server's identity. +This option can be used multiple times to provide alternate +certificates for SNI. .It Cm port Op Ar port Listen on the given .Ar port @@ -800,21 +794,10 @@ The default is 100. .It Ic pki Ar pkiname Cm cert Ar certfile Associate certificate file .Ar certfile -with host -.Ar pkiname , -and use that file to prove the identity of the mail server to clients. -.Ar pkiname -is the server's name, -derived from the default hostname -or set using either -.Pa /etc/mail/mailname -or using the -.Ic hostname -directive. -If a fallback certificate or SNI is wanted, the -.Sq * -wildcard may be used as +with pki entry .Ar pkiname . +The pki entry defines a keypair configuration that can be referenced +in listener rules and relay actions. .Pp A certificate chain may be created by appending one or many certificates, including a Certificate Authority certificate, @@ -825,10 +808,10 @@ The creation of certificates is document .It Ic pki Ar pkiname Cm key Ar keyfile Associate the key located in .Ar keyfile -with host +with pki entry .Ar pkiname . .It Ic pki Ar pkiname Cm dhe Ar params -Specify the DHE parameters to use for DHE cipher suites with host +Specify the DHE parameters to use for DHE cipher suites with pki entry .Ar pkiname . Valid parameter values are .Cm none , Index: smtpd.h =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpd.h,v retrieving revision 1.661 diff -u -p -r1.661 smtpd.h --- smtpd.h 19 Jan 2021 09:16:20 -0000 1.661 +++ smtpd.h 19 Jan 2021 11:47:07 -0000 @@ -542,6 +542,10 @@ struct listener { TAILQ_ENTRY(listener) entry; int local; /* there must be a better way */ + + struct tls *tls; + struct pki **pki; + int pkicount; }; struct smtpd { @@ -1176,6 +1180,7 @@ struct dispatcher_remote { char *source; + struct tls_config *tls_config; char *ca; char *pki; @@ -1690,6 +1695,7 @@ const char *rule_to_text(struct rule *); const char *sockaddr_to_text(const struct sockaddr *); const char *mailaddr_to_text(const struct mailaddr *); const char *expandnode_to_text(struct expandnode *); +const char *tls_to_text(struct tls *); /* util.c */ Index: ssl.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/ssl.c,v retrieving revision 1.93 diff -u -p -r1.93 ssl.c --- ssl.c 5 Jun 2019 06:40:13 -0000 1.93 +++ ssl.c 19 Jan 2021 08:41:42 -0000 @@ -450,3 +450,59 @@ ssl_ctx_fake_private_key(SSL_CTX *ctx, c return (ret); } + +static void +hash_x509(X509 *cert, char *hash, size_t hashlen) +{ + static const char hex[] = "0123456789abcdef"; + size_t off; + char digest[EVP_MAX_MD_SIZE]; + int dlen, i; + + if (X509_pubkey_digest(cert, EVP_sha256(), digest, &dlen) != 1) + fatalx("%s: X509_pubkey_digest failed", __func__); + + if (hashlen < 2 * dlen + sizeof("SHA256:")) + fatalx("%s: hash buffer to small", __func__); + + off = strlcpy(hash, "SHA256:", hashlen); + + for (i = 0; i < dlen; i++) { + hash[off++] = hex[(digest[i] >> 4) & 0x0f]; + hash[off++] = hex[digest[i] & 0x0f]; + } + hash[off] = 0; +} + +char * +ssl_pubkey_hash(const char *buf, off_t len) +{ +#define TLS_CERT_HASH_SIZE 128 + BIO *in; + X509 *x509 = NULL; + char *hash = NULL; + + if ((in = BIO_new_mem_buf(buf, len)) == NULL) { + log_warnx("%s: BIO_new_mem_buf failed", __func__); + return NULL; + } + + if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) { + log_warnx("%s: PEM_read_bio_X509 failed", __func__); + goto fail; + } + + if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) { + log_warn("%s: malloc", __func__); + goto fail; + } + hash_x509(x509, hash, TLS_CERT_HASH_SIZE); + +fail: + BIO_free(in); + + if (x509) + X509_free(x509); + + return hash; +} Index: ssl.h =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/ssl.h,v retrieving revision 1.21 diff -u -p -r1.21 ssl.h --- ssl.h 18 Sep 2019 11:26:30 -0000 1.21 +++ ssl.h 19 Jan 2021 09:06:14 -0000 @@ -15,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <openssl/ssl.h> + #define SSL_CIPHERS "HIGH:!aNULL:!MD5" #define SSL_SESSION_TIMEOUT 300 @@ -62,6 +64,7 @@ int ssl_load_pkey(const void *, size_t, X509 **, EVP_PKEY **); int ssl_ctx_fake_private_key(SSL_CTX *, const void *, size_t, char *, off_t, X509 **, EVP_PKEY **); +char *ssl_pubkey_hash(const char *, off_t); /* ssl_privsep.c */ int ssl_by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **); Index: to.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/to.c,v retrieving revision 1.45 diff -u -p -r1.45 to.c --- to.c 19 Jan 2021 09:16:20 -0000 1.45 +++ to.c 19 Jan 2021 11:47:07 -0000 @@ -43,6 +43,9 @@ #include <stdlib.h> #include <string.h> #include <time.h> +#if IO_TLS +#include <tls.h> +#endif #include <unistd.h> #include "smtpd.h" @@ -795,3 +798,18 @@ alias_is_error(struct expandnode *alias, alias->type = EXPAND_ERROR; return 1; } + +#if IO_TLS +const char * +tls_to_text(struct tls *tls) +{ + static char buf[256]; + + (void)snprintf(buf, sizeof buf, "%s:%s:%d", + tls_conn_version(tls), + tls_conn_cipher(tls), + tls_conn_cipher_strength(tls)); + + return (buf); +} +#endif Index: smtp/Makefile =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtp/Makefile,v retrieving revision 1.3 diff -u -p -r1.3 Makefile --- smtp/Makefile 18 Sep 2019 11:26:30 -0000 1.3 +++ smtp/Makefile 19 Jan 2021 09:08:22 -0000 @@ -17,7 +17,7 @@ SRCS+= ssl_verify.c CPPFLAGS+= -DIO_TLS -LDADD+= -levent -lutil -lssl -lcrypto -lm -lz -DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBM} ${LIBZ} +LDADD+= -levent -lutil -ltls -lssl -lcrypto -lm -lz +DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO} ${LIBM} ${LIBZ} .include <bsd.prog.mk> Index: smtpd/Makefile =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpd/Makefile,v retrieving revision 1.110 diff -u -p -r1.110 Makefile --- smtpd/Makefile 31 Dec 2020 08:27:15 -0000 1.110 +++ smtpd/Makefile 19 Jan 2021 08:36:15 -0000 @@ -82,8 +82,8 @@ SRCS+= stat_ramstat.c MAN= sendmail.8 smtpd.8 smtpd.conf.5 table.5 BINDIR= /usr/sbin -LDADD+= -levent -lutil -lssl -lcrypto -lm -lz -DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBM} ${LIBZ} +LDADD+= -levent -lutil -ltls -lssl -lcrypto -lm -lz +DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO} ${LIBM} ${LIBZ} CFLAGS+= -fstack-protector-all CFLAGS+= -I${.CURDIR}/..