Start porting smtpd to libtls.
authoreric <eric@openbsd.org>
Fri, 5 Mar 2021 12:37:32 +0000 (12:37 +0000)
committereric <eric@openbsd.org>
Fri, 5 Mar 2021 12:37:32 +0000 (12:37 +0000)
Note that it changes the way SNI works: The certificate to use is now
selected by looking at the names found in the certificates themselves,
rather than the names of the pki entries in the configuration file.
The set of certificates for a tls listener must be defined explicitly by
using the pki listener option multiple times.

ok tb@

23 files changed:
usr.sbin/smtpd/ca.c
usr.sbin/smtpd/config.c
usr.sbin/smtpd/dispatcher.c
usr.sbin/smtpd/iobuf.c
usr.sbin/smtpd/iobuf.h
usr.sbin/smtpd/ioev.c
usr.sbin/smtpd/ioev.h
usr.sbin/smtpd/mta.c
usr.sbin/smtpd/mta_session.c
usr.sbin/smtpd/parse.y
usr.sbin/smtpd/smtp.c
usr.sbin/smtpd/smtp.h
usr.sbin/smtpd/smtp/Makefile
usr.sbin/smtpd/smtp_client.c
usr.sbin/smtpd/smtp_session.c
usr.sbin/smtpd/smtpc.c
usr.sbin/smtpd/smtpd.c
usr.sbin/smtpd/smtpd.conf.5
usr.sbin/smtpd/smtpd.h
usr.sbin/smtpd/smtpd/Makefile
usr.sbin/smtpd/ssl.c
usr.sbin/smtpd/ssl.h
usr.sbin/smtpd/to.c

index 4f17286..06499d5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -69,6 +69,7 @@ static int ecdsae_do_verify(const unsigned char *, int, const ECDSA_SIG *,
     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 *imsg)
        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 *imsg)
        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 char *from, unsigned char *to,
        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 char *from, unsigned char *to,
        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 *dgst, int dgst_len,
        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 *dgst, int dgst_len,
        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 094d418..73a76ac 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -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 a8f39cc..3c67ea4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -154,6 +154,8 @@ dispatcher(void)
 {
        struct passwd   *pw;
 
+       ca_engine_init();
+
        mda_postfork();
        mta_postfork();
        smtp_postfork();
@@ -196,8 +198,6 @@ dispatcher(void)
        config_peer(PROC_CONTROL);
        config_peer(PROC_CA);
 
-       ca_engine_init();
-
        if (pledge("stdio inet unix recvfd sendfd", NULL) == -1)
                err(1, "pledge");
 
index c899dc5..c5b01d9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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"
 
@@ -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 *tls)
 }
 
 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 c454d0a..94f5c56 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
  *
@@ -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 struct iovec *, int);
 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 8ae0bc3..109a2c3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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,
@@ -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 io *, int);
 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
 
@@ -168,7 +165,7 @@ io_set_nolinger(int fd)
 
        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");
 }
 
 /*
@@ -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);
 }
@@ -368,7 +366,7 @@ io_set_read(struct io *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;
@@ -384,7 +382,7 @@ io_set_write(struct io *io)
 
        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;
@@ -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, void *humppa)
 }
 
 #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);
@@ -867,28 +893,17 @@ io_dispatch_accept_tls(int fd, short event, void *humppa)
                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 event, void *humppa)
                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, void *humppa)
 
 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 event, void *humppa)
        }
 
        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 event, void *humppa)
                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:
@@ -1053,7 +1042,7 @@ io_reload_tls(struct io *io)
                        return; /* paused */
                break;
        default:
-               errx(1, "io_reload_tls(): bad state");
+               errx(1, "io_reload_tls: bad state");
        }
 
        io_reset(io, ev, dispatch);
index f155a7f..96b9e5c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
  *
@@ -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 ea79e2d..74dd7c7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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
 
@@ -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 *, 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;
@@ -463,6 +472,70 @@ mta_imsg(struct mproc *p, struct imsg *imsg)
 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 6f3917b..2445378 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -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 *, char *, ...);
 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 *line)
                        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 char *fmt, ...)
 }
 
 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) {
index c2022bd..40f78be 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -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 *lo)
        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 *lo)
 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,  struct listen_opts *lo)
                    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 35776c4..171025d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -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 *, int, const struct sockaddr_storage *
 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, 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;
@@ -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)
@@ -154,20 +164,78 @@ 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;
@@ -319,22 +379,6 @@ smtp_collect(void)
        }
 }
 
-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)
 {
index dc91d87..11ab6cb 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -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 fc47fa0..db5f178 100644 (file)
@@ -1,4 +1,4 @@
-#      $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}/..
 
@@ -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 8e146e1..372e50a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -187,7 +187,7 @@ smtp_cert_verified(struct smtp_client *proto, int verified)
 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, void *arg)
 
        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:
@@ -649,10 +654,6 @@ smtp_client_io(struct io *io, int evt, void *arg)
                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;
index 469f161..96de6dc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -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 *, int, const char *, const char *);
 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(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 *);
 
@@ -1066,17 +1064,26 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
 }
 
 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 *arg)
 
        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 *arg)
 
                /* 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);
 }
@@ -2260,112 +2269,6 @@ smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args,
        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)
 {
@@ -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 ec77e15..fd0f8ef 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -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, struct smtp_client *proto, void *ctx)
 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 ee9a771..329f6d8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -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 20ac8b5..6359cc8 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $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>
@@ -17,7 +17,7 @@
 .\" 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
@@ -345,17 +345,9 @@ sending a single warning after four hours.
 .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 documented in
 .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 487573d..c0e4878 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -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 5a0d099..704078f 100644 (file)
@@ -1,4 +1,4 @@
-#      $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}/..
 
@@ -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}/..
index bd18ad6..c4ec69a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -450,3 +450,59 @@ ssl_ctx_fake_private_key(SSL_CTX *ctx, const void *data, size_t datalen,
 
        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 1c0dc07..9ee1b84 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
  *
@@ -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, char *, off_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 11cdf59..8a285f6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -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, const char *line, size_t len)
        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