relayd: SNI

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

relayd: SNI

Reyk Floeter-2
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);

Reply | Threaded
Open this post in threaded view
|

Re: relayd: SNI

Reyk Floeter-2
On Thu, May 09, 2019 at 02:51:23PM +0200, Reyk Floeter wrote:
> Hi,
>
> this diff adds SNI support to relayd.
>

Below is the same diff again -current minus one debug line.

jsing@ has noted that calling tls_config_set_keypair_ocsp_mem() with
NULL ocsp options could be replaced with tls_config_set_keypair_mem(),
but I'd like to keep it for now because I have an OCSP diff on top of
it that I'll send once SNI is in.

OK?

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 13 May 2019 08:53:17 -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 13 May 2019 08:53:17 -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.234
diff -u -p -u -p -r1.234 parse.y
--- usr.sbin/relayd/parse.y 10 May 2019 09:15:00 -0000 1.234
+++ usr.sbin/relayd/parse.y 13 May 2019 08:53:18 -0000
@@ -169,8 +169,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
@@ -992,7 +992,7 @@ optdigest : digest {
  ;
 
 proto : relay_proto PROTO STRING {
- struct protocol *p;
+ struct protocol *p;
 
  if (!loadcfg) {
  free($3);
@@ -1029,6 +1029,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,
@@ -1257,6 +1258,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; }
  ;
@@ -1688,18 +1700,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)) >=
@@ -1719,7 +1722,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);
@@ -1731,7 +1733,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",
@@ -1755,11 +1766,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);
@@ -2317,6 +2340,7 @@ lookup(char *s)
  { "interval", INTERVAL },
  { "ip", IP },
  { "key", KEY },
+ { "keypair", KEYPAIR },
  { "label", LABEL },
  { "least-states", LEASTSTATES },
  { "listen", LISTEN },
@@ -3268,11 +3292,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);
 
@@ -3290,10 +3311,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.244
diff -u -p -u -p -r1.244 relay.c
--- usr.sbin/relayd/relay.c 10 May 2019 09:15:00 -0000 1.244
+++ usr.sbin/relayd/relay.c 13 May 2019 08:53:18 -0000
@@ -2098,8 +2098,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;
 
@@ -2131,6 +2132,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) {
@@ -2159,24 +2161,47 @@ 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;
- }
+ 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,
@@ -2188,11 +2213,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) {
@@ -2209,14 +2231,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)
@@ -2663,75 +2679,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 13 May 2019 08:53:18 -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.189
diff -u -p -u -p -r1.189 relayd.conf.5
--- usr.sbin/relayd/relayd.conf.5 10 May 2019 09:15:00 -0000 1.189
+++ usr.sbin/relayd/relayd.conf.5 13 May 2019 08:53:18 -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.254
diff -u -p -u -p -r1.254 relayd.h
--- usr.sbin/relayd/relayd.h 10 May 2019 09:15:00 -0000 1.254
+++ usr.sbin/relayd/relayd.h 13 May 2019 08:53:18 -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;
@@ -707,6 +712,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;
@@ -725,6 +736,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;
@@ -762,6 +774,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;
@@ -776,7 +798,6 @@ struct relay_config {
  struct timeval timeout;
  enum forwardmode fwdmode;
  union hashkey hashkey;
- off_t tls_key_len;
  off_t tls_cakey_len;
 };
 
@@ -801,10 +822,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;
@@ -1086,6 +1105,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;
@@ -1179,7 +1199,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 *);
@@ -1293,6 +1312,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);