Implement RSA privilege separation for OpenSMTPD, based on my previous
authorreyk <reyk@openbsd.org>
Tue, 29 Apr 2014 19:13:13 +0000 (19:13 +0000)
committerreyk <reyk@openbsd.org>
Tue, 29 Apr 2014 19:13:13 +0000 (19:13 +0000)
implementation for relayd(8).  The smtpd(8) pony processes (mta
client, smtp server) don't keep the private keys in memory but send
their private key operations as imsgs to the "lookup"/mta process.
It's worth mentioning that this prevents acidental private key leakage
as it could have been caused by "Heartbleed".

ok gilles@

14 files changed:
usr.sbin/smtpd/ca.c
usr.sbin/smtpd/config.c
usr.sbin/smtpd/lka.c
usr.sbin/smtpd/mproc.c
usr.sbin/smtpd/mta_session.c
usr.sbin/smtpd/pony.c
usr.sbin/smtpd/smtp.c
usr.sbin/smtpd/smtp_session.c
usr.sbin/smtpd/smtpd.c
usr.sbin/smtpd/smtpd.h
usr.sbin/smtpd/ssl.c
usr.sbin/smtpd/ssl.h
usr.sbin/smtpd/ssl_privsep.c
usr.sbin/smtpd/ssl_smtpd.c

index 32fb84c..36fbd22 100644 (file)
@@ -1,6 +1,7 @@
-/*     $OpenBSD: ca.c,v 1.3 2013/11/21 08:36:51 eric Exp $     */
+/*     $OpenBSD: ca.c,v 1.4 2014/04/29 19:13:13 reyk Exp $     */
 
 /*
+ * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
  * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  */
 
 #include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
 
-#include <openssl/err.h>
-#include <openssl/ssl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <imsg.h>
 
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/engine.h>
+
+#include "smtpd.h"
 #include "log.h"
+#include "ssl.h"
+
+static int      ca_verify_cb(int, X509_STORE_CTX *);
+
+static int      rsae_send_imsg(int, const u_char *, u_char *, RSA *,
+                   int, u_int);
+static int      rsae_pub_enc(int, const u_char *, u_char *, RSA *, int);
+static int      rsae_pub_dec(int,const u_char *, u_char *, RSA *, int);
+static int      rsae_priv_enc(int, const u_char *, u_char *, RSA *, int);
+static int      rsae_priv_dec(int, const u_char *, u_char *, RSA *, int);
+static int      rsae_mod_exp(BIGNUM *, const BIGNUM *, RSA *, BN_CTX *);
+static int      rsae_bn_mod_exp(BIGNUM *, const BIGNUM *, const BIGNUM *,
+                   const BIGNUM *, BN_CTX *, BN_MONT_CTX *);
+static int      rsae_init(RSA *);
+static int      rsae_finish(RSA *);
+static int      rsae_sign(int, const u_char *, u_int, u_char *, u_int *,
+                   const RSA *);
+static int      rsae_verify(int dtype, const u_char *m, u_int, const u_char *,
+                   u_int, const RSA *);
+static int      rsae_keygen(RSA *, int, BIGNUM *, BN_GENCB *);
+
+void
+ca_init(void)
+{
+       BIO             *in = NULL;
+       EVP_PKEY        *pkey = NULL;
+       struct pki      *pki;
+       const char      *k;
+       void            *iter_dict;
+
+       log_debug("debug: init private ssl-tree");
+       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");
 
-int    ca_X509_verify(X509 *, STACK_OF(X509) *, const char *, const char *, const char **);
+               if ((pkey = PEM_read_bio_PrivateKey(in,
+                   NULL, NULL, NULL)) == NULL)
+                       fatalx("ca_launch: PEM");
+               BIO_free(in);
+
+               pki->pki_pkey = pkey;
+
+               explicit_bzero(pki->pki_key, pki->pki_key_len);
+               free(pki->pki_key);
+               pki->pki_key = NULL;
+       }
+}
 
 static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
