From 41b8cf0b57b2fefd16311de17ef77de326470257 Mon Sep 17 00:00:00 2001 From: millert Date: Fri, 18 Feb 2022 16:57:36 +0000 Subject: [PATCH] Revert changes to use the new libtls signer api There are bugs in the new libtls signer that can lead to a crash. OK tb@ jsing@ --- usr.sbin/smtpd/ca.c | 647 ++++++++++++++++++++++++++++++++---- usr.sbin/smtpd/dispatcher.c | 4 +- usr.sbin/smtpd/mta.c | 10 +- usr.sbin/smtpd/smtp.c | 10 +- usr.sbin/smtpd/smtpd.c | 6 +- usr.sbin/smtpd/smtpd.h | 14 +- usr.sbin/smtpd/ssl.c | 59 +++- usr.sbin/smtpd/ssl.h | 3 +- 8 files changed, 672 insertions(+), 81 deletions(-) diff --git a/usr.sbin/smtpd/ca.c b/usr.sbin/smtpd/ca.c index 03304e95c4e..84997dba172 100644 --- a/usr.sbin/smtpd/ca.c +++ b/usr.sbin/smtpd/ca.c @@ -1,7 +1,6 @@ -/* $OpenBSD: ca.c,v 1.41 2022/02/12 18:22:04 eric Exp $ */ +/* $OpenBSD: ca.c,v 1.42 2022/02/18 16:57:36 millert Exp $ */ /* - * Copyright (c) 2021 Eric Faurot * Copyright (c) 2014 Reyk Floeter * Copyright (c) 2012 Gilles Chehade * @@ -18,23 +17,45 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include +#include #include #include -#include #include -#include #include #include "smtpd.h" -#include "ssl.h" #include "log.h" +#include "ssl.h" -static void ca_imsg(struct mproc *, struct imsg *); -static void ca_init(void); - -static struct tls_signer *signer; -static uint64_t reqid = 0; +static int ca_verify_cb(int, X509_STORE_CTX *); + +static int rsae_send_imsg(int, const unsigned char *, unsigned char *, + RSA *, int, unsigned int); +static int rsae_pub_enc(int, const unsigned char *, unsigned char *, + RSA *, int); +static int rsae_pub_dec(int,const unsigned char *, unsigned char *, + RSA *, int); +static int rsae_priv_enc(int, const unsigned char *, unsigned char *, + RSA *, int); +static int rsae_priv_dec(int, const unsigned char *, unsigned 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_keygen(RSA *, int, BIGNUM *, BN_GENCB *); + +static ECDSA_SIG *ecdsae_do_sign(const unsigned char *, int, const BIGNUM *, + const BIGNUM *, EC_KEY *); +static int ecdsae_sign_setup(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **); +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 ca_shutdown(void) @@ -48,9 +69,7 @@ ca(void) { struct passwd *pw; - ca_init(); - - purge_config(PURGE_EVERYTHING); + purge_config(PURGE_LISTENERS|PURGE_TABLES|PURGE_RULES|PURGE_DISPATCHERS); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); @@ -79,6 +98,9 @@ ca(void) config_peer(PROC_PARENT); config_peer(PROC_DISPATCHER); + /* Ignore them until we get our config */ + mproc_disable(p_dispatcher); + if (pledge("stdio", NULL) == -1) fatal("pledge"); @@ -88,35 +110,120 @@ ca(void) return (0); } -static void +void ca_init(void) { - struct pki *pki; - void *iter_dict; - - if ((signer = tls_signer_new()) == NULL) - fatal("tls_signer_new"); - + BIO *in = NULL; + EVP_PKEY *pkey = NULL; + 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, NULL, (void **)&pki)) { + while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) { if (pki->pki_key == NULL) continue; - if (tls_signer_add_keypair_mem(signer, pki->pki_cert, - pki->pki_cert_len, pki->pki_key, pki->pki_key_len) == -1) - fatalx("ca_init: tls_signer_add_keypair_mem"); + + 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); + + 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); } } -static void +static int +ca_verify_cb(int ok, X509_STORE_CTX *ctx) +{ + switch (X509_STORE_CTX_get_error(ctx)) { + case X509_V_OK: + break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + break; + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + break; + case X509_V_ERR_NO_EXPLICIT_POLICY: + break; + } + return ok; +} + +int +ca_X509_verify(void *certificate, void *chain, const char *CAfile, + const char *CRLfile, const char **errstr) +{ + X509_STORE *store = NULL; + X509_STORE_CTX *xsc = NULL; + int ret = 0; + long error = 0; + + if ((store = X509_STORE_new()) == NULL) + goto end; + + if (!X509_STORE_load_locations(store, CAfile, NULL)) { + log_warn("warn: unable to load CA file %s", CAfile); + goto end; + } + X509_STORE_set_default_paths(store); + + if ((xsc = X509_STORE_CTX_new()) == NULL) + goto end; + + if (X509_STORE_CTX_init(xsc, store, certificate, chain) != 1) + goto end; + + X509_STORE_CTX_set_verify_cb(xsc, ca_verify_cb); + + ret = X509_verify_cert(xsc); + +end: + *errstr = NULL; + if (ret != 1) { + if (xsc) { + error = X509_STORE_CTX_get_error(xsc); + *errstr = X509_verify_cert_error_string(error); + } + else if (ERR_peek_last_error()) + *errstr = ERR_error_string(ERR_peek_last_error(), NULL); + } + + X509_STORE_CTX_free(xsc); + X509_STORE_free(store); + + return ret > 0 ? 1 : 0; +} + +void ca_imsg(struct mproc *p, struct imsg *imsg) { - const void *input = NULL; - uint8_t *sig = NULL; - struct msg m; - const char *hash; - size_t input_len, siglen; - int padding_type, ret, v; - uint64_t id; + EVP_PKEY *pkey; + RSA *rsa = NULL; + EC_KEY *ecdsa = NULL; + const void *from = NULL; + unsigned char *to = NULL; + struct msg m; + const char *hash; + size_t flen, tlen, padding; + int buf_len; + int ret = 0; + uint64_t id; + int v; if (imsg == NULL) ca_shutdown(); @@ -125,6 +232,10 @@ ca_imsg(struct mproc *p, struct imsg *imsg) case IMSG_CONF_START: return; case IMSG_CONF_END: + ca_init(); + + /* Start fulfilling requests */ + mproc_enable(p_dispatcher); return; case IMSG_CTL_VERBOSE: @@ -141,39 +252,112 @@ ca_imsg(struct mproc *p, struct imsg *imsg) profiling = v; return; - case IMSG_CA_SIGN: + case IMSG_CA_RSA_PRIVENC: + case IMSG_CA_RSA_PRIVDEC: m_msg(&m, imsg); m_get_id(&m, &id); m_get_string(&m, &hash); - m_get_data(&m, &input, &input_len); - m_get_int(&m, &padding_type); + m_get_data(&m, &from, &flen); + m_get_size(&m, &tlen); + m_get_size(&m, &padding); m_end(&m); - ret = tls_signer_sign(signer, hash, input, input_len, - padding_type, &sig, &siglen); + 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"); + + switch (imsg->hdr.type) { + case IMSG_CA_RSA_PRIVENC: + ret = RSA_private_encrypt(flen, from, to, rsa, + padding); + break; + case IMSG_CA_RSA_PRIVDEC: + ret = RSA_private_decrypt(flen, from, to, rsa, + padding); + break; + } m_create(p, imsg->hdr.type, 0, 0, -1); m_add_id(p, id); m_add_int(p, ret); - if (ret != -1) - m_add_data(p, sig, siglen); + if (ret > 0) + m_add_data(p, to, (size_t)ret); m_close(p); - free(sig); + + free(to); + RSA_free(rsa); + return; + + case IMSG_CA_ECDSA_SIGN: + m_msg(&m, imsg); + m_get_id(&m, &id); + m_get_string(&m, &hash); + m_get_data(&m, &from, &flen); + m_end(&m); + + 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) + fatalx("ca_imsg: calloc"); + ret = ECDSA_sign(0, from, flen, to, &buf_len, ecdsa); + m_create(p, imsg->hdr.type, 0, 0, -1); + m_add_id(p, id); + m_add_int(p, ret); + if (ret > 0) + m_add_data(p, to, (size_t)buf_len); + m_close(p); + free(to); + EC_KEY_free(ecdsa); return; } fatalx("ca_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); } +/* + * RSA privsep engine (called from unprivileged processes) + */ + +const RSA_METHOD *rsa_default = NULL; + +static RSA_METHOD *rsae_method = NULL; + static int -ca_imsg_get_sync(uint32_t type, uint8_t **output, size_t *output_len) +rsae_send_imsg(int flen, const unsigned char *from, unsigned char *to, + RSA *rsa, int padding, unsigned int cmd) { - struct imsgbuf *ibuf; - struct imsg imsg; - struct msg m; - const void *data; - uint64_t id; - int ret, n, done = 0; + int ret = 0; + struct imsgbuf *ibuf; + struct imsg imsg; + int n, done = 0; + const void *toptr; + char *hash; + size_t tlen; + struct msg m; + uint64_t id; + + if ((hash = 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_ca, cmd, 0, 0, -1); + reqid++; + m_add_id(p_ca, reqid); + 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); + m_flush(p_ca); ibuf = &p_ca->imsgbuf; @@ -191,7 +375,11 @@ ca_imsg_get_sync(uint32_t type, uint8_t **output, size_t *output_len) log_imsg(PROC_DISPATCHER, PROC_CA, &imsg); - if (imsg.hdr.type != type) { + switch (imsg.hdr.type) { + case IMSG_CA_RSA_PRIVENC: + case IMSG_CA_RSA_PRIVDEC: + break; + default: /* Another imsg is queued up in the buffer */ dispatcher_imsg(p_ca, &imsg); imsg_free(&imsg); @@ -203,36 +391,359 @@ ca_imsg_get_sync(uint32_t type, uint8_t **output, size_t *output_len) if (id != reqid) fatalx("invalid response id"); m_get_int(&m, &ret); - if (ret != -1) { - m_get_data(&m, &data, output_len); - if ((*output = malloc(*output_len)) == NULL) { - *output_len = 0; - ret = -1; - } - else - memcpy(*output, data, *output_len); - } + if (ret > 0) + m_get_data(&m, &toptr, &tlen); m_end(&m); - imsg_free(&imsg); + + if (ret > 0) + memcpy(to, toptr, tlen); done = 1; + + imsg_free(&imsg); } } - mproc_event_add(p_ca); return (ret); } -int -ca_sign(void *arg, const char *hash, const uint8_t *input, size_t input_len, - int padding_type, uint8_t **output, size_t *output_len) +static int +rsae_pub_enc(int flen,const unsigned char *from, unsigned char *to, RSA *rsa, + int padding) { - m_create(p_ca, IMSG_CA_SIGN, 0, 0, -1); - m_add_id(p_ca, ++reqid); + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + return (RSA_meth_get_pub_enc(rsa_default)(flen, from, to, rsa, padding)); +} + +static int +rsae_pub_dec(int flen,const unsigned char *from, unsigned char *to, RSA *rsa, + int padding) +{ + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + return (RSA_meth_get_pub_dec(rsa_default)(flen, from, to, rsa, padding)); +} + +static int +rsae_priv_enc(int flen, const unsigned char *from, unsigned 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_RSA_PRIVENC)); + return (RSA_meth_get_priv_enc(rsa_default)(flen, from, to, rsa, padding)); +} + +static int +rsae_priv_dec(int flen, const unsigned char *from, unsigned 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_RSA_PRIVDEC)); + + return (RSA_meth_get_priv_dec(rsa_default)(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_meth_get_mod_exp(rsa_default)(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_meth_get_bn_mod_exp(rsa_default)(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_meth_get_init(rsa_default) == NULL) + return (1); + return (RSA_meth_get_init(rsa_default)(rsa)); +} + +static int +rsae_finish(RSA *rsa) +{ + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + if (RSA_meth_get_finish(rsa_default) == NULL) + return (1); + return (RSA_meth_get_finish(rsa_default)(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_meth_get_keygen(rsa_default)(rsa, bits, e, cb)); +} + + +/* + * ECDSA privsep engine (called from unprivileged processes) + */ + +const ECDSA_METHOD *ecdsa_default = NULL; + +static ECDSA_METHOD *ecdsae_method = NULL; + +ECDSA_METHOD * +ECDSA_METHOD_new_temporary(const char *name, int); + +ECDSA_METHOD * +ECDSA_METHOD_new_temporary(const char *name, int flags) +{ + ECDSA_METHOD *ecdsa; + + if ((ecdsa = calloc(1, sizeof (*ecdsa))) == NULL) + return NULL; + + if ((ecdsa->name = strdup(name)) == NULL) { + free(ecdsa); + return NULL; + } + + ecdsa->flags = flags; + return ecdsa; +} + +static ECDSA_SIG * +ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len, + const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey) +{ + int ret = 0; + struct imsgbuf *ibuf; + struct imsg imsg; + int n, done = 0; + const void *toptr; + char *hash; + size_t tlen; + struct msg m; + uint64_t id; + ECDSA_SIG *sig = NULL; + + if ((hash = ECDSA_get_ex_data(eckey, 0)) == NULL) + return (0); + + /* + * Send a synchronous imsg because we cannot defer the ECDSA + * operation in OpenSSL's engine layer. + */ + m_create(p_ca, IMSG_CA_ECDSA_SIGN, 0, 0, -1); + reqid++; + m_add_id(p_ca, reqid); m_add_string(p_ca, hash); - m_add_data(p_ca, input, input_len); - m_add_int(p_ca, padding_type); + m_add_data(p_ca, (const void *)dgst, (size_t)dgst_len); m_flush(p_ca); - return (ca_imsg_get_sync(IMSG_CA_SIGN, output, output_len)); + ibuf = &p_ca->imsgbuf; + + while (!done) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + 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; + + log_imsg(PROC_DISPATCHER, PROC_CA, &imsg); + + switch (imsg.hdr.type) { + case IMSG_CA_ECDSA_SIGN: + break; + default: + /* Another imsg is queued up in the buffer */ + dispatcher_imsg(p_ca, &imsg); + imsg_free(&imsg); + continue; + } + + m_msg(&m, &imsg); + m_get_id(&m, &id); + if (id != reqid) + fatalx("invalid response id"); + m_get_int(&m, &ret); + if (ret > 0) + m_get_data(&m, &toptr, &tlen); + m_end(&m); + done = 1; + + if (ret > 0) + d2i_ECDSA_SIG(&sig, (const unsigned char **)&toptr, tlen); + imsg_free(&imsg); + } + } + mproc_event_add(p_ca); + + return (sig); +} + +ECDSA_SIG * +ecdsae_do_sign(const unsigned char *dgst, int dgst_len, + const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey) +{ + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + if (ECDSA_get_ex_data(eckey, 0) != NULL) + return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey)); + return (ecdsa_default->ecdsa_do_sign(dgst, dgst_len, inv, rp, eckey)); +} + +int +ecdsae_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, + BIGNUM **r) +{ + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + return (ecdsa_default->ecdsa_sign_setup(eckey, ctx, kinv, r)); +} + +int +ecdsae_do_verify(const unsigned char *dgst, int dgst_len, + const ECDSA_SIG *sig, EC_KEY *eckey) +{ + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + return (ecdsa_default->ecdsa_do_verify(dgst, dgst_len, sig, eckey)); +} + + +static void +rsa_engine_init(void) +{ + ENGINE *e; + const char *errstr, *name; + + if ((rsae_method = RSA_meth_new("RSA privsep engine", 0)) == NULL) { + errstr = "RSA_meth_new"; + goto fail; + } + + RSA_meth_set_pub_enc(rsae_method, rsae_pub_enc); + RSA_meth_set_pub_dec(rsae_method, rsae_pub_dec); + RSA_meth_set_priv_enc(rsae_method, rsae_priv_enc); + RSA_meth_set_priv_dec(rsae_method, rsae_priv_dec); + RSA_meth_set_mod_exp(rsae_method, rsae_mod_exp); + RSA_meth_set_bn_mod_exp(rsae_method, rsae_bn_mod_exp); + RSA_meth_set_init(rsae_method, rsae_init); + RSA_meth_set_finish(rsae_method, rsae_finish); + RSA_meth_set_keygen(rsae_method, rsae_keygen); + + if ((e = ENGINE_get_default_RSA()) == NULL) { + if ((e = ENGINE_new()) == NULL) { + errstr = "ENGINE_new"; + goto fail; + } + if (!ENGINE_set_name(e, RSA_meth_get0_name(rsae_method))) { + errstr = "ENGINE_set_name"; + goto fail; + } + if ((rsa_default = RSA_get_default_method()) == NULL) { + errstr = "RSA_get_default_method"; + goto fail; + } + } else if ((rsa_default = ENGINE_get_RSA(e)) == NULL) { + errstr = "ENGINE_get_RSA"; + goto fail; + } + + if ((name = ENGINE_get_name(e)) == NULL) + name = "unknown RSA engine"; + + log_debug("debug: %s: using %s", __func__, name); + + if (RSA_meth_get_mod_exp(rsa_default) == NULL) + RSA_meth_set_mod_exp(rsae_method, NULL); + if (RSA_meth_get_bn_mod_exp(rsa_default) == NULL) + RSA_meth_set_bn_mod_exp(rsae_method, NULL); + if (RSA_meth_get_keygen(rsa_default) == NULL) + RSA_meth_set_keygen(rsae_method, NULL); + RSA_meth_set_flags(rsae_method, + RSA_meth_get_flags(rsa_default) | RSA_METHOD_FLAG_NO_CHECK); + RSA_meth_set0_app_data(rsae_method, + RSA_meth_get0_app_data(rsa_default)); + + if (!ENGINE_set_RSA(e, rsae_method)) { + errstr = "ENGINE_set_RSA"; + goto fail; + } + if (!ENGINE_set_default_RSA(e)) { + errstr = "ENGINE_set_default_RSA"; + goto fail; + } + + return; + + fail: + ssl_error(errstr); + fatalx("%s", errstr); +} + +static void +ecdsa_engine_init(void) +{ + ENGINE *e; + const char *errstr, *name; + + if ((ecdsae_method = ECDSA_METHOD_new_temporary("ECDSA privsep engine", 0)) == NULL) { + errstr = "ECDSA_METHOD_new_temporary"; + goto fail; + } + + ecdsae_method->ecdsa_do_sign = ecdsae_do_sign; + ecdsae_method->ecdsa_sign_setup = ecdsae_sign_setup; + ecdsae_method->ecdsa_do_verify = ecdsae_do_verify; + + if ((e = ENGINE_get_default_ECDSA()) == NULL) { + if ((e = ENGINE_new()) == NULL) { + errstr = "ENGINE_new"; + goto fail; + } + if (!ENGINE_set_name(e, ecdsae_method->name)) { + errstr = "ENGINE_set_name"; + goto fail; + } + if ((ecdsa_default = ECDSA_get_default_method()) == NULL) { + errstr = "ECDSA_get_default_method"; + goto fail; + } + } else if ((ecdsa_default = ENGINE_get_ECDSA(e)) == NULL) { + errstr = "ENGINE_get_ECDSA"; + goto fail; + } + + if ((name = ENGINE_get_name(e)) == NULL) + name = "unknown ECDSA engine"; + + log_debug("debug: %s: using %s", __func__, name); + + if (!ENGINE_set_ECDSA(e, ecdsae_method)) { + errstr = "ENGINE_set_ECDSA"; + goto fail; + } + if (!ENGINE_set_default_ECDSA(e)) { + errstr = "ENGINE_set_default_ECDSA"; + goto fail; + } + + return; + + fail: + ssl_error(errstr); + fatalx("%s", errstr); +} + +void +ca_engine_init(void) +{ + rsa_engine_init(); + ecdsa_engine_init(); } diff --git a/usr.sbin/smtpd/dispatcher.c b/usr.sbin/smtpd/dispatcher.c index f1d061da5d3..002ba8b9ccd 100644 --- a/usr.sbin/smtpd/dispatcher.c +++ b/usr.sbin/smtpd/dispatcher.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dispatcher.c,v 1.6 2022/02/12 18:22:04 eric Exp $ */ +/* $OpenBSD: dispatcher.c,v 1.7 2022/02/18 16:57:36 millert Exp $ */ /* * Copyright (c) 2014 Gilles Chehade @@ -132,6 +132,8 @@ dispatcher(void) { struct passwd *pw; + ca_engine_init(); + mda_postfork(); mta_postfork(); smtp_postfork(); diff --git a/usr.sbin/smtpd/mta.c b/usr.sbin/smtpd/mta.c index 080c562c8c4..dbcf2c01581 100644 --- a/usr.sbin/smtpd/mta.c +++ b/usr.sbin/smtpd/mta.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mta.c,v 1.242 2022/02/12 18:22:04 eric Exp $ */ +/* $OpenBSD: mta.c,v 1.243 2022/02/18 16:57:36 millert Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard @@ -124,6 +124,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; @@ -500,7 +506,7 @@ mta_setup_dispatcher(struct dispatcher *dispatcher) fatal("client pki \"%s\" not found ", remote->pki); tls_config_set_dheparams(config, dheparams[pki->pki_dhe]); - tls_config_set_sign_cb(config, ca_sign, NULL); + 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"); diff --git a/usr.sbin/smtpd/smtp.c b/usr.sbin/smtpd/smtp.c index 00bda472ddd..a9b7d48c8a5 100644 --- a/usr.sbin/smtpd/smtp.c +++ b/usr.sbin/smtpd/smtp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtp.c,v 1.172 2022/02/12 18:22:04 eric Exp $ */ +/* $OpenBSD: smtp.c,v 1.173 2022/02/18 16:57:36 millert Exp $ */ /* * Copyright (c) 2008 Gilles Chehade @@ -47,6 +47,12 @@ 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; static size_t maxsessions; @@ -177,7 +183,7 @@ smtp_setup_listener_tls(struct listener *l) if (tls_config_set_dheparams(config, dheparams[pki->pki_dhe]) == -1) fatal("tls_config_set_dheparams"); - tls_config_set_sign_cb(config, ca_sign, NULL); + tls_config_use_fake_private_key(config); for (i = 0; i < l->pkicount; i++) { pki = l->pki[i]; if (i == 0) { diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c index 08f76233c00..636c898f803 100644 --- a/usr.sbin/smtpd/smtpd.c +++ b/usr.sbin/smtpd/smtpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.c,v 1.342 2022/02/12 18:22:04 eric Exp $ */ +/* $OpenBSD: smtpd.c,v 1.343 2022/02/18 16:57:36 millert Exp $ */ /* * Copyright (c) 2008 Gilles Chehade @@ -2100,7 +2100,9 @@ imsg_to_str(int type) CASE(IMSG_FILTER_SMTP_DATA_BEGIN); CASE(IMSG_FILTER_SMTP_DATA_END); - CASE(IMSG_CA_SIGN); + CASE(IMSG_CA_RSA_PRIVENC); + CASE(IMSG_CA_RSA_PRIVDEC); + CASE(IMSG_CA_ECDSA_SIGN); default: (void)snprintf(buf, sizeof(buf), "IMSG_??? (%d)", type); diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index df86eb05212..125a6a5dfbe 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.673 2022/02/12 18:22:04 eric Exp $ */ +/* $OpenBSD: smtpd.h,v 1.674 2022/02/18 16:57:36 millert Exp $ */ /* * Copyright (c) 2008 Gilles Chehade @@ -329,7 +329,9 @@ enum imsg_type { IMSG_FILTER_SMTP_DATA_BEGIN, IMSG_FILTER_SMTP_DATA_END, - IMSG_CA_SIGN, + IMSG_CA_RSA_PRIVENC, + IMSG_CA_RSA_PRIVDEC, + IMSG_CA_ECDSA_SIGN, }; enum smtp_proc_type { @@ -1269,8 +1271,12 @@ void bounce_fd(int); /* ca.c */ -int ca(void); -int ca_sign(void *, const char *, const uint8_t *, size_t, int, uint8_t **, size_t *); +int ca(void); +int ca_X509_verify(void *, void *, const char *, const char *, const char **); +void ca_imsg(struct mproc *, struct imsg *); +void ca_init(void); +void ca_engine_init(void); + /* compress_backend.c */ struct compress_backend *compress_backend_lookup(const char *); diff --git a/usr.sbin/smtpd/ssl.c b/usr.sbin/smtpd/ssl.c index 65b259073d7..a7b8521dca3 100644 --- a/usr.sbin/smtpd/ssl.c +++ b/usr.sbin/smtpd/ssl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl.c,v 1.97 2022/02/12 18:22:04 eric Exp $ */ +/* $OpenBSD: ssl.c,v 1.98 2022/02/18 16:57:36 millert Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -243,3 +244,59 @@ ssl_error(const char *where) log_debug("debug: SSL library error: %s: %s", where, errbuf); } } + +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; +} diff --git a/usr.sbin/smtpd/ssl.h b/usr.sbin/smtpd/ssl.h index 1144038a653..912e6a25f4e 100644 --- a/usr.sbin/smtpd/ssl.h +++ b/usr.sbin/smtpd/ssl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl.h,v 1.25 2022/02/12 18:22:04 eric Exp $ */ +/* $OpenBSD: ssl.h,v 1.26 2022/02/18 16:57:36 millert Exp $ */ /* * Copyright (c) 2013 Gilles Chehade * @@ -44,3 +44,4 @@ void ssl_error(const char *); int ssl_load_certificate(struct pki *, const char *); int ssl_load_keyfile(struct pki *, const char *, const char *); int ssl_load_cafile(struct ca *, const char *); +char *ssl_pubkey_hash(const char *, off_t); -- 2.20.1