Hi, this diff adds SNI support to relayd.
It is a bit big and I have to break it down, but I'm sending this first version now to give people a chance to test. The major "infrastructure" change is that keypairs are not stored in relay structs anymore but in a global list where each keypair ("cert") is associated to a relay id. relayd currently loads the keypair using the listen address as the file name for the .key and .crt files. I decided to keep this behavior. The new optional "tls keypair" argument allows to specify one or more keypairs by name instead and they will be loaded as .key and .crt files accordingly. ```relayd.conf protocol foo { tls keypair "localhost" tls keypair "server" # Or: tls { keypair "localhost", keypair "server" } } relay assl { listen on 127.0.0.1 port 443 tls protocol foo forward to 199.233.217.205 port 80 } ``` Results in: relay_load_certfiles: using certificate /etc/ssl/localhost.crt relay_load_certfiles: using private key /etc/ssl/private/localhost.key relay_load_certfiles: using certificate /etc/ssl/server.crt relay_load_certfiles: using private key /etc/ssl/private/server.key Connecting to relayd now gives you certificates based on the server name, using the first one as the default. Comments? Reyk Index: usr.sbin/relayd/ca.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/ca.c,v retrieving revision 1.34 diff -u -p -u -p -r1.34 ca.c --- usr.sbin/relayd/ca.c 19 Sep 2018 11:28:02 -0000 1.34 +++ usr.sbin/relayd/ca.c 9 May 2019 12:35:40 -0000 @@ -108,56 +108,60 @@ hash_x509(X509 *cert, char *hash, size_t void ca_launch(void) { - char hash[TLS_CERT_HASH_SIZE]; - char *buf; - BIO *in = NULL; - EVP_PKEY *pkey = NULL; - struct relay *rlay; - X509 *cert = NULL; - off_t len; + char hash[TLS_CERT_HASH_SIZE]; + char *buf; + BIO *in = NULL; + EVP_PKEY *pkey = NULL; + struct relay *rlay; + struct relay_cert *cert; + X509 *x509 = NULL; + off_t len; - TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) { - if ((rlay->rl_conf.flags & (F_TLS|F_TLSCLIENT)) == 0) + TAILQ_FOREACH(cert, env->sc_certs, cert_entry) { + if (cert->cert_fd == -1 || cert->cert_key_fd == -1) continue; - if (rlay->rl_tls_cert_fd != -1) { - if ((buf = relay_load_fd(rlay->rl_tls_cert_fd, - &len)) == NULL) - fatal("ca_launch: cert relay_load_fd"); + if ((buf = relay_load_fd(cert->cert_fd, &len)) == NULL) + fatal("ca_launch: cert relay_load_fd"); - if ((in = BIO_new_mem_buf(buf, len)) == NULL) - fatalx("ca_launch: cert BIO_new_mem_buf"); + if ((in = BIO_new_mem_buf(buf, len)) == NULL) + fatalx("ca_launch: cert BIO_new_mem_buf"); - if ((cert = PEM_read_bio_X509(in, NULL, - NULL, NULL)) == NULL) - fatalx("ca_launch: cert PEM_read_bio_X509"); + if ((x509 = PEM_read_bio_X509(in, NULL, + NULL, NULL)) == NULL) + fatalx("ca_launch: cert PEM_read_bio_X509"); - hash_x509(cert, hash, sizeof(hash)); + hash_x509(x509, hash, sizeof(hash)); - BIO_free(in); - X509_free(cert); - purge_key(&buf, len); - } - if (rlay->rl_conf.tls_key_len) { - if ((in = BIO_new_mem_buf(rlay->rl_tls_key, - rlay->rl_conf.tls_key_len)) == NULL) - fatalx("%s: key", __func__); - - if ((pkey = PEM_read_bio_PrivateKey(in, - NULL, NULL, NULL)) == NULL) - fatalx("%s: PEM", __func__); - BIO_free(in); + BIO_free(in); + X509_free(x509); + purge_key(&buf, len); - rlay->rl_tls_pkey = pkey; + if ((buf = relay_load_fd(cert->cert_key_fd, &len)) == NULL) + fatal("ca_launch: key relay_load_fd"); - if (pkey_add(env, pkey, hash) == NULL) - fatalx("tls pkey"); + if ((in = BIO_new_mem_buf(buf, len)) == NULL) + fatalx("%s: key", __func__); - purge_key(&rlay->rl_tls_key, - rlay->rl_conf.tls_key_len); - } + if ((pkey = PEM_read_bio_PrivateKey(in, + NULL, NULL, NULL)) == NULL) + fatalx("%s: PEM", __func__); - if (rlay->rl_tls_cacert_fd != -1) { + cert->cert_pkey = pkey; + + if (pkey_add(env, pkey, hash) == NULL) + fatalx("tls pkey"); + + BIO_free(in); + purge_key(&buf, len); + } + + TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) { + if ((rlay->rl_conf.flags & (F_TLS|F_TLSCLIENT)) == 0) + continue; + + if (rlay->rl_tls_cacert_fd != -1 && + rlay->rl_conf.tls_cakey_len) { if ((buf = relay_load_fd(rlay->rl_tls_cacert_fd, &len)) == NULL) fatal("ca_launch: cacert relay_load_fd"); @@ -165,17 +169,16 @@ ca_launch(void) if ((in = BIO_new_mem_buf(buf, len)) == NULL) fatalx("ca_launch: cacert BIO_new_mem_buf"); - if ((cert = PEM_read_bio_X509(in, NULL, + if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) fatalx("ca_launch: cacert PEM_read_bio_X509"); - hash_x509(cert, hash, sizeof(hash)); + hash_x509(x509, hash, sizeof(hash)); BIO_free(in); - X509_free(cert); + X509_free(x509); purge_key(&buf, len); - } - if (rlay->rl_conf.tls_cakey_len) { + if ((in = BIO_new_mem_buf(rlay->rl_tls_cakey, rlay->rl_conf.tls_cakey_len)) == NULL) fatalx("%s: key", __func__); Index: usr.sbin/relayd/config.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/config.c,v retrieving revision 1.36 diff -u -p -u -p -r1.36 config.c --- usr.sbin/relayd/config.c 29 Nov 2017 15:24:50 -0000 1.36 +++ usr.sbin/relayd/config.c 9 May 2019 12:35:40 -0000 @@ -81,6 +81,12 @@ config_init(struct relayd *env) calloc(1, sizeof(*env->sc_relays))) == NULL) return (-1); TAILQ_INIT(env->sc_relays); + + if ((env->sc_certs = + calloc(1, sizeof(*env->sc_certs))) == NULL) + return (-1); + TAILQ_INIT(env->sc_certs); + if ((env->sc_pkeys = calloc(1, sizeof(*env->sc_pkeys))) == NULL) return (-1); @@ -98,6 +104,7 @@ config_init(struct relayd *env) env->sc_proto_default.tcpflags = TCPFLAG_DEFAULT; env->sc_proto_default.tcpbacklog = RELAY_BACKLOG; env->sc_proto_default.tlsflags = TLSFLAG_DEFAULT; + TAILQ_INIT(&env->sc_proto_default.tlscerts); (void)strlcpy(env->sc_proto_default.tlsciphers, TLSCIPHERS_DEFAULT, sizeof(env->sc_proto_default.tlsciphers)); @@ -140,6 +147,7 @@ config_purge(struct relayd *env, u_int r struct netroute *nr; struct router *rt; struct ca_pkey *pkey; + struct keyname *keyname; u_int what; what = ps->ps_what[privsep_process] & reset; @@ -185,6 +193,12 @@ config_purge(struct relayd *env, u_int r free(proto->style); free(proto->tlscapass); free(proto); + while ((keyname = + TAILQ_FIRST(&proto->tlscerts)) != NULL) { + TAILQ_REMOVE(&proto->tlscerts, keyname, entry); + free(keyname->name); + free(keyname); + } } env->sc_protocount = 0; } @@ -690,6 +704,7 @@ config_getproto(struct relayd *env, stru } TAILQ_INIT(&proto->rules); + TAILQ_INIT(&proto->tlscerts); proto->tlscapass = NULL; TAILQ_INSERT_TAIL(env->sc_protos, proto, entry); @@ -773,12 +788,13 @@ config_getrule(struct relayd *env, struc } static int -config_setrelayfd(struct privsep *ps, int id, int n, int rlay_id, int type, - int ofd) +config_setrelayfd(struct privsep *ps, int id, int n, + objid_t obj_id, objid_t rlay_id, enum fd_type type, int ofd) { struct ctl_relayfd rfd; int fd; + rfd.id = obj_id; rfd.relayid = rlay_id; rfd.type = type; @@ -798,6 +814,7 @@ config_setrelay(struct relayd *env, stru struct ctl_relaytable crt; struct relay_table *rlt; struct relay_config rl; + struct relay_cert *cert; int id; int fd, n, m; struct iovec iov[6]; @@ -824,12 +841,6 @@ config_setrelay(struct relayd *env, stru iov[c++].iov_len = sizeof(rl); if ((what & CONFIG_CA_ENGINE) == 0 && - rl.tls_key_len) { - iov[c].iov_base = rlay->rl_tls_key; - iov[c++].iov_len = rl.tls_key_len; - } else - rl.tls_key_len = 0; - if ((what & CONFIG_CA_ENGINE) == 0 && rl.tls_cakey_len) { iov[c].iov_base = rlay->rl_tls_cakey; iov[c++].iov_len = rl.tls_cakey_len; @@ -868,22 +879,42 @@ config_setrelay(struct relayd *env, stru } } - - if (what & CONFIG_CERTS) { + /* cert keypairs */ + TAILQ_FOREACH(cert, env->sc_certs, cert_entry) { + if (cert->cert_relayid != rlay->rl_conf.id) + continue; n = -1; proc_range(ps, id, &n, &m); - for (n = 0; n < m; n++) { - if (rlay->rl_tls_cert_fd != -1 && + for (n = 0; (what & CONFIG_CERTS) && n < m; n++) { + if (cert->cert_fd != -1 && config_setrelayfd(ps, id, n, - rlay->rl_conf.id, RELAY_FD_CERT, - rlay->rl_tls_cert_fd) == -1) { + cert->cert_id, cert->cert_relayid, + RELAY_FD_CERT, cert->cert_fd) == -1) { log_warn("%s: fd passing failed for " "`%s'", __func__, rlay->rl_conf.name); return (-1); } - if (rlay->rl_tls_ca_fd != -1 && + if (id == PROC_CA && + cert->cert_key_fd != -1 && config_setrelayfd(ps, id, n, + cert->cert_id, cert->cert_relayid, + RELAY_FD_KEY, cert->cert_key_fd) == -1) { + log_warn("%s: fd passing failed for " + "`%s'", __func__, + rlay->rl_conf.name); + return (-1); + } + } + } + + /* CA certs */ + if (what & CONFIG_CERTS) { + n = -1; + proc_range(ps, id, &n, &m); + for (n = 0; n < m; n++) { + if (rlay->rl_tls_ca_fd != -1 && + config_setrelayfd(ps, id, n, 0, rlay->rl_conf.id, RELAY_FD_CACERT, rlay->rl_tls_ca_fd) == -1) { log_warn("%s: fd passing failed for " @@ -892,7 +923,7 @@ config_setrelay(struct relayd *env, stru return (-1); } if (rlay->rl_tls_cacert_fd != -1 && - config_setrelayfd(ps, id, n, + config_setrelayfd(ps, id, n, 0, rlay->rl_conf.id, RELAY_FD_CAFILE, rlay->rl_tls_cacert_fd) == -1) { log_warn("%s: fd passing failed for " @@ -933,10 +964,6 @@ config_setrelay(struct relayd *env, stru close(rlay->rl_s); rlay->rl_s = -1; } - if (rlay->rl_tls_cert_fd != -1) { - close(rlay->rl_tls_cert_fd); - rlay->rl_tls_cert_fd = -1; - } if (rlay->rl_tls_cacert_fd != -1) { close(rlay->rl_tls_cacert_fd); rlay->rl_tls_cacert_fd = -1; @@ -945,6 +972,19 @@ config_setrelay(struct relayd *env, stru close(rlay->rl_tls_ca_fd); rlay->rl_tls_ca_fd = -1; } + TAILQ_FOREACH(cert, env->sc_certs, cert_entry) { + if (cert->cert_relayid != rlay->rl_conf.id) + continue; + + if (cert->cert_fd != -1) { + close(cert->cert_fd); + cert->cert_fd = -1; + } + if (cert->cert_key_fd != -1) { + close(cert->cert_key_fd); + cert->cert_key_fd = -1; + } + } return (0); } @@ -965,7 +1005,6 @@ config_getrelay(struct relayd *env, stru s = sizeof(rlay->rl_conf); rlay->rl_s = imsg->fd; - rlay->rl_tls_cert_fd = -1; rlay->rl_tls_ca_fd = -1; rlay->rl_tls_cacert_fd = -1; @@ -980,17 +1019,11 @@ config_getrelay(struct relayd *env, stru } if ((off_t)(IMSG_DATA_SIZE(imsg) - s) < - (rlay->rl_conf.tls_key_len + rlay->rl_conf.tls_cakey_len)) { + (rlay->rl_conf.tls_cakey_len)) { log_debug("%s: invalid message length", __func__); goto fail; } - if (rlay->rl_conf.tls_key_len) { - if ((rlay->rl_tls_key = get_data(p + s, - rlay->rl_conf.tls_key_len)) == NULL) - goto fail; - s += rlay->rl_conf.tls_key_len; - } if (rlay->rl_conf.tls_cakey_len) { if ((rlay->rl_tls_cakey = get_data(p + s, rlay->rl_conf.tls_cakey_len)) == NULL) @@ -1010,7 +1043,6 @@ config_getrelay(struct relayd *env, stru return (0); fail: - free(rlay->rl_tls_key); free(rlay->rl_tls_cakey); close(rlay->rl_s); free(rlay); @@ -1062,22 +1094,37 @@ config_getrelaytable(struct relayd *env, int config_getrelayfd(struct relayd *env, struct imsg *imsg) { - struct relay_table *rlt = NULL; struct ctl_relayfd crfd; - struct relay *rlay; + struct relay *rlay = NULL; + struct relay_cert *cert; u_int8_t *p = imsg->data; IMSG_SIZE_CHECK(imsg, &crfd); memcpy(&crfd, p, sizeof(crfd)); - if ((rlay = relay_find(env, crfd.relayid)) == NULL) { - log_debug("%s: unknown relay", __func__); - goto fail; + switch (crfd.type) { + case RELAY_FD_CERT: + case RELAY_FD_KEY: + if ((cert = cert_find(env, crfd.id)) == NULL) { + if ((cert = cert_add(env, crfd.id)) == NULL) + return (-1); + cert->cert_relayid = crfd.relayid; + } + /* FALLTHROUGH */ + default: + if ((rlay = relay_find(env, crfd.relayid)) == NULL) { + log_debug("%s: unknown relay", __func__); + return (-1); + } + break; } switch (crfd.type) { case RELAY_FD_CERT: - rlay->rl_tls_cert_fd = imsg->fd; + cert->cert_fd = imsg->fd; + break; + case RELAY_FD_KEY: + cert->cert_key_fd = imsg->fd; break; case RELAY_FD_CACERT: rlay->rl_tls_ca_fd = imsg->fd; @@ -1092,8 +1139,4 @@ config_getrelayfd(struct relayd *env, st imsg->fd, crfd.type, rlay->rl_conf.name); return (0); - - fail: - free(rlt); - return (-1); } Index: usr.sbin/relayd/parse.y =================================================================== RCS file: /cvs/src/usr.sbin/relayd/parse.y,v retrieving revision 1.233 diff -u -p -u -p -r1.233 parse.y --- usr.sbin/relayd/parse.y 13 Mar 2019 23:29:32 -0000 1.233 +++ usr.sbin/relayd/parse.y 9 May 2019 12:35:41 -0000 @@ -168,8 +168,8 @@ typedef struct { %token ALL APPEND BACKLOG BACKUP BUFFER CA CACHE SET CHECK CIPHERS CODE %token COOKIE DEMOTE DIGEST DISABLE ERROR EXPECT PASS BLOCK EXTERNAL FILENAME %token FORWARD FROM HASH HEADER HEADERLEN HOST HTTP ICMP INCLUDE INET INET6 -%token INTERFACE INTERVAL IP LABEL LISTEN VALUE LOADBALANCE LOG LOOKUP METHOD -%token MODE NAT NO DESTINATION NODELAY NOTHING ON PARENT PATH PFTAG PORT +%token INTERFACE INTERVAL IP KEYPAIR LABEL LISTEN VALUE LOADBALANCE LOG LOOKUP +%token METHOD MODE NAT NO DESTINATION NODELAY NOTHING ON PARENT PATH PFTAG PORT %token PREFORK PRIORITY PROTO QUERYSTR REAL REDIRECT RELAY REMOVE REQUEST %token RESPONSE RETRY QUICK RETURN ROUNDROBIN ROUTE SACK SCRIPT SEND SESSION %token SNMP SOCKET SPLICE SSL STICKYADDR STYLE TABLE TAG TAGGED TCP TIMEOUT TLS @@ -991,7 +991,7 @@ optdigest : digest { ; proto : relay_proto PROTO STRING { - struct protocol *p; + struct protocol *p; if (!loadcfg) { free($3); @@ -1028,6 +1028,7 @@ proto : relay_proto PROTO STRING { p->tcpbacklog = RELAY_BACKLOG; p->httpheaderlen = RELAY_DEFHEADERLENGTH; TAILQ_INIT(&p->rules); + TAILQ_INIT(&p->tlscerts); (void)strlcpy(p->tlsciphers, TLSCIPHERS_DEFAULT, sizeof(p->tlsciphers)); (void)strlcpy(p->tlsecdhecurves, TLSECDHECURVES_DEFAULT, @@ -1256,6 +1257,17 @@ tlsflags : SESSION TICKETS { proto->tick } free($3); } + | KEYPAIR STRING { + struct keyname *name; + + if ((name = calloc(1, sizeof(*name))) == NULL) { + yyerror("calloc"); + free($2); + YYERROR; + } + name->name = $2; + TAILQ_INSERT_TAIL(&proto->tlscerts, name, entry); + } | NO flag { proto->tlsflags &= ~($2); } | flag { proto->tlsflags |= $1; } ; @@ -1663,18 +1675,9 @@ relay : RELAY STRING { YYACCEPT; } - TAILQ_FOREACH(r, conf->sc_relays, rl_entry) - if (!strcmp(r->rl_conf.name, $2)) - break; - if (r != NULL) { - yyerror("relay %s defined twice", $2); - free($2); - YYERROR; - } - TAILQ_INIT(&relays); - if ((r = calloc(1, sizeof (*r))) == NULL) fatal("out of memory"); + TAILQ_INIT(&relays); if (strlcpy(r->rl_conf.name, $2, sizeof(r->rl_conf.name)) >= @@ -1694,7 +1697,6 @@ relay : RELAY STRING { r->rl_proto = NULL; r->rl_conf.proto = EMPTY_ID; r->rl_conf.dstretry = 0; - r->rl_tls_cert_fd = -1; r->rl_tls_ca_fd = -1; r->rl_tls_cacert_fd = -1; TAILQ_INIT(&r->rl_tables); @@ -1706,7 +1708,16 @@ relay : RELAY STRING { dstmode = RELAY_DSTMODE_DEFAULT; rlay = r; } '{' optnl relayopts_l '}' { - struct relay *r; + struct relay *r; + struct relay_config *rlconf = &rlay->rl_conf; + struct keyname *name; + + if (relay_findbyname(conf, rlconf->name) != NULL || + relay_findbyaddr(conf, rlconf) != NULL) { + yyerror("relay %s or listener defined twice", + rlconf->name); + YYERROR; + } if (rlay->rl_conf.ss.ss_family == AF_UNSPEC) { yyerror("relay %s has no listener", @@ -1730,11 +1741,23 @@ relay : RELAY STRING { rlay->rl_proto = &conf->sc_proto_default; rlay->rl_conf.proto = conf->sc_proto_default.id; } - if (relay_load_certfiles(rlay) == -1) { + + if (TAILQ_EMPTY(&rlay->rl_proto->tlscerts) && + relay_load_certfiles(conf, rlay, NULL) == -1) { yyerror("cannot load certificates for relay %s", rlay->rl_conf.name); YYERROR; } + TAILQ_FOREACH(name, &rlay->rl_proto->tlscerts, entry) { + if (relay_load_certfiles(conf, + rlay, name->name) == -1) { + yyerror("cannot load keypair %s" + " for relay %s", name->name, + rlay->rl_conf.name); + YYERROR; + } + } + conf->sc_relaycount++; SPLAY_INIT(&rlay->rl_sessions); TAILQ_INSERT_TAIL(conf->sc_relays, rlay, rl_entry); @@ -2280,6 +2303,7 @@ lookup(char *s) { "interval", INTERVAL }, { "ip", IP }, { "key", KEY }, + { "keypair", KEYPAIR }, { "label", LABEL }, { "least-states", LEASTSTATES }, { "listen", LISTEN }, @@ -3231,11 +3255,8 @@ relay_inherit(struct relay *ra, struct r rb->rl_conf.flags = (ra->rl_conf.flags & ~F_TLS) | (rc.flags & F_TLS); if (!(rb->rl_conf.flags & F_TLS)) { - rb->rl_tls_cert_fd = -1; rb->rl_tls_cacert_fd = -1; rb->rl_tls_ca_fd = -1; - rb->rl_tls_key = NULL; - rb->rl_conf.tls_key_len = 0; } TAILQ_INIT(&rb->rl_tables); @@ -3253,10 +3274,12 @@ relay_inherit(struct relay *ra, struct r if (relay_findbyname(conf, rb->rl_conf.name) != NULL || relay_findbyaddr(conf, &rb->rl_conf) != NULL) { - yyerror("relay %s defined twice", rb->rl_conf.name); + yyerror("relay %s or listener defined twice", + rb->rl_conf.name); goto err; } - if (relay_load_certfiles(rb) == -1) { + + if (relay_load_certfiles(conf, rb, NULL) == -1) { yyerror("cannot load certificates for relay %s", rb->rl_conf.name); goto err; Index: usr.sbin/relayd/relay.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relay.c,v retrieving revision 1.243 diff -u -p -u -p -r1.243 relay.c --- usr.sbin/relayd/relay.c 8 May 2019 23:22:19 -0000 1.243 +++ usr.sbin/relayd/relay.c 9 May 2019 12:35:41 -0000 @@ -2080,8 +2080,9 @@ relay_tls_ctx_create(struct relay *rlay) { struct tls_config *tls_cfg, *tls_client_cfg; struct tls *tls = NULL; + struct relay_cert *cert; const char *fake_key; - int fake_keylen; + int fake_keylen, keyfound = 0; char *buf = NULL, *cabuf = NULL; off_t len = 0, calen = 0; @@ -2113,6 +2114,7 @@ relay_tls_ctx_create(struct relay *rlay) log_warn("failed to read root certificates"); goto err; } + rlay->rl_tls_ca_fd = -1; if (tls_config_set_ca_mem(tls_client_cfg, buf, len) != 0) { @@ -2141,24 +2143,49 @@ relay_tls_ctx_create(struct relay *rlay) */ tls_config_skip_private_key_check(tls_cfg); - if ((buf = relay_load_fd(rlay->rl_tls_cert_fd, &len)) == NULL) { - log_warn("failed to load tls certificate"); - goto err; - } + TAILQ_FOREACH(cert, env->sc_certs, cert_entry) { + if (cert->cert_relayid != rlay->rl_conf.id || + cert->cert_fd == -1) + continue; + keyfound++; + + if ((buf = relay_load_fd(cert->cert_fd, + &len)) == NULL) { + log_warn("failed to load tls certificate"); + goto err; + } + cert->cert_fd = -1; - if ((fake_keylen = ssl_ctx_fake_private_key(buf, len, - &fake_key)) == -1) { - /* error already printed */ - goto err; - } + if ((fake_keylen = ssl_ctx_fake_private_key(buf, len, + &fake_key)) == -1) { + /* error already printed */ + goto err; + } - if (tls_config_set_keypair_ocsp_mem(tls_cfg, buf, len, - fake_key, fake_keylen, NULL, 0) != 0) { - log_warnx("failed to set tls certificate: %s", - tls_config_error(tls_cfg)); - goto err; - } + log_debug("%s: keyfound %d", __func__, keyfound); + if (keyfound == 1 && + tls_config_set_keypair_ocsp_mem(tls_cfg, buf, len, + fake_key, fake_keylen, NULL, 0) != 0) { + log_warnx("failed to set tls certificate: %s", + tls_config_error(tls_cfg)); + goto err; + } + + /* loading certificate public key */ + if (keyfound == 1 && + !ssl_load_pkey(buf, len, NULL, &rlay->rl_tls_pkey)) + goto err; + + if (tls_config_add_keypair_ocsp_mem(tls_cfg, buf, len, + fake_key, fake_keylen, NULL, 0) != 0) { + log_warnx("failed to add tls certificate: %s", + tls_config_error(tls_cfg)); + goto err; + } + + purge_key(&buf, len); + } if (rlay->rl_tls_cacert_fd != -1) { if ((cabuf = relay_load_fd(rlay->rl_tls_cacert_fd, @@ -2170,11 +2197,8 @@ relay_tls_ctx_create(struct relay *rlay) if (!ssl_load_pkey(cabuf, calen, &rlay->rl_tls_cacertx509, &rlay->rl_tls_capkey)) goto err; - /* loading certificate public key */ - log_debug("%s: loading certificate", __func__); - if (!ssl_load_pkey(buf, len, NULL, &rlay->rl_tls_pkey)) - goto err; } + rlay->rl_tls_cacert_fd = -1; tls = tls_server(); if (tls == NULL) { @@ -2191,14 +2215,8 @@ relay_tls_ctx_create(struct relay *rlay) rlay->rl_tls_ctx = tls; purge_key(&cabuf, calen); - purge_key(&buf, len); } - /* The fd for the keys/certs are not needed anymore */ - close(rlay->rl_tls_cert_fd); - close(rlay->rl_tls_cacert_fd); - close(rlay->rl_tls_ca_fd); - if (rlay->rl_tls_client_cfg == NULL) tls_config_free(tls_client_cfg); if (rlay->rl_tls_cfg == NULL) @@ -2645,75 +2663,6 @@ relay_load_fd(int fd, off_t *len) close(fd); errno = err; return (NULL); -} - -int -relay_load_certfiles(struct relay *rlay) -{ - char certfile[PATH_MAX]; - char hbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; - struct protocol *proto = rlay->rl_proto; - int useport = htons(rlay->rl_conf.port); - - if (rlay->rl_conf.flags & F_TLSCLIENT) { - if (strlen(proto->tlsca)) { - if ((rlay->rl_tls_ca_fd = - open(proto->tlsca, O_RDONLY)) == -1) - return (-1); - log_debug("%s: using ca %s", __func__, proto->tlsca); - } - if (strlen(proto->tlscacert)) { - if ((rlay->rl_tls_cacert_fd = - open(proto->tlscacert, O_RDONLY)) == -1) - return (-1); - log_debug("%s: using ca certificate %s", __func__, - proto->tlscacert); - } - if (strlen(proto->tlscakey) && proto->tlscapass != NULL) { - if ((rlay->rl_tls_cakey = - ssl_load_key(env, proto->tlscakey, - &rlay->rl_conf.tls_cakey_len, - proto->tlscapass)) == NULL) - return (-1); - log_debug("%s: using ca key %s", __func__, - proto->tlscakey); - } - } - - if ((rlay->rl_conf.flags & F_TLS) == 0) - return (0); - - if (print_host(&rlay->rl_conf.ss, hbuf, sizeof(hbuf)) == NULL) - return (-1); - - if (snprintf(certfile, sizeof(certfile), - "/etc/ssl/%s:%u.crt", hbuf, useport) == -1) - return (-1); - if ((rlay->rl_tls_cert_fd = open(certfile, O_RDONLY)) == -1) { - if (snprintf(certfile, sizeof(certfile), - "/etc/ssl/%s.crt", hbuf) == -1) - return (-1); - if ((rlay->rl_tls_cert_fd = open(certfile, O_RDONLY)) == -1) - return (-1); - useport = 0; - } - log_debug("%s: using certificate %s", __func__, certfile); - - if (useport) { - if (snprintf(certfile, sizeof(certfile), - "/etc/ssl/private/%s:%u.key", hbuf, useport) == -1) - return -1; - } else { - if (snprintf(certfile, sizeof(certfile), - "/etc/ssl/private/%s.key", hbuf) == -1) - return -1; - } - if ((rlay->rl_tls_key = ssl_load_key(env, certfile, - &rlay->rl_conf.tls_key_len, NULL)) == NULL) - return (-1); - log_debug("%s: using private key %s", __func__, certfile); - - return (0); } int Index: usr.sbin/relayd/relayd.c =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relayd.c,v retrieving revision 1.176 diff -u -p -u -p -r1.176 relayd.c --- usr.sbin/relayd/relayd.c 8 May 2019 23:22:19 -0000 1.176 +++ usr.sbin/relayd/relayd.c 9 May 2019 12:35:41 -0000 @@ -554,6 +554,7 @@ purge_relay(struct relayd *env, struct r { struct rsession *con; struct relay_table *rlt; + struct relay_cert *cert, *tmpcert; /* shutdown and remove relay */ if (event_initialized(&rlay->rl_ev)) @@ -572,7 +573,6 @@ purge_relay(struct relayd *env, struct r if (rlay->rl_dstbev != NULL) bufferevent_free(rlay->rl_dstbev); - purge_key(&rlay->rl_tls_key, rlay->rl_conf.tls_key_len); purge_key(&rlay->rl_tls_cakey, rlay->rl_conf.tls_cakey_len); if (rlay->rl_tls_pkey != NULL) { @@ -597,6 +597,19 @@ purge_relay(struct relayd *env, struct r free(rlt); } + TAILQ_FOREACH_SAFE(cert, env->sc_certs, cert_entry, tmpcert) { + if (rlay->rl_conf.id != cert->cert_relayid) + continue; + if (cert->cert_fd != -1) + close(cert->cert_fd); + if (cert->cert_key_fd != -1) + close(cert->cert_key_fd); + if (cert->cert_pkey != NULL) + EVP_PKEY_free(cert->cert_pkey); + TAILQ_REMOVE(env->sc_certs, cert, cert_entry); + free(cert); + } + free(rlay); } @@ -1234,6 +1247,127 @@ pkey_add(struct relayd *env, EVP_PKEY *p TAILQ_INSERT_TAIL(env->sc_pkeys, ca_pkey, pkey_entry); return (ca_pkey); +} + +struct relay_cert * +cert_add(struct relayd *env, objid_t id) +{ + static objid_t last_cert_id = 0; + struct relay_cert *cert; + + if ((cert = calloc(1, sizeof(*cert))) == NULL) + return (NULL); + + if (id == 0) + id = ++last_cert_id; + cert->cert_id = id; + cert->cert_fd = -1; + cert->cert_key_fd = -1; + + TAILQ_INSERT_TAIL(env->sc_certs, cert, cert_entry); + + return (cert); +} + +struct relay_cert * +cert_find(struct relayd *env, objid_t id) +{ + struct relay_cert *cert; + + TAILQ_FOREACH(cert, env->sc_certs, cert_entry) + if (cert->cert_id == id) + return (cert); + return (NULL); +} + +int +relay_load_certfiles(struct relayd *env, struct relay *rlay, const char *name) +{ + char certfile[PATH_MAX]; + char hbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + struct protocol *proto = rlay->rl_proto; + struct relay_cert *cert; + int useport = htons(rlay->rl_conf.port); + int cert_fd = -1, key_fd = -1; + + if (rlay->rl_conf.flags & F_TLSCLIENT) { + if (strlen(proto->tlsca) && rlay->rl_tls_ca_fd == -1) { + if ((rlay->rl_tls_ca_fd = + open(proto->tlsca, O_RDONLY)) == -1) + return (-1); + log_debug("%s: using ca %s", __func__, proto->tlsca); + } + if (strlen(proto->tlscacert) && rlay->rl_tls_cacert_fd == -1) { + if ((rlay->rl_tls_cacert_fd = + open(proto->tlscacert, O_RDONLY)) == -1) + return (-1); + log_debug("%s: using ca certificate %s", __func__, + proto->tlscacert); + } + if (strlen(proto->tlscakey) && !rlay->rl_conf.tls_cakey_len && + proto->tlscapass != NULL) { + if ((rlay->rl_tls_cakey = + ssl_load_key(env, proto->tlscakey, + &rlay->rl_conf.tls_cakey_len, + proto->tlscapass)) == NULL) + return (-1); + log_debug("%s: using ca key %s", __func__, + proto->tlscakey); + } + } + + if ((rlay->rl_conf.flags & F_TLS) == 0) + return (0); + + if (name == NULL && + print_host(&rlay->rl_conf.ss, hbuf, sizeof(hbuf)) == NULL) + goto fail; + else if (name != NULL && + strlcpy(hbuf, name, sizeof(hbuf)) >= sizeof(hbuf)) + goto fail; + + if (snprintf(certfile, sizeof(certfile), + "/etc/ssl/%s:%u.crt", hbuf, useport) == -1) + goto fail; + if ((cert_fd = open(certfile, O_RDONLY)) == -1) { + if (snprintf(certfile, sizeof(certfile), + "/etc/ssl/%s.crt", hbuf) == -1) + goto fail; + if ((cert_fd = open(certfile, O_RDONLY)) == -1) + goto fail; + useport = 0; + } + log_debug("%s: using certificate %s", __func__, certfile); + + if (useport) { + if (snprintf(certfile, sizeof(certfile), + "/etc/ssl/private/%s:%u.key", hbuf, useport) == -1) + goto fail; + } else { + if (snprintf(certfile, sizeof(certfile), + "/etc/ssl/private/%s.key", hbuf) == -1) + goto fail; + } + if ((key_fd = open(certfile, O_RDONLY)) == -1) + goto fail; + log_debug("%s: using private key %s", __func__, certfile); + + if ((cert = cert_add(env, 0)) == NULL) + goto fail; + + cert->cert_relayid = rlay->rl_conf.id; + cert->cert_fd = cert_fd; + cert->cert_key_fd = key_fd; + + return (0); + + fail: + if (cert_fd != -1) + close(cert_fd); + if (key_fd != -1) + close(key_fd); + + return (-1); } void Index: usr.sbin/relayd/relayd.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relayd.conf.5,v retrieving revision 1.188 diff -u -p -u -p -r1.188 relayd.conf.5 --- usr.sbin/relayd/relayd.conf.5 4 Mar 2019 21:25:03 -0000 1.188 +++ usr.sbin/relayd/relayd.conf.5 9 May 2019 12:35:41 -0000 @@ -682,27 +682,10 @@ Like the previous directive, but for red .Xc Specify the address and port for the relay to listen on. The relay will accept incoming connections to the specified address. -.Pp If the .Ic tls keyword is present, the relay will accept connections using the encrypted TLS protocol. -The relay will attempt to look up a private key in -.Pa /etc/ssl/private/address:port.key -and a public certificate in -.Pa /etc/ssl/address:port.crt , -where -.Ar address -is the specified IP address and -.Ar port -is the specified port that the relay listens on. -If these files are not present, the relay will continue to look in -.Pa /etc/ssl/private/address.key -and -.Pa /etc/ssl/address.crt . -See -.Xr ssl 8 -for details about SSL/TLS server certificates. .It Ic protocol Ar name Use the specified protocol definition for the relay. The generic TCP protocol options will be used by default; @@ -963,6 +946,25 @@ Values higher than 1024 bits can cause i TLS clients. The default is .Ic no edh . +.It Ic keypair Ar name +The relay will attempt to look up a private key in +.Pa /etc/ssl/private/name:port.key +and a public certificate in +.Pa /etc/ssl/name:port.crt , +where +.Ar port +is the specified port that the relay listens on. +If these files are not present, the relay will continue to look in +.Pa /etc/ssl/private/name.key +and +.Pa /etc/ssl/name.crt . +This option can be specified multiple times for TLS Server Name Indication. +If not specified, +a keypair will be loaded using the specified IP address of the relay as +.Ar name . +See +.Xr ssl 8 +for details about SSL/TLS server certificates. .It Ic no cipher-server-preference Prefer the client's cipher list over the server's preferences when choosing a cipher for the connection. Index: usr.sbin/relayd/relayd.h =================================================================== RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v retrieving revision 1.253 diff -u -p -u -p -r1.253 relayd.h --- usr.sbin/relayd/relayd.h 8 May 2019 23:22:19 -0000 1.253 +++ usr.sbin/relayd/relayd.h 9 May 2019 12:35:41 -0000 @@ -137,13 +137,18 @@ struct ctl_relaytable { u_int32_t flags; }; +enum fd_type { + RELAY_FD_CERT = 1, + RELAY_FD_CACERT = 2, + RELAY_FD_CAFILE = 3, + RELAY_FD_KEY = 4 +}; + struct ctl_relayfd { + objid_t id; objid_t relayid; - int type; + enum fd_type type; }; -#define RELAY_FD_CERT 1 -#define RELAY_FD_CACERT 2 -#define RELAY_FD_CAFILE 3 struct ctl_script { objid_t host; @@ -704,6 +709,12 @@ struct relay_ticket_key { #define HTTPFLAG_WEBSOCKETS 0x01 +struct keyname { + TAILQ_ENTRY(keyname) entry; + char *name; +}; +TAILQ_HEAD(keynamelist, keyname); + struct protocol { objid_t id; u_int32_t flags; @@ -722,6 +733,7 @@ struct protocol { char tlscacert[PATH_MAX]; char tlscakey[PATH_MAX]; char *tlscapass; + struct keynamelist tlscerts; char name[MAX_NAME_SIZE]; int tickets; enum prototype type; @@ -759,6 +771,16 @@ struct ca_pkey { }; TAILQ_HEAD(ca_pkeylist, ca_pkey); +struct relay_cert { + objid_t cert_id; + objid_t cert_relayid; + int cert_fd; + int cert_key_fd; + EVP_PKEY *cert_pkey; + TAILQ_ENTRY(relay_cert) cert_entry; +}; +TAILQ_HEAD(relaycertlist, relay_cert); + struct relay_config { objid_t id; u_int32_t flags; @@ -773,7 +795,6 @@ struct relay_config { struct timeval timeout; enum forwardmode fwdmode; union hashkey hashkey; - off_t tls_key_len; off_t tls_cakey_len; }; @@ -798,10 +819,8 @@ struct relay { struct tls_config *rl_tls_client_cfg; struct tls *rl_tls_ctx; - int rl_tls_cert_fd; - int rl_tls_ca_fd; - int rl_tls_cacert_fd; - char *rl_tls_key; + int rl_tls_ca_fd; + int rl_tls_cacert_fd; EVP_PKEY *rl_tls_pkey; X509 *rl_tls_cacertx509; char *rl_tls_cakey; @@ -1083,6 +1102,7 @@ struct relayd { struct routerlist *sc_rts; struct netroutelist *sc_routes; struct ca_pkeylist *sc_pkeys; + struct relaycertlist *sc_certs; struct sessionlist sc_sessions; char sc_demote_group[IFNAMSIZ]; u_int16_t sc_id; @@ -1176,7 +1196,6 @@ int relay_privinit(struct relay *); void relay_notify_done(struct host *, const char *); int relay_session_cmp(struct rsession *, struct rsession *); char *relay_load_fd(int, off_t *); -int relay_load_certfiles(struct relay *); void relay_close(struct rsession *, const char *, int); int relay_reset_event(struct ctl_relay_event *); void relay_natlook(int, short, void *); @@ -1290,6 +1309,10 @@ struct relay *relay_findbyname(struct re struct relay *relay_findbyaddr(struct relayd *, struct relay_config *); EVP_PKEY *pkey_find(struct relayd *, char *hash); struct ca_pkey *pkey_add(struct relayd *, EVP_PKEY *, char *hash); +struct relay_cert *cert_add(struct relayd *, objid_t); +struct relay_cert *cert_find(struct relayd *, objid_t); +int relay_load_certfiles(struct relayd *, struct relay *, + const char *); int expand_string(char *, size_t, const char *, const char *); void translate_string(char *); void purge_key(char **, off_t);