+ca_verify_cb(int ok, X509_STORE_CTX *ctx)
 {
        switch (X509_STORE_CTX_get_error(ctx)) {
        case X509_V_OK:
@@ -50,7 +110,7 @@ verify_cb(int ok, X509_STORE_CTX *ctx)
 }
 
 int
-ca_X509_verify(X509 *certificate, STACK_OF(X509) *chain, const char *CAfile,
+ca_X509_verify(void *certificate, void *chain, const char *CAfile,
     const char *CRLfile, const char **errstr)
 {
        X509_STORE     *store = NULL;
@@ -72,7 +132,7 @@ ca_X509_verify(X509 *certificate, STACK_OF(X509) *chain, const char *CAfile,
        if (X509_STORE_CTX_init(xsc, store, certificate, chain) != 1)
                goto end;
 
-       X509_STORE_CTX_set_verify_cb(xsc, verify_cb);
+       X509_STORE_CTX_set_verify_cb(xsc, ca_verify_cb);
 
        ret = X509_verify_cert(xsc);
 
@@ -92,3 +152,262 @@ end:
 
        return ret > 0 ? 1 : 0;
 }
+
+void
+ca_imsg(struct mproc *p, struct imsg *imsg)
+{
+       RSA                     *rsa;
+       const void              *from = NULL;
+       u_char                   *to = NULL;
+       struct msg               m;
+       const char              *pkiname;
+       size_t                   flen, tlen, padding;
+       struct pki              *pki;
+       int                      ret = 0;
+
+       m_msg(&m, imsg);
+       m_get_string(&m, &pkiname);
+       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");
+
+       if ((to = calloc(1, tlen)) == NULL)
+               fatalx("ca_imsg: calloc");
+
+       switch (imsg->hdr.type) {
+       case IMSG_CA_PRIVENC:
+               ret = RSA_private_encrypt(flen, from, to, rsa,
+                   padding);
+               break;
+       case IMSG_CA_PRIVDEC:
+               ret = RSA_private_decrypt(flen, from, to, rsa,
+                   padding);
+               break;
+       }
+
+       m_create(p, imsg->hdr.type, 0, 0, -1);
+       m_add_int(p, ret);
+       if (ret > 0)
+               m_add_data(p, to, (size_t)ret);
+       m_close(p);
+
+       free(to);
+       RSA_free(rsa);
+}
+
+/*
+ * RSA privsep engine (called from unprivileged processes)
+ */
+
+const RSA_METHOD *rsa_default = NULL;
+
+static RSA_METHOD rsae_method = {
+       "RSA privsep engine",
+       rsae_pub_enc,
+       rsae_pub_dec,
+       rsae_priv_enc,
+       rsae_priv_dec,
+       rsae_mod_exp,
+       rsae_bn_mod_exp,
+       rsae_init,
+       rsae_finish,
+       0,
+       NULL,
+       rsae_sign,
+       rsae_verify,
+       rsae_keygen
+};
+
+static int
+rsae_send_imsg(int flen, const u_char *from, u_char *to, RSA *rsa,
+    int padding, u_int cmd)
+{
+       int              ret = 0;
+       struct imsgbuf  *ibuf;
+       struct imsg      imsg;
+       int              n, done = 0;
+       const void      *toptr;
+       char            *pkiname;
+       size_t           tlen;
+       struct msg       m;
+
+       if ((pkiname = RSA_get_ex_data(rsa, 0)) == NULL)
+               return (0);
+
+       /*
+        * Send a synchronous imsg because we cannot defer the RSA
+        * operation in OpenSSL's engine layer.
+        */
+       m_create(p_lka, cmd, 0, 0, -1);
+       m_add_string(p_lka, pkiname);
+       m_add_data(p_lka, (const void *)from, (size_t)flen);
+       m_add_size(p_lka, (size_t)RSA_size(rsa));
+       m_add_size(p_lka, (size_t)padding);
+       m_flush(p_lka);
+
+       ibuf = &p_lka->imsgbuf;
+
+       while (!done) {
+               if ((n = imsg_read(ibuf)) == -1)
+                       fatalx("imsg_read");
+               if (n == 0)
+                       fatalx("pipe closed");
+
+               while (!done) {
+                       if ((n = imsg_get(ibuf, &imsg)) == -1)
+                               fatalx("imsg_get error");
+                       if (n == 0)
+                               break;
+                       if (imsg.hdr.type != cmd)
+                               fatalx("invalid response");
+
+                       m_msg(&m, &imsg);
+                       m_get_int(&m, &ret);
+                       if (ret > 0)
+                               m_get_data(&m, &toptr, &tlen);
+                       m_end(&m);
+
+                       if (ret > 0)
+                               memcpy(to, toptr, tlen);
+                       done = 1;
+
+                       imsg_free(&imsg);
+               }
+       }
+       mproc_event_add(p_lka);
+
+       return (ret);
+}
+
+static int
+rsae_pub_enc(int flen,const u_char *from, u_char *to, RSA *rsa,int padding)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       return (rsa_default->rsa_pub_enc(flen, from, to, rsa, padding));
+}
+
+static int
+rsae_pub_dec(int flen,const u_char *from, u_char *to, RSA *rsa,int padding)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       return (rsa_default->rsa_pub_dec(flen, from, to, rsa, padding));
+}
+
+static int
+rsae_priv_enc(int flen, const u_char *from, u_char *to, RSA *rsa, int padding)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       if (RSA_get_ex_data(rsa, 0) != NULL) {
+               return (rsae_send_imsg(flen, from, to, rsa, padding,
+                   IMSG_CA_PRIVENC));
+       }
+       return (rsa_default->rsa_priv_enc(flen, from, to, rsa, padding));
+}
+
+static int
+rsae_priv_dec(int flen, const u_char *from, u_char *to, RSA *rsa, int padding)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       if (RSA_get_ex_data(rsa, 0) != NULL) {
+               return (rsae_send_imsg(flen, from, to, rsa, padding,
+                   IMSG_CA_PRIVDEC));
+       }
+       return (rsa_default->rsa_priv_dec(flen, from, to, rsa, padding));
+}
+
+static int
+rsae_mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       return (rsa_default->rsa_mod_exp(r0, I, rsa, ctx));
+}
+
+static int
+rsae_bn_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+    const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       return (rsa_default->bn_mod_exp(r, a, p, m, ctx, m_ctx));
+}
+
+static int
+rsae_init(RSA *rsa)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       if (rsa_default->init == NULL)
+               return (1);
+       return (rsa_default->init(rsa));
+}
+
+static int
+rsae_finish(RSA *rsa)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       if (rsa_default->finish == NULL)
+               return (1);
+       return (rsa_default->finish(rsa));
+}
+
+static int
+rsae_sign(int type, const u_char *m, u_int m_length, u_char *sigret,
+    u_int *siglen, const RSA *rsa)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       return (rsa_default->rsa_sign(type, m, m_length,
+           sigret, siglen, rsa));
+}
+
+static int
+rsae_verify(int dtype, const u_char *m, u_int m_length, const u_char *sigbuf,
+    u_int siglen, const RSA *rsa)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       return (rsa_default->rsa_verify(dtype, m, m_length,
+           sigbuf, siglen, rsa));
+}
+
+static int
+rsae_keygen(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb)
+{
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+       return (rsa_default->rsa_keygen(rsa, bits, e, cb));
+}
+
+int
+ca_engine_init(void)
+{
+       ENGINE  *e;
+
+       log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
+
+       if ((e = ENGINE_get_default_RSA()) == NULL ||
+           (rsa_default = ENGINE_get_RSA(e)) == NULL)
+               return (-1);
+
+       if (rsa_default->flags & RSA_FLAG_SIGN_VER)
+               fatalx("unsupported RSA engine");
+
+       if (rsa_default->rsa_mod_exp == NULL)
+               rsae_method.rsa_mod_exp = NULL;
+       if (rsa_default->rsa_mod_exp == NULL)
+               rsae_method.rsa_mod_exp = NULL;
+       if (rsa_default->bn_mod_exp == NULL)
+               rsae_method.bn_mod_exp = NULL;
+       if (rsa_default->rsa_keygen == NULL)
+               rsae_method.rsa_keygen = NULL;
+       rsae_method.flags = rsa_default->flags |
+           RSA_METHOD_FLAG_NO_CHECK;
+       rsae_method.app_data = rsa_default->app_data;
+
+       if (!ENGINE_set_RSA(e, &rsae_method) ||
+           !ENGINE_set_default_RSA(e))
+               return (-1);
+
+       return (0);
+}
index 9eff839..10843ef 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.29 2014/04/29 10:18:06 reyk Exp $        */
+/*     $OpenBSD: config.c,v 1.30 2014/04/29 19:13:13 reyk Exp $        */
 
 /*
  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -46,6 +46,8 @@ purge_config(uint8_t what)
        struct table    *t;
        struct rule     *r;
        struct pki      *p;
+       const char      *k;
+       void            *iter_dict;
 
        if (what & PURGE_LISTENERS) {
                while ((l = TAILQ_FIRST(env->sc_listeners)) != NULL) {
@@ -72,13 +74,33 @@ purge_config(uint8_t what)
        if (what & PURGE_PKI) {
                while (dict_poproot(env->sc_pki_dict, (void **)&p)) {
                        explicit_bzero(p->pki_cert, p->pki_cert_len);
-                       explicit_bzero(p->pki_key, p->pki_key_len);
                        free(p->pki_cert);
-                       free(p->pki_key);
+                       if (p->pki_key) {
+                               explicit_bzero(p->pki_key, p->pki_key_len);
+                               free(p->pki_key);
+                       }
+                       if (p->pki_pkey)
+                               EVP_PKEY_free(p->pki_pkey);
                        free(p);
                }
                free(env->sc_pki_dict);
                env->sc_pki_dict = NULL;
+       } else if (what & PURGE_PKI_KEYS) {
+               iter_dict = NULL;
+               while (dict_iter(env->sc_pki_dict, &iter_dict, &k,
+                   (void **)&p)) {
+                       explicit_bzero(p->pki_cert, p->pki_cert_len);
+                       free(p->pki_cert);
+                       p->pki_cert = NULL;
+                       if (p->pki_key) {
+                               explicit_bzero(p->pki_key, p->pki_key_len);
+                               free(p->pki_key);
+                               p->pki_key = NULL;
+                       }
+                       if (p->pki_pkey)
+                               EVP_PKEY_free(p->pki_pkey);
+                       p->pki_pkey = NULL;
+               }
        }
 }
 
index abfc4ba..dc7f785 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: lka.c,v 1.167 2014/04/15 08:32:45 eric Exp $  */
+/*     $OpenBSD: lka.c,v 1.168 2014/04/29 19:13:13 reyk Exp $  */
 
 /*
  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -91,6 +91,12 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
                return;
        }
 
+       if (imsg->hdr.type == IMSG_CA_PRIVENC ||
+           imsg->hdr.type == IMSG_CA_PRIVDEC) {
+               ca_imsg(p, imsg);
+               return;
+       }
+
        if (p->proc == PROC_PONY) {
                switch (imsg->hdr.type) {
                case IMSG_SMTP_EXPAND_RCPT:
@@ -134,13 +140,10 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
                        }
                        resp_ca_cert.status = CA_OK;
                        resp_ca_cert.cert_len = pki->pki_cert_len;
-                       resp_ca_cert.key_len = pki->pki_key_len;
                        iov[0].iov_base = &resp_ca_cert;
                        iov[0].iov_len = sizeof(resp_ca_cert);
                        iov[1].iov_base = pki->pki_cert;
                        iov[1].iov_len = pki->pki_cert_len;
-                       iov[2].iov_base = pki->pki_key;
-                       iov[2].iov_len = pki->pki_key_len;
                        m_composev(p, IMSG_SMTP_SSL_INIT, 0, 0, -1, iov, nitems(iov));
                        return;
 
@@ -256,13 +259,10 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
                        }
                        resp_ca_cert.status = CA_OK;
                        resp_ca_cert.cert_len = pki->pki_cert_len;
-                       resp_ca_cert.key_len = pki->pki_key_len;
                        iov[0].iov_base = &resp_ca_cert;
                        iov[0].iov_len = sizeof(resp_ca_cert);
                        iov[1].iov_base = pki->pki_cert;
                        iov[1].iov_len = pki->pki_cert_len;
-                       iov[2].iov_base = pki->pki_key;
-                       iov[2].iov_len = pki->pki_key_len;
                        m_composev(p, IMSG_MTA_SSL_INIT, 0, 0, -1, iov, nitems(iov));
                        return;
 
@@ -389,6 +389,7 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
                        if (verbose & TRACE_TABLES)
                                table_dump_all();
                        table_open_all();
+                       ca_init();
 
                        /* Start fulfilling requests */
                        mproc_enable(p_pony);
