From c4df3bf27090249fec8edcd55b8fb30d01283bdf Mon Sep 17 00:00:00 2001 From: reyk Date: Tue, 29 Apr 2014 19:13:13 +0000 Subject: [PATCH] Implement RSA privilege separation for OpenSMTPD, based on my previous 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@ --- usr.sbin/smtpd/ca.c | 333 +++++++++++++++++++++++++++++++++- usr.sbin/smtpd/config.c | 28 ++- usr.sbin/smtpd/lka.c | 15 +- usr.sbin/smtpd/mproc.c | 37 +++- usr.sbin/smtpd/mta_session.c | 18 +- usr.sbin/smtpd/pony.c | 4 +- usr.sbin/smtpd/smtp.c | 4 +- usr.sbin/smtpd/smtp_session.c | 15 +- usr.sbin/smtpd/smtpd.c | 6 +- usr.sbin/smtpd/smtpd.h | 22 ++- usr.sbin/smtpd/ssl.c | 99 +++++++++- usr.sbin/smtpd/ssl.h | 16 +- usr.sbin/smtpd/ssl_privsep.c | 33 +--- usr.sbin/smtpd/ssl_smtpd.c | 6 +- 14 files changed, 538 insertions(+), 98 deletions(-) diff --git a/usr.sbin/smtpd/ca.c b/usr.sbin/smtpd/ca.c index 32fb84c00c5..36fbd22b9b7 100644 --- a/usr.sbin/smtpd/ca.c +++ b/usr.sbin/smtpd/ca.c @@ -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 * Copyright (c) 2012 Gilles Chehade * * Permission to use, copy, modify, and distribute this software for any @@ -17,16 +18,75 @@ */ #include +#include +#include -#include -#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/usr.sbin/smtpd/config.c b/usr.sbin/smtpd/config.c index 9eff839309a..10843ef56f7 100644 --- a/usr.sbin/smtpd/config.c +++ b/usr.sbin/smtpd/config.c @@ -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 @@ -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; + } } } diff --git a/usr.sbin/smtpd/lka.c b/usr.sbin/smtpd/lka.c index abfc4ba52fa..dc7f7852924 100644 --- a/usr.sbin/smtpd/lka.c +++ b/usr.sbin/smtpd/lka.c @@ -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 @@ -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); diff --git a/usr.sbin/smtpd/mproc.c b/usr.sbin/smtpd/mproc.c index a5223d61e0c..50e484fd9c6 100644 --- a/usr.sbin/smtpd/mproc.c +++ b/usr.sbin/smtpd/mproc.c @@ -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 @@ -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) { diff --git a/usr.sbin/smtpd/mta_session.c b/usr.sbin/smtpd/mta_session.c index 0be56d71cbb..e3b43b7f50e 100644 --- a/usr.sbin/smtpd/mta_session.c +++ b/usr.sbin/smtpd/mta_session.c @@ -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 @@ -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; diff --git a/usr.sbin/smtpd/pony.c b/usr.sbin/smtpd/pony.c index cd8b353662e..2b3e59226ed 100644 --- a/usr.sbin/smtpd/pony.c +++ b/usr.sbin/smtpd/pony.c @@ -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 @@ -213,6 +213,8 @@ pony(void) config_peer(PROC_CONTROL); config_done(); + ca_engine_init(); + if (event_dispatch() < 0) fatal("event_dispatch"); pony_shutdown(); diff --git a/usr.sbin/smtpd/smtp.c b/usr.sbin/smtpd/smtp.c index 5c673888ddb..5ecb84b53e1 100644 --- a/usr.sbin/smtpd/smtp.c +++ b/usr.sbin/smtpd/smtp.c @@ -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 @@ -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); diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c index 1fb2ff7b3d7..ad1842fae84 100644 --- a/usr.sbin/smtpd/smtp_session.c +++ b/usr.sbin/smtpd/smtp_session.c @@ -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 @@ -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; diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c index 6e4c34835ef..8abf47dda7e 100644 --- a/usr.sbin/smtpd/smtpd.c +++ b/usr.sbin/smtpd/smtpd.c @@ -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 @@ -47,6 +47,7 @@ #include #include +#include #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); diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index 6ef7ca9e010..670a470a3c7 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -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 @@ -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 *); diff --git a/usr.sbin/smtpd/ssl.c b/usr.sbin/smtpd/ssl.c index b636ae0fd38..4387e790dcd 100644 --- a/usr.sbin/smtpd/ssl.c +++ b/usr.sbin/smtpd/ssl.c @@ -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 @@ -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); +} diff --git a/usr.sbin/smtpd/ssl.h b/usr.sbin/smtpd/ssl.h index eb4e65f0550..c2df38a66ab 100644 --- a/usr.sbin/smtpd/ssl.h +++ b/usr.sbin/smtpd/ssl.h @@ -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 * @@ -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 **); diff --git a/usr.sbin/smtpd/ssl_privsep.c b/usr.sbin/smtpd/ssl_privsep.c index e53fb4f418e..66dd0c9b5f0 100644 --- a/usr.sbin/smtpd/ssl_privsep.c +++ b/usr.sbin/smtpd/ssl_privsep.c @@ -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) { diff --git a/usr.sbin/smtpd/ssl_smtpd.c b/usr.sbin/smtpd/ssl_smtpd.c index 49326b3a4f3..d7181bda243 100644 --- a/usr.sbin/smtpd/ssl_smtpd.c +++ b/usr.sbin/smtpd/ssl_smtpd.c @@ -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 @@ -44,12 +44,12 @@ 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; -- 2.20.1