No much report so far.
Anybody had a chance to test this?
Here is the same diff again with manpage update this time.
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.256
diff -u -p -r1.256 smtpd.conf.5
--- smtpd.conf.5 27 Jan 2021 14:59:10 -0000 1.256
+++ smtpd.conf.5 5 Feb 2021 08:24:11 -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}/..