-/* $OpenBSD: ca.c,v 1.37 2020/12/31 08:27:15 martijn Exp $ */
+/* $OpenBSD: ca.c,v 1.38 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
EC_KEY *);
+static struct dict pkeys;
static uint64_t reqid = 0;
static 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);
}
}
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;
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");
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)
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);
/*
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);
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);
/*
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);
-/* $OpenBSD: config.c,v 1.53 2021/01/19 09:16:20 claudio Exp $ */
+/* $OpenBSD: config.c,v 1.54 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
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);
-/* $OpenBSD: dispatcher.c,v 1.1 2020/12/31 08:27:15 martijn Exp $ */
+/* $OpenBSD: dispatcher.c,v 1.2 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2014 Gilles Chehade <gilles@poolp.org>
{
struct passwd *pw;
+ ca_engine_init();
+
mda_postfork();
mta_postfork();
smtp_postfork();
config_peer(PROC_CONTROL);
config_peer(PROC_CA);
- ca_engine_init();
-
if (pledge("stdio inet unix recvfd sendfd", NULL) == -1)
err(1, "pledge");
-/* $OpenBSD: iobuf.c,v 1.14 2021/01/23 16:11:11 rob Exp $ */
+/* $OpenBSD: iobuf.c,v 1.15 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
*
#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"
#ifdef IO_TLS
int
-iobuf_flush_tls(struct iobuf *io, void *tls)
+iobuf_flush_tls(struct iobuf *io, struct tls *tls)
{
ssize_t s;
}
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;
-/* $OpenBSD: iobuf.h,v 1.5 2019/06/12 17:42:53 eric Exp $ */
+/* $OpenBSD: iobuf.h,v 1.6 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
*
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 *);
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);
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 *);
-/* $OpenBSD: ioev.c,v 1.43 2021/01/23 16:11:11 rob Exp $ */
+/* $OpenBSD: ioev.c,v 1.44 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
*
#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,
int flags;
int state;
struct event ev;
- void *tls;
+ struct tls *tls;
+ char *name;
+
const char *error; /* only valid immediately on callback */
};
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 *);
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
memset(&l, 0, sizeof(l));
if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1)
- err(1, "io_set_linger:setsockopt()");
+ err(1, "io_set_linger:setsockopt");
}
/*
current = NULL;
#ifdef IO_TLS
- SSL_free(io->tls);
+ tls_free(io->tls);
io->tls = NULL;
#endif
io->sock = -1;
}
+ free(io->name);
iobuf_clear(&io->iobuf);
free(io);
}
mode = io->flags & IO_RW;
if (!(mode == 0 || mode == IO_WRITE))
- errx(1, "io_set_read(): full-duplex or reading");
+ errx(1, "io_set_read: full-duplex or reading");
io->flags &= ~IO_RW;
io->flags |= IO_READ;
mode = io->flags & IO_RW;
if (!(mode == 0 || mode == IO_READ))
- errx(1, "io_set_write(): full-duplex or writing");
+ errx(1, "io_set_write: full-duplex or writing");
io->flags &= ~IO_RW;
io->flags |= IO_WRITE;
return io->error;
}
-void *
+struct tls *
io_tls(struct io *io)
{
return io->tls;
}
#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_accept_tls: expect IO_READ mode");
if (io->tls)
- errx(1, "io_start_tls(): TLS already started");
+ errx(1, "io_accept_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);
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
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);
}
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);
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;
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;
}
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);
}
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;
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:
return; /* paused */
break;
default:
- errx(1, "io_reload_tls(): bad state");
+ errx(1, "io_reload_tls: bad state");
}
io_reset(io, ev, dispatch);
-/* $OpenBSD: ioev.h,v 1.18 2019/09/11 04:19:19 martijn Exp $ */
+/* $OpenBSD: ioev.h,v 1.19 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
*
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? */
#define IO_OUT 0x02
struct io;
+struct tls;
void io_set_nonblocking(int);
void io_set_nolinger(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);
-/* $OpenBSD: mta.c,v 1.234 2019/12/21 10:34:07 gilles Exp $ */
+/* $OpenBSD: mta.c,v 1.235 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
#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
#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 *);
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;
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
-/* $OpenBSD: mta_session.c,v 1.138 2020/12/21 11:44:07 martijn Exp $ */
+/* $OpenBSD: mta_session.c,v 1.139 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <tls.h>
#include <unistd.h>
#include "smtpd.h"
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);
return;
}
- mta_cert_init(s);
+ mta_tls_init(s);
break;
case MTA_AUTH_PLAIN:
if (s->use_smtps) {
io_set_write(io);
- mta_cert_init(s);
+ mta_tls_init(s);
}
else {
mta_enter_state(s, MTA_BANNER);
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:
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) {
}
static void
-mta_cert_init(struct mta_session *s)
+mta_tls_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;
- }
+ struct tls_config *tls_config;
+ struct tls *tls;
- 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)
-{
- 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);
-
- 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) {
-/* $OpenBSD: parse.y,v 1.284 2021/01/23 16:11:11 rob Exp $ */
+/* $OpenBSD: parse.y,v 1.285 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
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;
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) {
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;
static void
config_listener(struct listener *h, struct listen_opts *lo)
{
+ int i;
+
h->fd = -1;
h->port = lo->port;
h->flags = lo->flags;
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);
}
}
-/* $OpenBSD: smtp.c,v 1.166 2019/08/10 16:07:01 gilles Exp $ */
+/* $OpenBSD: smtp.c,v 1.167 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <tls.h>
#include <unistd.h>
#include <openssl/ssl.h>
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,
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;
}
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)
}
}
+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)
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;
}
}
-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
smtp_accepted(struct listener *listener, int sock, const struct sockaddr_storage *ss, struct io *io)
{
-/* $OpenBSD: smtp.h,v 1.3 2019/09/02 20:05:21 eric Exp $ */
+/* $OpenBSD: smtp.h,v 1.4 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
/* 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 */
-# $OpenBSD: Makefile,v 1.3 2019/09/18 11:26:30 eric Exp $
+# $OpenBSD: Makefile,v 1.4 2021/03/05 12:37:32 eric Exp $
.PATH: ${.CURDIR}/..
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>
-/* $OpenBSD: smtp_client.c,v 1.14 2020/04/24 11:34:07 eric Exp $ */
+/* $OpenBSD: smtp_client.c,v 1.15 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
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
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:
smtp_client_abort(proto, FAIL_CONN, io_error(io));
break;
- case IO_TLSERROR:
- smtp_client_abort(proto, FAIL_CONN, io_error(io));
- break;
-
case IO_DISCONNECTED:
smtp_client_abort(proto, FAIL_CONN, io_error(io));
break;
-/* $OpenBSD: smtp_session.c,v 1.428 2020/12/21 11:44:07 martijn Exp $ */
+/* $OpenBSD: smtp_session.c,v 1.429 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <tls.h>
#include <unistd.h>
#include <vis.h>
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 *, ...);
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 *);
}
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) {
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:
/* Wait for the client to start tls */
if (s->state == STATE_TLS) {
- smtp_cert_init(s);
+ smtp_tls_init(s);
break;
}
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);
}
return (1);
}
-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)
{
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;
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",
-/* $OpenBSD: smtpc.c,v 1.13 2020/12/29 12:17:54 jmc Exp $ */
+/* $OpenBSD: smtpc.c,v 1.14 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
+#include <tls.h>
#include <unistd.h>
#include <openssl/ssl.h>
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)
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");
if (servname == NULL)
servname = host;
+ params.tls_servname = servname;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
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
-/* $OpenBSD: smtpd.c,v 1.336 2020/12/31 08:27:15 martijn Exp $ */
+/* $OpenBSD: smtpd.c,v 1.337 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
#include <string.h>
#include <sysexits.h>
#include <time.h>
+#include <tls.h>
#include <unistd.h>
#include <openssl/ssl.h>
env->sc_opts |= opts;
- ssl_init();
+ tls_init();
if (parse_config(conf, conffile, opts))
exit(1);
-.\" $OpenBSD: smtpd.conf.5,v 1.257 2021/02/13 07:59:54 jmc Exp $
+.\" $OpenBSD: smtpd.conf.5,v 1.258 2021/03/05 12:37:32 eric Exp $
.\"
.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org>
.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.\"
-.Dd $Mdocdate: February 13 2021 $
+.Dd $Mdocdate: March 5 2021 $
.Dt SMTPD.CONF 5
.Os
.Sh NAME
.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 ,
.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
.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,
.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 ,
-/* $OpenBSD: smtpd.h,v 1.661 2021/01/19 09:16:20 claudio Exp $ */
+/* $OpenBSD: smtpd.h,v 1.662 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
TAILQ_ENTRY(listener) entry;
int local; /* there must be a better way */
+
+ struct tls *tls;
+ struct pki **pki;
+ int pkicount;
};
struct smtpd {
char *source;
+ struct tls_config *tls_config;
char *ca;
char *pki;
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 */
-# $OpenBSD: Makefile,v 1.110 2020/12/31 08:27:15 martijn Exp $
+# $OpenBSD: Makefile,v 1.111 2021/03/05 12:37:32 eric Exp $
.PATH: ${.CURDIR}/..
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}/..
-/* $OpenBSD: ssl.c,v 1.93 2019/06/05 06:40:13 gilles Exp $ */
+/* $OpenBSD: ssl.c,v 1.94 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
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;
+}
-/* $OpenBSD: ssl.h,v 1.21 2019/09/18 11:26:30 eric Exp $ */
+/* $OpenBSD: ssl.h,v 1.22 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2013 Gilles Chehade <gilles@poolp.org>
*
* 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
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 **);
-/* $OpenBSD: to.c,v 1.45 2021/01/19 09:16:20 claudio Exp $ */
+/* $OpenBSD: to.c,v 1.46 2021/03/05 12:37:32 eric Exp $ */
/*
* Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#if IO_TLS
+#include <tls.h>
+#endif
#include <unistd.h>
#include "smtpd.h"
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