index a5223d6..50e484f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mproc.c,v 1.8 2014/04/19 17:45:05 gilles Exp $        */
+/*     $OpenBSD: mproc.c,v 1.9 2014/04/29 19:13:13 reyk Exp $  */
 
 /*
  * Copyright (c) 2012 Eric Faurot <eric@faurot.net>
@@ -38,7 +38,6 @@
 #include "smtpd.h"
 #include "log.h"
 
-static void mproc_event_add(struct mproc *);
 static void mproc_dispatch(int, short, void *);
 
 static ssize_t msgbuf_write2(struct msgbuf *);
@@ -117,7 +116,7 @@ mproc_disable(struct mproc *p)
        mproc_event_add(p);
 }
 
-static void
+void
 mproc_event_add(struct mproc *p)
 {
        short   events;
@@ -417,6 +416,25 @@ m_close(struct mproc *p)
        mproc_event_add(p);
 }
 
+void
+m_flush(struct mproc *p)
+{
+       if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd,
+           p->m_buf, p->m_pos) == -1)
+               fatal("imsg_compose");
+
+       log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (flush)",
+           proc_name(smtpd_process),
+           proc_name(p->proc),
+           p->m_pos,
+           imsg_to_str(p->m_type));
+
+       p->msg_out += 1;
+       p->m_pos = 0;
+
+       imsg_flush(&p->imsgbuf);
+}
+
 static struct imsg * current;
 
 static void
@@ -505,6 +523,7 @@ m_add_typed_sized(struct mproc *p, uint8_t type, const void *data, size_t len)
 enum {
        M_INT,
        M_UINT32,
+       M_SIZET,
        M_TIME,
        M_STRING,
        M_DATA,
@@ -528,6 +547,12 @@ m_add_u32(struct mproc *m, uint32_t u32)
        m_add_typed(m, M_UINT32, &u32, sizeof u32);
 };
 
+void
+m_add_size(struct mproc *m, size_t sz)
+{
+       m_add_typed(m, M_SIZET, &sz, sizeof sz);
+};
+
 void
 m_add_time(struct mproc *m, time_t v)
 {
@@ -604,6 +629,12 @@ m_get_u32(struct msg *m, uint32_t *u32)
        m_get_typed(m, M_UINT32, u32, sizeof(*u32));
 }
 
+void
+m_get_size(struct msg *m, size_t *sz)
+{
+       m_get_typed(m, M_SIZET, sz, sizeof(*sz));
+}
+
 void
 m_get_time(struct msg *m, time_t *t)
 {
index 0be56d7..e3b43b7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mta_session.c,v 1.62 2014/04/29 17:32:42 gilles Exp $ */
+/*     $OpenBSD: mta_session.c,v 1.63 2014/04/29 19:13:13 reyk Exp $   */
 
 /*
  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -253,6 +253,7 @@ mta_session_imsg(struct mproc *p, struct imsg *imsg)
        const char              *name;
        void                    *ssl;
        int                      dnserror, status;
+       char                    *pkiname;
 
        switch (imsg->hdr.type) {
 
@@ -328,7 +329,7 @@ mta_session_imsg(struct mproc *p, struct imsg *imsg)
                                return;
                        }
                        else {
-                               ssl = ssl_mta_init(NULL, 0, NULL, 0);
+                               ssl = ssl_mta_init(NULL, NULL, 0);
                                if (ssl == NULL)
                                        fatal("mta: ssl_mta_init");
                                io_start_tls(&s->io, ssl);
@@ -339,19 +340,18 @@ mta_session_imsg(struct mproc *p, struct imsg *imsg)
                resp_ca_cert = xmemdup(imsg->data, sizeof *resp_ca_cert, "mta:ca_cert");
                resp_ca_cert->cert = xstrdup((char *)imsg->data +
                    sizeof *resp_ca_cert, "mta:ca_cert");
-               resp_ca_cert->key = xstrdup((char *)imsg->data +
-                   sizeof *resp_ca_cert + resp_ca_cert->cert_len,
-                   "mta:ca_key");
-               ssl = ssl_mta_init(resp_ca_cert->cert, resp_ca_cert->cert_len,
-                   resp_ca_cert->key, resp_ca_cert->key_len);
+               if (s->relay->pki_name)
+                       pkiname = s->relay->pki_name;
+               else
+                       pkiname = s->helo;
+               ssl = ssl_mta_init(pkiname,
+                   resp_ca_cert->cert, resp_ca_cert->cert_len);
                if (ssl == NULL)
                        fatal("mta: ssl_mta_init");
                io_start_tls(&s->io, ssl);
 
                explicit_bzero(resp_ca_cert->cert, resp_ca_cert->cert_len);
-               explicit_bzero(resp_ca_cert->key, resp_ca_cert->key_len);
                free(resp_ca_cert->cert);
-               free(resp_ca_cert->key);
                free(resp_ca_cert);
                return;
 
index cd8b353..2b3e592 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pony.c,v 1.2 2014/04/09 18:55:19 eric Exp $   */
+/*     $OpenBSD: pony.c,v 1.3 2014/04/29 19:13:13 reyk Exp $   */
 
 /*
  * Copyright (c) 2014 Gilles Chehade <gilles@poolp.org>
@@ -213,6 +213,8 @@ pony(void)
        config_peer(PROC_CONTROL);
        config_done();
 
+       ca_engine_init();
+
        if (event_dispatch() < 0)
                fatal("event_dispatch");
        pony_shutdown();
index 5c67388..5ecb84b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: smtp.c,v 1.136 2014/04/19 13:52:49 gilles Exp $       */
+/*     $OpenBSD: smtp.c,v 1.137 2014/04/29 19:13:13 reyk Exp $ */
 
 /*
  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -209,7 +209,7 @@ smtp_setup_events(void)
                dict_xset(env->sc_ssl_dict, k, ssl_ctx);
        }
 
-       purge_config(PURGE_PKI);
+       purge_config(PURGE_PKI_KEYS);
 
        log_debug("debug: smtp: will accept at most %d clients",
            (getdtablesize() - getdtablecount())/2 - SMTP_FD_RESERVE);
index 1fb2ff7..ad1842f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: smtp_session.c,v 1.209 2014/04/29 12:18:27 reyk Exp $ */
+/*     $OpenBSD: smtp_session.c,v 1.210 2014/04/29 19:13:13 reyk Exp $ */
 
 /*
  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -295,6 +295,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
        struct smtp_session             *s;
        struct smtp_rcpt                *rcpt;
        void                            *ssl;
+       char                            *pkiname;
        char                             user[SMTPD_MAXLOGNAME];
        struct msg                       m;
        const char                      *line, *helo;
@@ -584,24 +585,18 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
                        fatal(NULL);
                resp_ca_cert->cert = xstrdup((char *)imsg->data +
                    sizeof *resp_ca_cert, "smtp:ca_cert");
-
-               resp_ca_cert->key = xstrdup((char *)imsg->data +
-                   sizeof *resp_ca_cert + resp_ca_cert->cert_len,
-                   "smtp:ca_key");
-
                if (s->listener->pki_name[0])
-                       ssl_ctx = dict_get(env->sc_ssl_dict, s->listener->pki_name);
+                       pkiname = s->listener->pki_name;
                else
-                       ssl_ctx = dict_get(env->sc_ssl_dict, s->smtpname);
+                       pkiname = s->smtpname;
+               ssl_ctx = dict_get(env->sc_ssl_dict, pkiname);
 
                ssl = ssl_smtp_init(ssl_ctx, smtp_sni_callback, s);
                io_set_read(&s->io);
                io_start_tls(&s->io, ssl);
 
                explicit_bzero(resp_ca_cert->cert, resp_ca_cert->cert_len);
-               explicit_bzero(resp_ca_cert->key, resp_ca_cert->key_len);
                free(resp_ca_cert->cert);
-               free(resp_ca_cert->key);
                free(resp_ca_cert);
                return;
 
index 6e4c348..8abf47d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: smtpd.c,v 1.222 2014/04/29 10:18:06 reyk Exp $        */
+/*     $OpenBSD: smtpd.c,v 1.223 2014/04/29 19:13:13 reyk Exp $        */
 
 /*
  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -47,6 +47,7 @@
 #include <util.h>
 
 #include <openssl/ssl.h>
+#include <openssl/evp.h>
 
 #include "smtpd.h"
 #include "log.h"
@@ -1378,6 +1379,9 @@ imsg_to_str(int type)
        CASE(IMSG_SMTP_EVENT_COMMIT);
        CASE(IMSG_SMTP_EVENT_ROLLBACK);
        CASE(IMSG_SMTP_EVENT_DISCONNECT);
+
+       CASE(IMSG_CA_PRIVENC);
+       CASE(IMSG_CA_PRIVDEC);
        default:
                (void)snprintf(buf, sizeof(buf), "IMSG_??? (%d)", type);
 
index 6ef7ca9..670a470 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: smtpd.h,v 1.456 2014/04/29 12:18:27 reyk Exp $        */
+/*     $OpenBSD: smtpd.h,v 1.457 2014/04/29 19:13:14 reyk Exp $        */
 
 /*
  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -284,6 +284,9 @@ enum imsg_type {
        IMSG_SMTP_EVENT_COMMIT,
        IMSG_SMTP_EVENT_ROLLBACK,
        IMSG_SMTP_EVENT_DISCONNECT,
+
+       IMSG_CA_PRIVENC,
+       IMSG_CA_PRIVDEC
 };
 
 enum blockmodes {
@@ -1032,8 +1035,6 @@ struct ca_cert_resp_msg {
        enum ca_resp_status     status;
        char                   *cert;
        off_t                   cert_len;
-       char                   *key;
-       off_t                   key_len;
 };
 
 struct ca_vrfy_req_msg {
@@ -1070,8 +1071,10 @@ void bounce_fd(int);
 
 
 /* ca.c */
