xref: /openbsd-src/usr.sbin/smtpd/ca.c (revision 16b0c81bb5f6edbf5168189a20f4f8992d1e7bb5)
1*16b0c81bSclaudio /*	$OpenBSD: ca.c,v 1.49 2024/11/21 13:22:21 claudio Exp $	*/
265c4fdfbSgilles 
365c4fdfbSgilles /*
4c4df3bf2Sreyk  * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
565c4fdfbSgilles  * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
665c4fdfbSgilles  *
765c4fdfbSgilles  * Permission to use, copy, modify, and distribute this software for any
865c4fdfbSgilles  * purpose with or without fee is hereby granted, provided that the above
965c4fdfbSgilles  * copyright notice and this permission notice appear in all copies.
1065c4fdfbSgilles  *
1165c4fdfbSgilles  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1265c4fdfbSgilles  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1365c4fdfbSgilles  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1465c4fdfbSgilles  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1565c4fdfbSgilles  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1665c4fdfbSgilles  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1765c4fdfbSgilles  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1865c4fdfbSgilles  */
1965c4fdfbSgilles 
20b6a48d3eStb #include <openssl/err.h>
2141b8cf0bSmillert #include <openssl/pem.h>
2205ec4924Sgilles #include <pwd.h>
2305ec4924Sgilles #include <signal.h>
2405ec4924Sgilles #include <string.h>
2505ec4924Sgilles #include <unistd.h>
2665c4fdfbSgilles 
27c4df3bf2Sreyk #include "smtpd.h"
283abbdc76Seric #include "log.h"
2941b8cf0bSmillert #include "ssl.h"
3039542f0aSeric 
3141b8cf0bSmillert static int	 rsae_send_imsg(int, const unsigned char *, unsigned char *,
3241b8cf0bSmillert 		    RSA *, int, unsigned int);
3341b8cf0bSmillert static int	 rsae_priv_enc(int, const unsigned char *, unsigned char *,
3441b8cf0bSmillert 		    RSA *, int);
3541b8cf0bSmillert static int	 rsae_priv_dec(int, const unsigned char *, unsigned char *,
3641b8cf0bSmillert 		    RSA *, int);
3741b8cf0bSmillert static ECDSA_SIG *ecdsae_do_sign(const unsigned char *, int, const BIGNUM *,
3841b8cf0bSmillert     const BIGNUM *, EC_KEY *);
3941b8cf0bSmillert 
4041b8cf0bSmillert static struct dict pkeys;
414b5cbcb9Sgilles static uint64_t	 reqid = 0;
42576d0b55Sreyk 
4376eb97c5Sreyk static void
4476eb97c5Sreyk ca_shutdown(void)
4576eb97c5Sreyk {
4643962b9cSeric 	log_debug("debug: ca agent exiting");
4776eb97c5Sreyk 	_exit(0);
4876eb97c5Sreyk }
4976eb97c5Sreyk 
50b88ab68dSeric int
5176eb97c5Sreyk ca(void)
5276eb97c5Sreyk {
5376eb97c5Sreyk 	struct passwd	*pw;
5476eb97c5Sreyk 
5541b8cf0bSmillert 	purge_config(PURGE_LISTENERS|PURGE_TABLES|PURGE_RULES|PURGE_DISPATCHERS);
5676eb97c5Sreyk 
5776eb97c5Sreyk 	if ((pw = getpwnam(SMTPD_USER)) == NULL)
5876eb97c5Sreyk 		fatalx("unknown user " SMTPD_USER);
5976eb97c5Sreyk 
6076eb97c5Sreyk 	if (chroot(PATH_CHROOT) == -1)
6176eb97c5Sreyk 		fatal("ca: chroot");
6276eb97c5Sreyk 	if (chdir("/") == -1)
6376eb97c5Sreyk 		fatal("ca: chdir(\"/\")");
6476eb97c5Sreyk 
6576eb97c5Sreyk 	config_process(PROC_CA);
6676eb97c5Sreyk 
6776eb97c5Sreyk 	if (setgroups(1, &pw->pw_gid) ||
6876eb97c5Sreyk 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
6976eb97c5Sreyk 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
7076eb97c5Sreyk 		fatal("ca: cannot drop privileges");
7176eb97c5Sreyk 
7276eb97c5Sreyk 	imsg_callback = ca_imsg;
7376eb97c5Sreyk 	event_init();
7476eb97c5Sreyk 
7543962b9cSeric 	signal(SIGINT, SIG_IGN);
7643962b9cSeric 	signal(SIGTERM, SIG_IGN);
7776eb97c5Sreyk 	signal(SIGPIPE, SIG_IGN);
7876eb97c5Sreyk 	signal(SIGHUP, SIG_IGN);
7976eb97c5Sreyk 
8057d312f7Seric 	config_peer(PROC_CONTROL);
8176eb97c5Sreyk 	config_peer(PROC_PARENT);
821a5b831aSmartijn 	config_peer(PROC_DISPATCHER);
8376eb97c5Sreyk 
8441b8cf0bSmillert 	/* Ignore them until we get our config */
8541b8cf0bSmillert 	mproc_disable(p_dispatcher);
8641b8cf0bSmillert 
8791ea8c26Sgilles 	if (pledge("stdio", NULL) == -1)
88ff01b044Seric 		fatal("pledge");
8991ea8c26Sgilles 
90f94528c3Seric 	event_dispatch();
91f94528c3Seric 	fatalx("exited event loop");
9276eb97c5Sreyk 
9376eb97c5Sreyk 	return (0);
9476eb97c5Sreyk }
9576eb97c5Sreyk 
9641b8cf0bSmillert void
97c4df3bf2Sreyk ca_init(void)
98c4df3bf2Sreyk {
9941b8cf0bSmillert 	BIO		*in = NULL;
10041b8cf0bSmillert 	EVP_PKEY	*pkey = NULL;
101c4df3bf2Sreyk 	struct pki	*pki;
10241b8cf0bSmillert 	const char	*k;
103c4df3bf2Sreyk 	void		*iter_dict;
10441b8cf0bSmillert 	char		*hash;
105c4df3bf2Sreyk 
10641b8cf0bSmillert 	log_debug("debug: init private ssl-tree");
10741b8cf0bSmillert 	dict_init(&pkeys);
108c4df3bf2Sreyk 	iter_dict = NULL;
10941b8cf0bSmillert 	while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) {
110c4df3bf2Sreyk 		if (pki->pki_key == NULL)
111c4df3bf2Sreyk 			continue;
11241b8cf0bSmillert 
11341b8cf0bSmillert 		in = BIO_new_mem_buf(pki->pki_key, pki->pki_key_len);
11441b8cf0bSmillert 		if (in == NULL)
11541b8cf0bSmillert 			fatalx("ca_init: key");
11641b8cf0bSmillert 		pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
11741b8cf0bSmillert 		if (pkey == NULL)
11841b8cf0bSmillert 			fatalx("ca_init: PEM");
11941b8cf0bSmillert 		BIO_free(in);
12041b8cf0bSmillert 
12141b8cf0bSmillert 		hash = ssl_pubkey_hash(pki->pki_cert, pki->pki_cert_len);
12241b8cf0bSmillert 		if (dict_check(&pkeys, hash))
12341b8cf0bSmillert 			EVP_PKEY_free(pkey);
12441b8cf0bSmillert 		else
12541b8cf0bSmillert 			dict_xset(&pkeys, hash, pkey);
12641b8cf0bSmillert 		free(hash);
127c4df3bf2Sreyk 	}
128c4df3bf2Sreyk }
12965c4fdfbSgilles 
13041b8cf0bSmillert int
13141b8cf0bSmillert ca_X509_verify(void *certificate, void *chain, const char *CAfile,
13241b8cf0bSmillert     const char *CRLfile, const char **errstr)
13341b8cf0bSmillert {
13441b8cf0bSmillert 	X509_STORE     *store = NULL;
13541b8cf0bSmillert 	X509_STORE_CTX *xsc = NULL;
13641b8cf0bSmillert 	int		ret = 0;
13741b8cf0bSmillert 	long		error = 0;
13841b8cf0bSmillert 
13941b8cf0bSmillert 	if ((store = X509_STORE_new()) == NULL)
14041b8cf0bSmillert 		goto end;
14141b8cf0bSmillert 
14241b8cf0bSmillert 	if (!X509_STORE_load_locations(store, CAfile, NULL)) {
14341b8cf0bSmillert 		log_warn("warn: unable to load CA file %s", CAfile);
14441b8cf0bSmillert 		goto end;
14541b8cf0bSmillert 	}
14641b8cf0bSmillert 	X509_STORE_set_default_paths(store);
14741b8cf0bSmillert 
14841b8cf0bSmillert 	if ((xsc = X509_STORE_CTX_new()) == NULL)
14941b8cf0bSmillert 		goto end;
15041b8cf0bSmillert 
15141b8cf0bSmillert 	if (X509_STORE_CTX_init(xsc, store, certificate, chain) != 1)
15241b8cf0bSmillert 		goto end;
15341b8cf0bSmillert 
15441b8cf0bSmillert 	ret = X509_verify_cert(xsc);
15541b8cf0bSmillert 
15641b8cf0bSmillert end:
15741b8cf0bSmillert 	*errstr = NULL;
15841b8cf0bSmillert 	if (ret != 1) {
15941b8cf0bSmillert 		if (xsc) {
16041b8cf0bSmillert 			error = X509_STORE_CTX_get_error(xsc);
16141b8cf0bSmillert 			*errstr = X509_verify_cert_error_string(error);
16241b8cf0bSmillert 		}
16341b8cf0bSmillert 		else if (ERR_peek_last_error())
16441b8cf0bSmillert 			*errstr = ERR_error_string(ERR_peek_last_error(), NULL);
16541b8cf0bSmillert 	}
16641b8cf0bSmillert 
16741b8cf0bSmillert 	X509_STORE_CTX_free(xsc);
16841b8cf0bSmillert 	X509_STORE_free(store);
16941b8cf0bSmillert 
17041b8cf0bSmillert 	return ret > 0 ? 1 : 0;
17141b8cf0bSmillert }
17241b8cf0bSmillert 
17341b8cf0bSmillert void
174c4df3bf2Sreyk ca_imsg(struct mproc *p, struct imsg *imsg)
175c4df3bf2Sreyk {
17641b8cf0bSmillert 	EVP_PKEY		*pkey;
17741b8cf0bSmillert 	RSA			*rsa = NULL;
17841b8cf0bSmillert 	EC_KEY			*ecdsa = NULL;
17941b8cf0bSmillert 	const void		*from = NULL;
18041b8cf0bSmillert 	unsigned char		*to = NULL;
181c4df3bf2Sreyk 	struct msg		 m;
182eed85469Seric 	const char		*hash;
18341b8cf0bSmillert 	size_t			 flen, tlen, padding;
18441b8cf0bSmillert 	int			 buf_len;
18541b8cf0bSmillert 	int			 ret = 0;
186576d0b55Sreyk 	uint64_t		 id;
18741b8cf0bSmillert 	int			 v;
188c4df3bf2Sreyk 
18943962b9cSeric 	if (imsg == NULL)
19043962b9cSeric 		ca_shutdown();
19143962b9cSeric 
19276eb97c5Sreyk 	switch (imsg->hdr.type) {
19376eb97c5Sreyk 	case IMSG_CONF_START:
19476eb97c5Sreyk 		return;
19576eb97c5Sreyk 	case IMSG_CONF_END:
19641b8cf0bSmillert 		ca_init();
19741b8cf0bSmillert 
19841b8cf0bSmillert 		/* Start fulfilling requests */
19941b8cf0bSmillert 		mproc_enable(p_dispatcher);
20076eb97c5Sreyk 		return;
20157d312f7Seric 
20276eb97c5Sreyk 	case IMSG_CTL_VERBOSE:
20376eb97c5Sreyk 		m_msg(&m, imsg);
20476eb97c5Sreyk 		m_get_int(&m, &v);
20576eb97c5Sreyk 		m_end(&m);
206f24248b7Sreyk 		log_trace_verbose(v);
20776eb97c5Sreyk 		return;
208ffdd47f9Seric 
20976eb97c5Sreyk 	case IMSG_CTL_PROFILE:
21076eb97c5Sreyk 		m_msg(&m, imsg);
21176eb97c5Sreyk 		m_get_int(&m, &v);
21276eb97c5Sreyk 		m_end(&m);
21376eb97c5Sreyk 		profiling = v;
21476eb97c5Sreyk 		return;
21576eb97c5Sreyk 
21641b8cf0bSmillert 	case IMSG_CA_RSA_PRIVENC:
21741b8cf0bSmillert 	case IMSG_CA_RSA_PRIVDEC:
218c4df3bf2Sreyk 		m_msg(&m, imsg);
219576d0b55Sreyk 		m_get_id(&m, &id);
220eed85469Seric 		m_get_string(&m, &hash);
22141b8cf0bSmillert 		m_get_data(&m, &from, &flen);
22241b8cf0bSmillert 		m_get_size(&m, &tlen);
22341b8cf0bSmillert 		m_get_size(&m, &padding);
224c4df3bf2Sreyk 		m_end(&m);
225c4df3bf2Sreyk 
22641b8cf0bSmillert 		pkey = dict_get(&pkeys, hash);
22741b8cf0bSmillert 		if (pkey == NULL || (rsa = EVP_PKEY_get1_RSA(pkey)) == NULL)
22841b8cf0bSmillert 			fatalx("ca_imsg: invalid pkey hash");
22941b8cf0bSmillert 
23041b8cf0bSmillert 		if ((to = calloc(1, tlen)) == NULL)
23141b8cf0bSmillert 			fatalx("ca_imsg: calloc");
23241b8cf0bSmillert 
23341b8cf0bSmillert 		switch (imsg->hdr.type) {
23441b8cf0bSmillert 		case IMSG_CA_RSA_PRIVENC:
23541b8cf0bSmillert 			ret = RSA_private_encrypt(flen, from, to, rsa,
23641b8cf0bSmillert 			    padding);
23741b8cf0bSmillert 			break;
23841b8cf0bSmillert 		case IMSG_CA_RSA_PRIVDEC:
23941b8cf0bSmillert 			ret = RSA_private_decrypt(flen, from, to, rsa,
24041b8cf0bSmillert 			    padding);
24141b8cf0bSmillert 			break;
24241b8cf0bSmillert 		}
243c4df3bf2Sreyk 
244c4df3bf2Sreyk 		m_create(p, imsg->hdr.type, 0, 0, -1);
245576d0b55Sreyk 		m_add_id(p, id);
246c4df3bf2Sreyk 		m_add_int(p, ret);
24741b8cf0bSmillert 		if (ret > 0)
24841b8cf0bSmillert 			m_add_data(p, to, (size_t)ret);
249c4df3bf2Sreyk 		m_close(p);
25041b8cf0bSmillert 
25141b8cf0bSmillert 		free(to);
25241b8cf0bSmillert 		RSA_free(rsa);
25341b8cf0bSmillert 		return;
25441b8cf0bSmillert 
25541b8cf0bSmillert 	case IMSG_CA_ECDSA_SIGN:
25641b8cf0bSmillert 		m_msg(&m, imsg);
25741b8cf0bSmillert 		m_get_id(&m, &id);
25841b8cf0bSmillert 		m_get_string(&m, &hash);
25941b8cf0bSmillert 		m_get_data(&m, &from, &flen);
26041b8cf0bSmillert 		m_end(&m);
26141b8cf0bSmillert 
26241b8cf0bSmillert 		pkey = dict_get(&pkeys, hash);
26341b8cf0bSmillert 		if (pkey == NULL ||
26441b8cf0bSmillert 		    (ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL)
26541b8cf0bSmillert 			fatalx("ca_imsg: invalid pkey hash");
26641b8cf0bSmillert 
26741b8cf0bSmillert 		buf_len = ECDSA_size(ecdsa);
26841b8cf0bSmillert 		if ((to = calloc(1, buf_len)) == NULL)
26941b8cf0bSmillert 			fatalx("ca_imsg: calloc");
27041b8cf0bSmillert 		ret = ECDSA_sign(0, from, flen, to, &buf_len, ecdsa);
27141b8cf0bSmillert 		m_create(p, imsg->hdr.type, 0, 0, -1);
27241b8cf0bSmillert 		m_add_id(p, id);
27341b8cf0bSmillert 		m_add_int(p, ret);
27441b8cf0bSmillert 		if (ret > 0)
27541b8cf0bSmillert 			m_add_data(p, to, (size_t)buf_len);
27641b8cf0bSmillert 		m_close(p);
27741b8cf0bSmillert 		free(to);
27841b8cf0bSmillert 		EC_KEY_free(ecdsa);
27976eb97c5Sreyk 		return;
28076eb97c5Sreyk 	}
28176eb97c5Sreyk 
282ff01b044Seric 	fatalx("ca_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
283c4df3bf2Sreyk }
284c4df3bf2Sreyk 
28541b8cf0bSmillert /*
28641b8cf0bSmillert  * RSA privsep engine (called from unprivileged processes)
28741b8cf0bSmillert  */
28841b8cf0bSmillert 
28941b8cf0bSmillert const RSA_METHOD *rsa_default = NULL;
29041b8cf0bSmillert 
29141b8cf0bSmillert static RSA_METHOD *rsae_method = NULL;
29241b8cf0bSmillert 
293c4df3bf2Sreyk static int
29441b8cf0bSmillert rsae_send_imsg(int flen, const unsigned char *from, unsigned char *to,
29541b8cf0bSmillert     RSA *rsa, int padding, unsigned int cmd)
296c4df3bf2Sreyk {
29741b8cf0bSmillert 	int		 ret = 0;
298c4df3bf2Sreyk 	struct imsgbuf	*ibuf;
299c4df3bf2Sreyk 	struct imsg	 imsg;
30041b8cf0bSmillert 	int		 n, done = 0;
30141b8cf0bSmillert 	const void	*toptr;
30241b8cf0bSmillert 	char		*hash;
30341b8cf0bSmillert 	size_t		 tlen;
304c4df3bf2Sreyk 	struct msg	 m;
305576d0b55Sreyk 	uint64_t	 id;
30641b8cf0bSmillert 
30741b8cf0bSmillert 	if ((hash = RSA_get_ex_data(rsa, 0)) == NULL)
30841b8cf0bSmillert 		return (0);
30941b8cf0bSmillert 
31041b8cf0bSmillert 	/*
31141b8cf0bSmillert 	 * Send a synchronous imsg because we cannot defer the RSA
31241b8cf0bSmillert 	 * operation in OpenSSL's engine layer.
31341b8cf0bSmillert 	 */
31441b8cf0bSmillert 	m_create(p_ca, cmd, 0, 0, -1);
31541b8cf0bSmillert 	reqid++;
31641b8cf0bSmillert 	m_add_id(p_ca, reqid);
31741b8cf0bSmillert 	m_add_string(p_ca, hash);
31841b8cf0bSmillert 	m_add_data(p_ca, (const void *)from, (size_t)flen);
31941b8cf0bSmillert 	m_add_size(p_ca, (size_t)RSA_size(rsa));
32041b8cf0bSmillert 	m_add_size(p_ca, (size_t)padding);
32141b8cf0bSmillert 	m_flush(p_ca);
322c4df3bf2Sreyk 
32376eb97c5Sreyk 	ibuf = &p_ca->imsgbuf;
324c4df3bf2Sreyk 
325c4df3bf2Sreyk 	while (!done) {
326*16b0c81bSclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
327dd7efffeSclaudio 			fatalx("imsgbuf_read");
328c4df3bf2Sreyk 		if (n == 0)
329c4df3bf2Sreyk 			fatalx("pipe closed");
330c4df3bf2Sreyk 
331c4df3bf2Sreyk 		while (!done) {
332c4df3bf2Sreyk 			if ((n = imsg_get(ibuf, &imsg)) == -1)
333c4df3bf2Sreyk 				fatalx("imsg_get error");
334c4df3bf2Sreyk 			if (n == 0)
335c4df3bf2Sreyk 				break;
336576d0b55Sreyk 
3371a5b831aSmartijn 			log_imsg(PROC_DISPATCHER, PROC_CA, &imsg);
338576d0b55Sreyk 
33941b8cf0bSmillert 			switch (imsg.hdr.type) {
34041b8cf0bSmillert 			case IMSG_CA_RSA_PRIVENC:
34141b8cf0bSmillert 			case IMSG_CA_RSA_PRIVDEC:
34241b8cf0bSmillert 				break;
34341b8cf0bSmillert 			default:
344576d0b55Sreyk 				/* Another imsg is queued up in the buffer */
3451a5b831aSmartijn 				dispatcher_imsg(p_ca, &imsg);
346576d0b55Sreyk 				imsg_free(&imsg);
347576d0b55Sreyk 				continue;
348576d0b55Sreyk 			}
349c4df3bf2Sreyk 
350c4df3bf2Sreyk 			m_msg(&m, &imsg);
351576d0b55Sreyk 			m_get_id(&m, &id);
3524b5cbcb9Sgilles 			if (id != reqid)
353576d0b55Sreyk 				fatalx("invalid response id");
354c4df3bf2Sreyk 			m_get_int(&m, &ret);
35541b8cf0bSmillert 			if (ret > 0)
35641b8cf0bSmillert 				m_get_data(&m, &toptr, &tlen);
357c4df3bf2Sreyk 			m_end(&m);
3583abbdc76Seric 
35941b8cf0bSmillert 			if (ret > 0)
36041b8cf0bSmillert 				memcpy(to, toptr, tlen);
36141b8cf0bSmillert 			done = 1;
36241b8cf0bSmillert 
36341b8cf0bSmillert 			imsg_free(&imsg);
36441b8cf0bSmillert 		}
36541b8cf0bSmillert 	}
36676eb97c5Sreyk 	mproc_event_add(p_ca);
367c4df3bf2Sreyk 
368c4df3bf2Sreyk 	return (ret);
369c4df3bf2Sreyk }
370c4df3bf2Sreyk 
37141b8cf0bSmillert static int
37241b8cf0bSmillert rsae_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,
37341b8cf0bSmillert     int padding)
37441b8cf0bSmillert {
37541b8cf0bSmillert 	log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
37641b8cf0bSmillert 	if (RSA_get_ex_data(rsa, 0) != NULL)
37741b8cf0bSmillert 		return (rsae_send_imsg(flen, from, to, rsa, padding,
37841b8cf0bSmillert 		    IMSG_CA_RSA_PRIVENC));
37941b8cf0bSmillert 	return (RSA_meth_get_priv_enc(rsa_default)(flen, from, to, rsa, padding));
38041b8cf0bSmillert }
38141b8cf0bSmillert 
38241b8cf0bSmillert static int
38341b8cf0bSmillert rsae_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,
38441b8cf0bSmillert     int padding)
38541b8cf0bSmillert {
38641b8cf0bSmillert 	log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
38741b8cf0bSmillert 	if (RSA_get_ex_data(rsa, 0) != NULL)
38841b8cf0bSmillert 		return (rsae_send_imsg(flen, from, to, rsa, padding,
38941b8cf0bSmillert 		    IMSG_CA_RSA_PRIVDEC));
39041b8cf0bSmillert 
39141b8cf0bSmillert 	return (RSA_meth_get_priv_dec(rsa_default)(flen, from, to, rsa, padding));
39241b8cf0bSmillert }
39341b8cf0bSmillert 
39441b8cf0bSmillert /*
39541b8cf0bSmillert  * ECDSA privsep engine (called from unprivileged processes)
39641b8cf0bSmillert  */
39741b8cf0bSmillert 
3988615c063Sop const EC_KEY_METHOD *ecdsa_default = NULL;
39941b8cf0bSmillert 
4008615c063Sop static EC_KEY_METHOD *ecdsae_method = NULL;
40141b8cf0bSmillert 
40241b8cf0bSmillert static ECDSA_SIG *
40341b8cf0bSmillert ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
40441b8cf0bSmillert     const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey)
40541b8cf0bSmillert {
40641b8cf0bSmillert 	int		 ret = 0;
40741b8cf0bSmillert 	struct imsgbuf	*ibuf;
40841b8cf0bSmillert 	struct imsg	 imsg;
40941b8cf0bSmillert 	int		 n, done = 0;
41041b8cf0bSmillert 	const void	*toptr;
41141b8cf0bSmillert 	char		*hash;
41241b8cf0bSmillert 	size_t		 tlen;
41341b8cf0bSmillert 	struct msg	 m;
41441b8cf0bSmillert 	uint64_t	 id;
41541b8cf0bSmillert 	ECDSA_SIG	*sig = NULL;
41641b8cf0bSmillert 
4178615c063Sop 	if ((hash = EC_KEY_get_ex_data(eckey, 0)) == NULL)
41841b8cf0bSmillert 		return (0);
41941b8cf0bSmillert 
42041b8cf0bSmillert 	/*
42141b8cf0bSmillert 	 * Send a synchronous imsg because we cannot defer the ECDSA
42241b8cf0bSmillert 	 * operation in OpenSSL's engine layer.
42341b8cf0bSmillert 	 */
42441b8cf0bSmillert 	m_create(p_ca, IMSG_CA_ECDSA_SIGN, 0, 0, -1);
42541b8cf0bSmillert 	reqid++;
42641b8cf0bSmillert 	m_add_id(p_ca, reqid);
427eed85469Seric 	m_add_string(p_ca, hash);
42841b8cf0bSmillert 	m_add_data(p_ca, (const void *)dgst, (size_t)dgst_len);
4294b5cbcb9Sgilles 	m_flush(p_ca);
4304b5cbcb9Sgilles 
43141b8cf0bSmillert 	ibuf = &p_ca->imsgbuf;
43241b8cf0bSmillert 
43341b8cf0bSmillert 	while (!done) {
434*16b0c81bSclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
435dd7efffeSclaudio 			fatalx("imsgbuf_read");
43641b8cf0bSmillert 		if (n == 0)
43741b8cf0bSmillert 			fatalx("pipe closed");
43841b8cf0bSmillert 		while (!done) {
43941b8cf0bSmillert 			if ((n = imsg_get(ibuf, &imsg)) == -1)
44041b8cf0bSmillert 				fatalx("imsg_get error");
44141b8cf0bSmillert 			if (n == 0)
44241b8cf0bSmillert 				break;
44341b8cf0bSmillert 
44441b8cf0bSmillert 			log_imsg(PROC_DISPATCHER, PROC_CA, &imsg);
44541b8cf0bSmillert 
44641b8cf0bSmillert 			switch (imsg.hdr.type) {
44741b8cf0bSmillert 			case IMSG_CA_ECDSA_SIGN:
44841b8cf0bSmillert 				break;
44941b8cf0bSmillert 			default:
45041b8cf0bSmillert 				/* Another imsg is queued up in the buffer */
45141b8cf0bSmillert 				dispatcher_imsg(p_ca, &imsg);
45241b8cf0bSmillert 				imsg_free(&imsg);
45341b8cf0bSmillert 				continue;
45441b8cf0bSmillert 			}
45541b8cf0bSmillert 
45641b8cf0bSmillert 			m_msg(&m, &imsg);
45741b8cf0bSmillert 			m_get_id(&m, &id);
45841b8cf0bSmillert 			if (id != reqid)
45941b8cf0bSmillert 				fatalx("invalid response id");
46041b8cf0bSmillert 			m_get_int(&m, &ret);
46141b8cf0bSmillert 			if (ret > 0)
46241b8cf0bSmillert 				m_get_data(&m, &toptr, &tlen);
46341b8cf0bSmillert 			m_end(&m);
46441b8cf0bSmillert 			done = 1;
46541b8cf0bSmillert 
46641b8cf0bSmillert 			if (ret > 0)
46741b8cf0bSmillert 				d2i_ECDSA_SIG(&sig, (const unsigned char **)&toptr, tlen);
46841b8cf0bSmillert 			imsg_free(&imsg);
46941b8cf0bSmillert 		}
47041b8cf0bSmillert 	}
47141b8cf0bSmillert 	mproc_event_add(p_ca);
47241b8cf0bSmillert 
47341b8cf0bSmillert 	return (sig);
47441b8cf0bSmillert }
47541b8cf0bSmillert 
4768615c063Sop static ECDSA_SIG *
4778615c063Sop ecdsae_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
4788615c063Sop     const BIGNUM *rp, EC_KEY *eckey)
4798615c063Sop {
4808615c063Sop 	ECDSA_SIG *(*psign_sig)(const unsigned char *, int, const BIGNUM *,
4818615c063Sop 	    const BIGNUM *, EC_KEY *);
4828615c063Sop 
4838615c063Sop 	log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
4848615c063Sop 	if (EC_KEY_get_ex_data(eckey, 0) != NULL)
48541b8cf0bSmillert 		return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey));
4868615c063Sop 	EC_KEY_METHOD_get_sign(ecdsa_default, NULL, NULL, &psign_sig);
4878615c063Sop 	return (psign_sig(dgst, dgst_len, inv, rp, eckey));
48841b8cf0bSmillert }
48941b8cf0bSmillert 
49041b8cf0bSmillert static void
49141b8cf0bSmillert rsa_engine_init(void)
49241b8cf0bSmillert {
493ed3431f4Sop 	const char	*errstr;
49441b8cf0bSmillert 
49541b8cf0bSmillert 	if ((rsa_default = RSA_get_default_method()) == NULL) {
49641b8cf0bSmillert 		errstr = "RSA_get_default_method";
49741b8cf0bSmillert 		goto fail;
49841b8cf0bSmillert 	}
49941b8cf0bSmillert 
5003f1e3a05Sop 	if ((rsae_method = RSA_meth_dup(rsa_default)) == NULL) {
5013f1e3a05Sop 		errstr = "RSA_meth_dup";
5023f1e3a05Sop 		goto fail;
5033f1e3a05Sop 	}
5043f1e3a05Sop 
5053f1e3a05Sop 	RSA_meth_set_priv_enc(rsae_method, rsae_priv_enc);
5063f1e3a05Sop 	RSA_meth_set_priv_dec(rsae_method, rsae_priv_dec);
5073f1e3a05Sop 
50841b8cf0bSmillert 	RSA_meth_set_flags(rsae_method,
50941b8cf0bSmillert 		RSA_meth_get_flags(rsa_default) | RSA_METHOD_FLAG_NO_CHECK);
51041b8cf0bSmillert 	RSA_meth_set0_app_data(rsae_method,
51141b8cf0bSmillert 		RSA_meth_get0_app_data(rsa_default));
51241b8cf0bSmillert 
513ed3431f4Sop 	RSA_set_default_method(rsae_method);
51441b8cf0bSmillert 
51541b8cf0bSmillert 	return;
51641b8cf0bSmillert 
51741b8cf0bSmillert  fail:
51841b8cf0bSmillert 	ssl_error(errstr);
51941b8cf0bSmillert 	fatalx("%s", errstr);
52041b8cf0bSmillert }
52141b8cf0bSmillert 
52241b8cf0bSmillert static void
52341b8cf0bSmillert ecdsa_engine_init(void)
52441b8cf0bSmillert {
5253f1e3a05Sop 	int (*sign)(int, const unsigned char *, int, unsigned char *,
5263f1e3a05Sop 	    unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *);
5273f1e3a05Sop 	int (*sign_setup)(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **);
528ed3431f4Sop 	const char *errstr;
52941b8cf0bSmillert 
5308615c063Sop 	if ((ecdsa_default = EC_KEY_get_default_method()) == NULL) {
5318615c063Sop 		errstr = "EC_KEY_get_default_method";
53241b8cf0bSmillert 		goto fail;
53341b8cf0bSmillert 	}
53441b8cf0bSmillert 
5353f1e3a05Sop 	if ((ecdsae_method = EC_KEY_METHOD_new(ecdsa_default)) == NULL) {
5363f1e3a05Sop 		errstr = "EC_KEY_METHOD_new";
5373f1e3a05Sop 		goto fail;
5383f1e3a05Sop 	}
5393f1e3a05Sop 
5403f1e3a05Sop 	EC_KEY_METHOD_get_sign(ecdsa_default, &sign, &sign_setup, NULL);
5413f1e3a05Sop 	EC_KEY_METHOD_set_sign(ecdsae_method, sign, sign_setup,
5423f1e3a05Sop 	    ecdsae_do_sign);
5433f1e3a05Sop 
544ed3431f4Sop 	EC_KEY_set_default_method(ecdsae_method);
54541b8cf0bSmillert 
54641b8cf0bSmillert 	return;
54741b8cf0bSmillert 
54841b8cf0bSmillert  fail:
54941b8cf0bSmillert 	ssl_error(errstr);
55041b8cf0bSmillert 	fatalx("%s", errstr);
55141b8cf0bSmillert }
55241b8cf0bSmillert 
55341b8cf0bSmillert void
55441b8cf0bSmillert ca_engine_init(void)
55541b8cf0bSmillert {
55641b8cf0bSmillert 	rsa_engine_init();
55741b8cf0bSmillert 	ecdsa_engine_init();
5584b5cbcb9Sgilles }
559