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);

  • relayd: SNI Reyk Floeter
    • Re: relayd: SNI Reyk Floeter

Reply via email to