-int    ca_X509_verify(void *, void *, const char *, const char *, const char **);
-
+int     ca_X509_verify(void *, void *, const char *, const char *, const char **);
+void    ca_imsg(struct mproc *, struct imsg *);
+void    ca_init(void);
+int     ca_engine_init(void);
 
 /* compress_backend.c */
 struct compress_backend *compress_backend_lookup(const char *);
@@ -1085,7 +1088,8 @@ int       uncompress_file(FILE *, FILE *);
 #define PURGE_TABLES           0x02
 #define PURGE_RULES            0x04
 #define PURGE_PKI              0x08
-#define PURGE_EVERYTHING       0xff
+#define PURGE_PKI_KEYS         0x10
+#define PURGE_EVERYTHING       0x0f
 void purge_config(uint8_t);
 void init_pipes(void);
 void config_process(enum smtp_proc_type);
@@ -1197,6 +1201,7 @@ void mproc_init(struct mproc *, int);
 void mproc_clear(struct mproc *);
 void mproc_enable(struct mproc *);
 void mproc_disable(struct mproc *);
+void mproc_event_add(struct mproc *);
 void m_compose(struct mproc *, uint32_t, uint32_t, pid_t, int, void *, size_t);
 void m_composev(struct mproc *, uint32_t, uint32_t, pid_t, int,
     const struct iovec *, int);
