From 00ae31043b18ac22f35083877482acea3baf4e99 Mon Sep 17 00:00:00 2001 From: reyk Date: Tue, 22 Apr 2014 08:04:23 +0000 Subject: [PATCH] Support the CA key for SSL inspection in the ca process. Instead of looking up the keys by relay id, add all keys to a list and look them up by key id. ok benno@ --- usr.sbin/relayd/ca.c | 46 +++++++++++++++------- usr.sbin/relayd/config.c | 19 +++++++-- usr.sbin/relayd/parse.y | 26 +++++++++++-- usr.sbin/relayd/relay.c | 20 +++++++--- usr.sbin/relayd/relayd.c | 40 ++++++++++++++++++- usr.sbin/relayd/relayd.h | 23 +++++++++-- usr.sbin/relayd/ssl.c | 84 ++++++++++++++++++++-------------------- 7 files changed, 185 insertions(+), 73 deletions(-) diff --git a/usr.sbin/relayd/ca.c b/usr.sbin/relayd/ca.c index 48a7c4de312..b478d843e4d 100644 --- a/usr.sbin/relayd/ca.c +++ b/usr.sbin/relayd/ca.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ca.c,v 1.6 2014/04/21 17:22:06 reyk Exp $ */ +/* $OpenBSD: ca.c,v 1.7 2014/04/22 08:04:23 reyk Exp $ */ /* * Copyright (c) 2014 Reyk Floeter @@ -109,6 +109,10 @@ ca_launch(void) rlay->rl_ssl_pkey = pkey; + if (pkey_add(env, pkey, + rlay->rl_conf.ssl_keyid) == NULL) + fatalx("ssl pkey"); + purge_key(&rlay->rl_ssl_key, rlay->rl_conf.ssl_key_len); } @@ -116,6 +120,29 @@ ca_launch(void) purge_key(&rlay->rl_ssl_cert, rlay->rl_conf.ssl_cert_len); } + if (rlay->rl_conf.ssl_cakey_len) { + if ((in = BIO_new_mem_buf(rlay->rl_ssl_cakey, + rlay->rl_conf.ssl_cakey_len)) == NULL) + fatalx("ca_launch: key"); + + if ((pkey = PEM_read_bio_PrivateKey(in, + NULL, NULL, NULL)) == NULL) + fatalx("ca_launch: PEM"); + BIO_free(in); + + rlay->rl_ssl_capkey = pkey; + + if (pkey_add(env, pkey, + rlay->rl_conf.ssl_cakeyid) == NULL) + fatalx("ca pkey"); + + purge_key(&rlay->rl_ssl_cakey, + rlay->rl_conf.ssl_cakey_len); + } + if (rlay->rl_conf.ssl_cacert_len) { + purge_key(&rlay->rl_ssl_cacert, + rlay->rl_conf.ssl_cacert_len); + } } } @@ -142,17 +169,6 @@ ca_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) return (0); } -static EVP_PKEY * -ca_get_key(objid_t id) -{ - struct relay *rlay; - - if ((rlay = relay_find(env, id)) == NULL) - return (NULL); - - return (rlay->rl_ssl_pkey); -} - int ca_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg) { @@ -174,11 +190,13 @@ ca_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg) if (IMSG_DATA_SIZE(imsg) != (sizeof(cko) + cko.cko_flen)) fatalx("ca_dispatch_relay: " "invalid key operation"); - if ((pkey = ca_get_key(cko.cko_id)) == NULL || + if ((pkey = pkey_find(env, cko.cko_id)) == NULL || (rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) fatalx("ca_dispatch_relay: " "invalid relay key or id"); + DPRINTF("%s:%d: key id %d", __func__, __LINE__, cko.cko_id); + from = (u_char *)imsg->data + sizeof(cko); if ((to = calloc(1, cko.cko_tlen)) == NULL) fatalx("ca_dispatch_relay: calloc"); @@ -243,7 +261,7 @@ rsae_send_imsg(int flen, const u_char *from, u_char *to, RSA *rsa, { struct ctl_keyop cko; int ret = 0; - u_int32_t *id; + objid_t *id; struct iovec iov[2]; struct imsgbuf *ibuf; struct imsgev *iev; diff --git a/usr.sbin/relayd/config.c b/usr.sbin/relayd/config.c index c9e45ecb279..609317a5680 100644 --- a/usr.sbin/relayd/config.c +++ b/usr.sbin/relayd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.12 2014/04/18 13:55:26 reyk Exp $ */ +/* $OpenBSD: config.c,v 1.13 2014/04/22 08:04:23 reyk Exp $ */ /* * Copyright (c) 2011 - 2014 Reyk Floeter @@ -96,6 +96,10 @@ config_init(struct relayd *env) calloc(1, sizeof(*env->sc_relays))) == NULL) return (-1); TAILQ_INIT(env->sc_relays); + if ((env->sc_pkeys = + calloc(1, sizeof(*env->sc_pkeys))) == NULL) + return (-1); + TAILQ_INIT(env->sc_pkeys); } if (what & CONFIG_PROTOS) { if ((env->sc_protos = @@ -147,6 +151,7 @@ config_purge(struct relayd *env, u_int reset) struct relay *rlay; struct netroute *nr; struct router *rt; + struct ca_pkey *pkey; u_int what; what = ps->ps_what[privsep_process] & reset; @@ -167,6 +172,12 @@ config_purge(struct relayd *env, u_int reset) } env->sc_rdrcount = 0; } + if (what & CONFIG_RELAYS && env->sc_pkeys != NULL) { + while ((pkey = TAILQ_FIRST(env->sc_pkeys)) != NULL) { + TAILQ_REMOVE(env->sc_pkeys, pkey, pkey_entry); + free(pkey); + } + } if (what & CONFIG_RELAYS && env->sc_relays != NULL) { while ((rlay = TAILQ_FIRST(env->sc_relays)) != NULL) purge_relay(env, rlay); @@ -853,10 +864,12 @@ config_setrelay(struct relayd *env, struct relay *rlay) iov[c].iov_base = rlay->rl_ssl_cacert; iov[c++].iov_len = rl.ssl_cacert_len; } - if (rl.ssl_cakey_len) { + if ((what & CONFIG_CA_ENGINE) == 0 && + rl.ssl_cakey_len) { iov[c].iov_base = rlay->rl_ssl_cakey; iov[c++].iov_len = rl.ssl_cakey_len; - } + } else + rl.ssl_cakey_len = 0; if (id == PROC_RELAY) { /* XXX imsg code will close the fd after 1st call */ diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index 5853d7e5474..f085bddf3d7 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.179 2014/04/21 17:33:31 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.180 2014/04/22 08:04:23 reyk Exp $ */ /* * Copyright (c) 2007-2011 Reyk Floeter @@ -99,6 +99,7 @@ objid_t last_relay_id = 0; objid_t last_proto_id = 0; objid_t last_rt_id = 0; objid_t last_nr_id = 0; +objid_t last_key_id = 0; static struct rdr *rdr = NULL; static struct table *table = NULL; @@ -124,6 +125,7 @@ int host(const char *, struct addresslist *, void host_free(struct addresslist *); struct table *table_inherit(struct table *); +int relay_id(struct relay *); struct relay *relay_inherit(struct relay *, struct relay *); int getservice(char *); int is_if_in_group(const char *, const char *); @@ -1323,7 +1325,11 @@ relay : RELAY STRING { YYERROR; } free($2); - r->rl_conf.id = ++last_relay_id; + if (relay_id(r) == -1) { + yyerror("too many relays defined"); + free(r); + YYERROR; + } r->rl_conf.timeout.tv_sec = RELAY_TIMEOUT; r->rl_proto = NULL; r->rl_conf.proto = EMPTY_ID; @@ -2848,6 +2854,19 @@ table_inherit(struct table *tb) return (NULL); } +int +relay_id(struct relay *rl) +{ + rl->rl_conf.id = ++last_relay_id; + rl->rl_conf.ssl_keyid = ++last_key_id; + rl->rl_conf.ssl_cakeyid = ++last_key_id; + + if (last_relay_id == INT_MAX || last_key_id == INT_MAX) + return (-1); + + return (0); +} + struct relay * relay_inherit(struct relay *ra, struct relay *rb) { @@ -2869,8 +2888,7 @@ relay_inherit(struct relay *ra, struct relay *rb) } TAILQ_INIT(&rb->rl_tables); - rb->rl_conf.id = ++last_relay_id; - if (last_relay_id == INT_MAX) { + if (relay_id(rb) == -1) { yyerror("too many relays defined"); goto err; } diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index ad7b7f86b65..d6d4cf6e43b 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay.c,v 1.168 2014/04/18 13:55:26 reyk Exp $ */ +/* $OpenBSD: relay.c,v 1.169 2014/04/22 08:04:23 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -1914,13 +1914,23 @@ relay_ssl_ctx_create(struct relay *rlay) log_debug("%s: loading private key", __func__); if (!ssl_ctx_fake_private_key(ctx, - &rlay->rl_conf.id, rlay->rl_ssl_cert, rlay->rl_conf.ssl_cert_len, + &rlay->rl_conf.ssl_keyid, + rlay->rl_ssl_cert, rlay->rl_conf.ssl_cert_len, &rlay->rl_ssl_x509, &rlay->rl_ssl_pkey)) goto err; if (!SSL_CTX_check_private_key(ctx)) goto err; + if (rlay->rl_conf.ssl_cacert_len) { + log_debug("%s: loading CA private key", __func__); + if (!ssl_ctx_load_pkey(ctx, + &rlay->rl_conf.ssl_cakeyid, rlay->rl_ssl_cacert, + rlay->rl_conf.ssl_cacert_len, + &rlay->rl_ssl_cacertx509, &rlay->rl_ssl_capkey)) + goto err; + } + /* Set session context to the local relay name */ if (!SSL_CTX_set_session_id_context(ctx, rlay->rl_conf.name, strlen(rlay->rl_conf.name))) @@ -1928,6 +1938,7 @@ relay_ssl_ctx_create(struct relay *rlay) /* The text versions of the keys/certs are not needed anymore */ purge_key(&rlay->rl_ssl_cert, rlay->rl_conf.ssl_cert_len); + purge_key(&rlay->rl_ssl_cacert, rlay->rl_conf.ssl_cacert_len); return (ctx); @@ -2101,9 +2112,8 @@ relay_ssl_connect(int fd, short event, void *arg) SSL_get_peer_certificate(con->se_out.ssl)) != NULL) { con->se_in.sslcert = ssl_update_certificate(servercert, - rlay->rl_ssl_pkey, - rlay->rl_ssl_cakey, rlay->rl_conf.ssl_cakey_len, - rlay->rl_ssl_cacert, rlay->rl_conf.ssl_cacert_len); + rlay->rl_ssl_pkey, rlay->rl_ssl_capkey, + rlay->rl_ssl_cacertx509); } else con->se_in.sslcert = NULL; if (servercert != NULL) diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index afabd002224..2e8cc86051d 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.121 2014/04/20 14:48:29 reyk Exp $ */ +/* $OpenBSD: relayd.c,v 1.122 2014/04/22 08:04:23 reyk Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter @@ -639,6 +639,14 @@ purge_relay(struct relayd *env, struct relay *rlay) EVP_PKEY_free(rlay->rl_ssl_pkey); rlay->rl_ssl_pkey = NULL; } + if (rlay->rl_ssl_cacertx509 != NULL) { + X509_free(rlay->rl_ssl_cacertx509); + rlay->rl_ssl_cacertx509 = NULL; + } + if (rlay->rl_ssl_capkey != NULL) { + EVP_PKEY_free(rlay->rl_ssl_capkey); + rlay->rl_ssl_capkey = NULL; + } if (rlay->rl_ssl_ctx != NULL) SSL_CTX_free(rlay->rl_ssl_ctx); @@ -833,6 +841,36 @@ relay_findbyaddr(struct relayd *env, struct relay_config *rc) return (NULL); } +EVP_PKEY * +pkey_find(struct relayd *env, objid_t id) +{ + struct ca_pkey *pkey; + + TAILQ_FOREACH(pkey, env->sc_pkeys, pkey_entry) + if (pkey->pkey_id == id) + return (pkey->pkey); + return (NULL); +} + +struct ca_pkey * +pkey_add(struct relayd *env, EVP_PKEY *pkey, objid_t id) +{ + struct ca_pkey *ca_pkey; + + if (env->sc_pkeys == NULL) + fatalx("pkeys"); + + if ((ca_pkey = calloc(1, sizeof(*ca_pkey))) == NULL) + return (NULL); + + ca_pkey->pkey = pkey; + ca_pkey->pkey_id = id; + + TAILQ_INSERT_TAIL(env->sc_pkeys, ca_pkey, pkey_entry); + + return (ca_pkey); +} + void event_again(struct event *ev, int fd, short event, void (*fn)(int, short, void *), diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index 559b167deab..23ddafdbee5 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.176 2014/04/20 14:48:29 reyk Exp $ */ +/* $OpenBSD: relayd.h,v 1.177 2014/04/22 08:04:23 reyk Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -627,6 +627,13 @@ struct relay_table { }; TAILQ_HEAD(relaytables, relay_table); +struct ca_pkey { + objid_t pkey_id; + EVP_PKEY *pkey; + TAILQ_ENTRY(ca_pkey) pkey_entry; +}; +TAILQ_HEAD(ca_pkeylist, ca_pkey); + struct relay_config { objid_t id; u_int32_t flags; @@ -643,9 +650,11 @@ struct relay_config { enum forwardmode fwdmode; off_t ssl_cert_len; off_t ssl_key_len; + objid_t ssl_keyid; off_t ssl_ca_len; off_t ssl_cacert_len; off_t ssl_cakey_len; + objid_t ssl_cakeyid; }; struct relay { @@ -674,8 +683,12 @@ struct relay { EVP_PKEY *rl_ssl_pkey; char *rl_ssl_ca; + char *rl_ssl_cacert; + X509 *rl_ssl_cacertx509; + char *rl_ssl_cakey; + EVP_PKEY *rl_ssl_capkey; struct ctl_stats rl_stats[RELAY_MAXPROC + 1]; @@ -929,6 +942,7 @@ struct relayd { struct relaylist *sc_relays; struct routerlist *sc_rts; struct netroutelist *sc_routes; + struct ca_pkeylist *sc_pkeys; u_int16_t sc_prefork_relay; char sc_demote_group[IFNAMSIZ]; u_int16_t sc_id; @@ -1092,8 +1106,9 @@ void ssl_transaction(struct ctl_tcp_event *); SSL_CTX *ssl_ctx_create(struct relayd *); void ssl_error(const char *, const char *); char *ssl_load_key(struct relayd *, const char *, off_t *, char *); -X509 *ssl_update_certificate(X509 *, EVP_PKEY *, - char *, off_t, char *, off_t); +X509 *ssl_update_certificate(X509 *, EVP_PKEY *, EVP_PKEY *, X509 *); +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, X509 **, EVP_PKEY **); @@ -1123,6 +1138,8 @@ struct protocol *proto_find(struct relayd *, objid_t); struct rsession *session_find(struct relayd *, objid_t); struct relay *relay_findbyname(struct relayd *, const char *); struct relay *relay_findbyaddr(struct relayd *, struct relay_config *); +EVP_PKEY *pkey_find(struct relayd *, objid_t); +struct ca_pkey *pkey_add(struct relayd *, EVP_PKEY *, objid_t); int expand_string(char *, size_t, const char *, const char *); void translate_string(char *); void purge_key(char **, off_t); diff --git a/usr.sbin/relayd/ssl.c b/usr.sbin/relayd/ssl.c index 35c78b0947b..ffe43d378da 100644 --- a/usr.sbin/relayd/ssl.c +++ b/usr.sbin/relayd/ssl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl.c,v 1.21 2014/04/21 17:22:06 reyk Exp $ */ +/* $OpenBSD: ssl.c,v 1.22 2014/04/22 08:04:23 reyk Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter @@ -359,13 +359,11 @@ ssl_load_key(struct relayd *env, const char *name, off_t *len, char *pass) } X509 * -ssl_update_certificate(X509 *oldcert, EVP_PKEY *pkey, - char *cakeystr, off_t cakeylen, char *cacertstr, off_t cacertlen) +ssl_update_certificate(X509 *oldcert, EVP_PKEY *pkey, EVP_PKEY *capkey, + X509 *cacert) { char name[2][SSL_NAME_SIZE]; - X509 *cert = NULL, *cacert = NULL; - EVP_PKEY *cakey = NULL; - BIO *bio = NULL; + X509 *cert = NULL; name[0][0] = name[1][0] = '\0'; if (!X509_NAME_oneline(X509_get_subject_name(oldcert), @@ -374,22 +372,6 @@ ssl_update_certificate(X509 *oldcert, EVP_PKEY *pkey, name[1], sizeof(name[1]))) goto done; - /* Get CA key */ - BIO_free_all(bio); - if ((bio = BIO_new_mem_buf(cakeystr, cakeylen)) == NULL) - goto done; - if ((cakey = PEM_read_bio_PrivateKey(bio, &cakey, - ssl_password_cb, NULL)) == NULL) - goto done; - - /* Get CA certificate */ - BIO_free_all(bio); - if ((bio = BIO_new_mem_buf(cacertstr, cacertlen)) == NULL) - goto done; - if ((cacert = PEM_read_bio_X509(bio, &cacert, - ssl_password_cb, NULL)) == NULL) - goto done; - if ((cert = X509_dup(oldcert)) == NULL) goto done; @@ -398,7 +380,7 @@ ssl_update_certificate(X509 *oldcert, EVP_PKEY *pkey, X509_set_issuer_name(cert, X509_get_subject_name(cacert)); /* Sign with our CA */ - if (!X509_sign(cert, cakey, EVP_sha1())) { + if (!X509_sign(cert, capkey, EVP_sha1())) { X509_free(cert); cert = NULL; } @@ -414,18 +396,12 @@ ssl_update_certificate(X509 *oldcert, EVP_PKEY *pkey, done: if (cert == NULL) ssl_error(__func__, name[0]); - if (bio != NULL) - BIO_free_all(bio); - if (cacert != NULL) - X509_free(cacert); - if (cakey != NULL) - EVP_PKEY_free(cakey); return (cert); } int -ssl_ctx_fake_private_key(SSL_CTX *ctx, void *data, char *buf, off_t len, +ssl_ctx_load_pkey(SSL_CTX *ctx, void *data, char *buf, off_t len, X509 **x509ptr, EVP_PKEY **pkeyptr) { int ret = 0; @@ -440,8 +416,7 @@ ssl_ctx_fake_private_key(SSL_CTX *ctx, void *data, char *buf, off_t len, } if ((x509 = PEM_read_bio_X509(in, NULL, - ctx->default_passwd_callback, - ctx->default_passwd_callback_userdata)) == NULL) { + ssl_password_cb, NULL)) == NULL) { SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_PEM_LIB); goto fail; } @@ -458,17 +433,6 @@ ssl_ctx_fake_private_key(SSL_CTX *ctx, void *data, char *buf, off_t len, RSA_set_ex_data(rsa, 0, data); - /* - * 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. - */ - if (!SSL_CTX_use_PrivateKey(ctx, pkey)) { - SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_SSL_LIB); - goto fail; - } - *x509ptr = x509; *pkeyptr = pkey; ret = 1; @@ -487,3 +451,37 @@ ssl_ctx_fake_private_key(SSL_CTX *ctx, void *data, char *buf, off_t len, return ret; } + +int +ssl_ctx_fake_private_key(SSL_CTX *ctx, void *data, char *buf, off_t len, + X509 **x509ptr, EVP_PKEY **pkeyptr) +{ + int ret; + + if (!(ret = ssl_ctx_load_pkey(ctx, data, buf, len, + x509ptr, pkeyptr))) + goto fail; + + /* + * 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. + */ + if (!SSL_CTX_use_PrivateKey(ctx, *pkeyptr)) { + SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_SSL_LIB); + goto fail; + } + + return (1); + + fail: + if (*pkeyptr != NULL) + EVP_PKEY_free(*pkeyptr); + if (*x509ptr != NULL) + X509_free(*x509ptr); + *x509ptr = NULL; + *pkeyptr = NULL; + + return (0); +} -- 2.20.1