@@ -1205,6 +1210,7 @@ void m_create(struct mproc *, uint32_t, uint32_t, pid_t, int);
 void m_add(struct mproc *, const void *, size_t);
 void m_add_int(struct mproc *, int);
 void m_add_u32(struct mproc *, uint32_t);
+void m_add_size(struct mproc *, size_t);
 void m_add_time(struct mproc *, time_t);
 void m_add_string(struct mproc *, const char *);
 void m_add_data(struct mproc *, const void *, size_t);
@@ -1215,11 +1221,13 @@ void m_add_sockaddr(struct mproc *, const struct sockaddr *);
 void m_add_mailaddr(struct mproc *, const struct mailaddr *);
 void m_add_envelope(struct mproc *, const struct envelope *);
 void m_close(struct mproc *);
+void m_flush(struct mproc *);
 
 void m_msg(struct msg *, struct imsg *);
 int  m_is_eom(struct msg *);
 void m_end(struct msg *);
 void m_get_int(struct msg *, int *);
+void m_get_size(struct msg *, size_t *);
 void m_get_u32(struct msg *, uint32_t *);
 void m_get_time(struct msg *, time_t *);
 void m_get_string(struct msg *, const char **);
@@ -1320,7 +1328,7 @@ const char *imsg_to_str(int);
 
 
 /* ssl_smtpd.c */
-void   *ssl_mta_init(char *, off_t, char *, off_t);
+void   *ssl_mta_init(void *, char *, off_t);
 void   *ssl_smtp_init(void *, void *, void *);
 
 
index b636ae0..4387e79 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ssl.c,v 1.62 2014/04/29 10:08:55 reyk Exp $   */
+/*     $OpenBSD: ssl.c,v 1.63 2014/04/29 19:13:14 reyk Exp $   */
 
 /*
  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -67,8 +67,7 @@ ssl_setup(SSL_CTX **ctxp, struct pki *pki)
        DH      *dh;
        SSL_CTX *ctx;
 
-       ctx = ssl_ctx_create(pki->pki_cert, pki->pki_cert_len,
-           pki->pki_key, pki->pki_key_len);
+       ctx = ssl_ctx_create(pki->pki_name, pki->pki_cert, pki->pki_cert_len);
 
        if (!SSL_CTX_set_session_id_context(ctx,
                (const unsigned char *)pki->pki_name,
@@ -243,7 +242,7 @@ fail:
 }
 
 SSL_CTX *
-ssl_ctx_create(char *cert, off_t cert_len, char *key, off_t key_len)
+ssl_ctx_create(void *pkiname, char *cert, off_t cert_len)
 {
        SSL_CTX *ctx;
 
@@ -265,13 +264,14 @@ ssl_ctx_create(char *cert, off_t cert_len, char *key, off_t key_len)
                fatal("ssl_ctx_create: could not set cipher list");
        }
 
-       if (cert != NULL && key != NULL) {
+       if (cert != NULL) {
                if (!ssl_ctx_use_certificate_chain(ctx, cert, cert_len)) {
                        ssl_error("ssl_ctx_create");
                        fatal("ssl_ctx_create: invalid certificate chain");
-               } else if (!ssl_ctx_use_private_key(ctx, key, key_len)) {
+               } else if (!ssl_ctx_fake_private_key(ctx,
+                   pkiname, cert, cert_len)) {
                        ssl_error("ssl_ctx_create");
-                       fatal("ssl_ctx_create: could not use private key");
+                       fatal("ssl_ctx_create: could not fake private key");
                } else if (!SSL_CTX_check_private_key(ctx)) {
                        ssl_error("ssl_ctx_create");
                        fatal("ssl_ctx_create: invalid private key");
@@ -453,3 +453,88 @@ ssl_set_ecdh_curve(SSL_CTX *ctx, const char *curve)
        SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
        EC_KEY_free(ecdh);
 }
+
+int
+ssl_ctx_load_pkey(SSL_CTX *ctx, void *data, char *buf, off_t len,
+    X509 **x509ptr, EVP_PKEY **pkeyptr)
+{
+       int              ret = 0;
+       BIO             *in;
+       X509            *x509 = NULL;
+       EVP_PKEY        *pkey = NULL;
+       RSA             *rsa = NULL;
+
+       if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
+               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_BUF_LIB);
+               return (0);
+       }
+
+       if ((x509 = PEM_read_bio_X509(in, NULL,
+           ssl_getpass_cb, NULL)) == NULL) {
+               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_PEM_LIB);
+               goto fail;
+       }
+
+       if ((pkey = X509_get_pubkey(x509)) == NULL) {
+               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_X509_LIB);
+               goto fail;
+       }
+
+       if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
+               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_EVP_LIB);
+               goto fail;
+       }
+
+       if (data)
+               RSA_set_ex_data(rsa, 0, data);
+
+       *x509ptr = x509;
+       *pkeyptr = pkey;
+       ret = 1;
+
+       goto done;
+
+ fail:
+       ssl_error("ssl_ctx_load_pkey");
+
+       if (pkey != NULL)
+               EVP_PKEY_free(pkey);
+       if (x509 != NULL)
+               X509_free(x509);
+
+ done:
+       if (in != NULL)
+               BIO_free(in);
+
+       return ret;
+}
+
+int
+ssl_ctx_fake_private_key(SSL_CTX *ctx, void *data, char *buf, off_t len)
+{
+       int              ret = 0;
+       EVP_PKEY        *pkey = NULL;
+       X509            *x509 = NULL;
+
+       if (!ssl_ctx_load_pkey(ctx, data, buf, len, &x509, &pkey))
+               return (0);
+
+       /*
+        * Use the public key as the "private" key - the secret key
+        * parameters are hidden in an extra process that will be
+        * contacted by the RSA engine.  The SSL/TLS library needs at
+        * least the public key parameters in the current process.
+        */
+       ret = SSL_CTX_use_PrivateKey(ctx, pkey);
+       if (!ret) {
+               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_SSL_LIB);
+               ssl_error("ssl_ctx_fake_private_key");
+       }
+
+       if (pkey != NULL)
+               EVP_PKEY_free(pkey);
+       if (x509 != NULL)
+               X509_free(x509);
+
+       return (ret);
+}
index eb4e65f..c2df38a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ssl.h,v 1.6 2014/04/29 10:08:55 reyk Exp $    */
+/*     $OpenBSD: ssl.h,v 1.7 2014/04/29 19:13:14 reyk Exp $    */
 /*
  * Copyright (c) 2013 Gilles Chehade <gilles@poolp.org>
  *
@@ -34,6 +34,8 @@ struct pki {
        char                    *pki_key;
        off_t                    pki_key_len;
 
+       EVP_PKEY                *pki_pkey;
+
        char                    *pki_dhparams_file;
        char                    *pki_dhparams;
        off_t                    pki_dhparams_len;
@@ -42,7 +44,7 @@ struct pki {
 /* ssl.c */
 void           ssl_init(void);
 int            ssl_setup(SSL_CTX **, struct pki *);
-SSL_CTX               *ssl_ctx_create(char *, off_t, char *, off_t);
+SSL_CTX               *ssl_ctx_create(void *, char *, off_t);
 int            ssl_cmp(struct pki *, struct pki *);
 DH            *get_dh1024(void);
 DH            *get_dh_from_memory(char *, size_t);
@@ -60,9 +62,11 @@ int          ssl_load_keyfile(struct pki *, const char *, const char *);
 int            ssl_load_cafile(struct pki *, const char *);
 int            ssl_load_dhparams(struct pki *, const char *);
 
+int            ssl_ctx_load_pkey(SSL_CTX *, void *, char *, off_t,
+                   X509 **, EVP_PKEY **);
+int            ssl_ctx_fake_private_key(SSL_CTX *, void *, char *, off_t);
 
 /* ssl_privsep.c */
-int     ssl_ctx_use_private_key(SSL_CTX *, char *, off_t);
-int     ssl_ctx_use_certificate_chain(SSL_CTX *, char *, off_t);
-int      ssl_ctx_load_verify_memory(SSL_CTX *, char *, off_t);
-int      ssl_by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **);
+int            ssl_ctx_use_certificate_chain(SSL_CTX *, char *, off_t);
+int            ssl_ctx_load_verify_memory(SSL_CTX *, char *, off_t);
+int            ssl_by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **);
index e53fb4f..66dd0c9 100644 (file)
@@ -1,4 +1,4 @@
-/*      $OpenBSD: ssl_privsep.c,v 1.6 2014/02/04 13:44:41 eric Exp $    */
+/*      $OpenBSD: ssl_privsep.c,v 1.7 2014/04/29 19:13:14 reyk Exp $    */
 
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
@@ -96,37 +96,6 @@ X509_LOOKUP_METHOD x509_mem_lookup = {
 
 #define X509_L_ADD_MEM 3
 
-int
-ssl_ctx_use_private_key(SSL_CTX *ctx, char *buf, off_t len)
-{
-       int              ret;
-       BIO             *in;
-       EVP_PKEY        *pkey;
-
-       ret = 0;
-
-       if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
-               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_BUF_LIB);
-               return 0;
-       }
-
-       pkey = PEM_read_bio_PrivateKey(in, NULL,
-           ctx->default_passwd_callback,
-           ctx->default_passwd_callback_userdata);
-
-       if (pkey == NULL) {
-               SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_PEM_LIB);
-               goto end;
-       }
-       ret = SSL_CTX_use_PrivateKey(ctx, pkey);
-       EVP_PKEY_free(pkey);
-end:
-       if (in != NULL)
-               BIO_free(in);
-       return ret;
-}
-
-
 int
 ssl_ctx_use_certificate_chain(SSL_CTX *ctx, char *buf, off_t len)
 {
index 49326b3..d7181bd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ssl_smtpd.c,v 1.6 2014/04/29 12:18:27 reyk Exp $      */
+/*     $OpenBSD: ssl_smtpd.c,v 1.7 2014/04/29 19:13:14 reyk Exp $      */
 
 /*
  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
 
 
 void *
-ssl_mta_init(char *cert, off_t cert_len, char *key, off_t key_len)
+ssl_mta_init(void *pkiname, char *cert, off_t cert_len)
 {
        SSL_CTX *ctx = NULL;
        SSL     *ssl = NULL;
 
-       ctx = ssl_ctx_create(cert, cert_len, key, key_len);
+       ctx = ssl_ctx_create(pkiname, cert, cert_len);
 
        if ((ssl = SSL_new(ctx)) == NULL)
                goto err;