On Fri, Nov 23, 2018 at 04:41:21PM +0100, Sebastian Benoit wrote:
> > It appears that relayd doesn't support TLS client certificate validation
> > (in the manner that httpd does with "tls client ca [cafile]").  Would
> > there be interest in a patch that added such support?
> 
> yes, a patch to support client certificates would be welcome.
> 
> /Benno

Wonderful.  Here's a first pass at such a patch.

Ashe

--

Index: usr.sbin/relayd/config.c
===================================================================
RCS file: /home/kivikakk/cvsync/root/src/usr.sbin/relayd/config.c,v
retrieving revision 1.36
retrieving revision 1.36.6.1
diff -u -p -r1.36 -r1.36.6.1
--- usr.sbin/relayd/config.c    29 Nov 2017 15:24:50 -0000      1.36
+++ usr.sbin/relayd/config.c    24 Nov 2018 16:15:37 -0000      1.36.6.1
@@ -900,6 +900,15 @@ config_setrelay(struct relayd *env, stru
                                            rlay->rl_conf.name);
                                        return (-1);
                                }
+                               if (rlay->rl_tls_client_ca_fd != -1 &&
+                                   config_setrelayfd(ps, id, n,
+                                   rlay->rl_conf.id, RELAY_FD_CLIENTCACERT,
+                                   rlay->rl_tls_client_ca_fd) == -1) {
+                                       log_warn("%s: fd passing failed for "
+                                           "`%s'", __func__,
+                                           rlay->rl_conf.name);
+                                       return (-1);
+                               }
                                /* Prevent fd exhaustion in the parent. */
                                if (proc_flush_imsg(ps, id, n) == -1) {
                                        log_warn("%s: failed to flush "
@@ -945,6 +954,10 @@ config_setrelay(struct relayd *env, stru
                close(rlay->rl_tls_ca_fd);
                rlay->rl_tls_ca_fd = -1;
        }
+       if (rlay->rl_tls_client_ca_fd != -1) {
+               close(rlay->rl_tls_client_ca_fd);
+               rlay->rl_tls_client_ca_fd = -1;
+       }
 
        return (0);
 }
@@ -968,6 +981,7 @@ config_getrelay(struct relayd *env, stru
        rlay->rl_tls_cert_fd = -1;
        rlay->rl_tls_ca_fd = -1;
        rlay->rl_tls_cacert_fd = -1;
+       rlay->rl_tls_client_ca_fd = -1;
 
        if (ps->ps_what[privsep_process] & CONFIG_PROTOS) {
                if (rlay->rl_conf.proto == EMPTY_ID)
@@ -1084,6 +1098,9 @@ config_getrelayfd(struct relayd *env, st
                break;
        case RELAY_FD_CAFILE:
                rlay->rl_tls_cacert_fd = imsg->fd;
+               break;
+       case RELAY_FD_CLIENTCACERT:
+               rlay->rl_tls_client_ca_fd = imsg->fd;
                break;
        }
 
Index: usr.sbin/relayd/parse.y
===================================================================
RCS file: /home/kivikakk/cvsync/root/src/usr.sbin/relayd/parse.y,v
retrieving revision 1.230
retrieving revision 1.230.2.2
diff -u -p -r1.230 -r1.230.2.2
--- usr.sbin/relayd/parse.y     1 Nov 2018 00:18:44 -0000       1.230
+++ usr.sbin/relayd/parse.y     24 Nov 2018 16:15:37 -0000      1.230.2.2
@@ -175,7 +175,7 @@ typedef struct {
 %token SNMP SOCKET SPLICE SSL STICKYADDR STYLE TABLE TAG TAGGED TCP TIMEOUT TLS
 %token TO ROUTER RTLABEL TRANSPARENT TRAP UPDATES URL VIRTUAL WITH TTL RTABLE
 %token MATCH PARAMS RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD ECDHE
-%token EDH TICKETS CONNECTION CONNECTIONS ERRORS STATE CHANGES CHECKS
+%token EDH TICKETS CONNECTION CONNECTIONS ERRORS STATE CHANGES CHECKS CLIENT
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.string>      hostname interface table value optstring
@@ -1246,6 +1246,16 @@ tlsflags : SESSION TICKETS { proto->tick
                        }
                        free($3);
                }
+               | CLIENT CA STRING              {
+                       if (strlcpy(proto->tlsclientca, $3,
+                           sizeof(proto->tlsclientca)) >=
+                           sizeof(proto->tlsclientca)) {
+                               yyerror("tlsclientca truncated");
+                               free($3);
+                               YYERROR;
+                               }
+                       free($3);
+               }
                | NO flag                       { proto->tlsflags &= ~($2); }
                | flag                          { proto->tlsflags |= $1; }
                ;
@@ -1687,6 +1697,7 @@ relay             : RELAY STRING  {
                        r->rl_tls_cert_fd = -1;
                        r->rl_tls_ca_fd = -1;
                        r->rl_tls_cacert_fd = -1;
+                       r->rl_tls_client_ca_fd = -1;
                        TAILQ_INIT(&r->rl_tables);
                        if (last_relay_id == INT_MAX) {
                                yyerror("too many relays defined");
@@ -2241,6 +2252,7 @@ lookup(char *s)
                { "check",              CHECK },
                { "checks",             CHECKS },
                { "ciphers",            CIPHERS },
+               { "client",             CLIENT },
                { "code",               CODE },
                { "connection",         CONNECTION },
                { "cookie",             COOKIE },
@@ -3224,6 +3236,7 @@ relay_inherit(struct relay *ra, struct r
                rb->rl_tls_cert_fd = -1;
                rb->rl_tls_cacert_fd = -1;
                rb->rl_tls_ca_fd = -1;
+               rb->rl_tls_client_ca_fd = -1;
                rb->rl_tls_key = NULL;
                rb->rl_conf.tls_key_len = 0;
        }
Index: usr.sbin/relayd/relay.c
===================================================================
RCS file: /home/kivikakk/cvsync/root/src/usr.sbin/relayd/relay.c,v
retrieving revision 1.241
retrieving revision 1.241.4.1
diff -u -p -r1.241 -r1.241.4.1
--- usr.sbin/relayd/relay.c     19 Sep 2018 11:28:02 -0000      1.241
+++ usr.sbin/relayd/relay.c     24 Nov 2018 16:15:37 -0000      1.241.4.1
@@ -2176,6 +2176,24 @@ relay_tls_ctx_create(struct relay *rlay)
                                goto err;
                }
 
+               if (rlay->rl_tls_client_ca_fd != -1) {
+                       if ((buf = relay_load_fd(rlay->rl_tls_client_ca_fd, 
&len)) ==
+                           NULL) {
+                               log_warn("failed to read tls client CA 
certificate");
+                               goto err;
+                       }
+
+                       if (tls_config_set_ca_mem(tls_cfg, buf, len) !=
+                           0) {
+                               log_warnx("failed to set tls client CA 
certificate: %s",
+                                   tls_config_error(tls_cfg));
+                               goto err;
+                       }
+                       purge_key(&buf, len);
+
+                       tls_config_verify_client(tls_cfg);
+               }
+
                tls = tls_server();
                if (tls == NULL) {
                        log_warnx("unable to allocate TLS context");
@@ -2198,6 +2216,7 @@ relay_tls_ctx_create(struct relay *rlay)
        close(rlay->rl_tls_cert_fd);
        close(rlay->rl_tls_cacert_fd);
        close(rlay->rl_tls_ca_fd);
+       close(rlay->rl_tls_client_ca_fd);
 
        if (rlay->rl_tls_client_cfg == NULL)
                tls_config_free(tls_client_cfg);
@@ -2678,6 +2697,12 @@ relay_load_certfiles(struct relay *rlay)
                        log_debug("%s: using ca key %s", __func__,
                            proto->tlscakey);
                }
+               if (strlen(proto->tlsclientca)) {
+                       if ((rlay->rl_tls_client_ca_fd =
+                           open(proto->tlsclientca, O_RDONLY)) == -1)
+                               return (-1);
+                       log_debug("%s: using client ca %s", __func__, 
proto->tlsclientca);
+               }
        }
 
        if ((rlay->rl_conf.flags & F_TLS) == 0)
@@ -2712,6 +2737,13 @@ relay_load_certfiles(struct relay *rlay)
            &rlay->rl_conf.tls_key_len, NULL)) == NULL)
                return (-1);
        log_debug("%s: using private key %s", __func__, certfile);
+
+       if (strlen(proto->tlsclientca)) {
+               if ((rlay->rl_tls_client_ca_fd =
+                   open(proto->tlsclientca, O_RDONLY)) == -1)
+                       return (-1);
+               log_debug("%s: using client ca %s", __func__, 
proto->tlsclientca);
+       }
 
        return (0);
 }
Index: usr.sbin/relayd/relayd.h
===================================================================
RCS file: /home/kivikakk/cvsync/root/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.251
retrieving revision 1.251.4.1
diff -u -p -r1.251 -r1.251.4.1
--- usr.sbin/relayd/relayd.h    9 Sep 2018 21:06:51 -0000       1.251
+++ usr.sbin/relayd/relayd.h    24 Nov 2018 16:15:37 -0000      1.251.4.1
@@ -141,9 +141,10 @@ struct ctl_relayfd {
        objid_t          relayid;
        int              type;
 };
-#define RELAY_FD_CERT  1
-#define RELAY_FD_CACERT        2
-#define RELAY_FD_CAFILE        3
+#define RELAY_FD_CERT          1
+#define RELAY_FD_CACERT                2
+#define RELAY_FD_CAFILE                3
+#define RELAY_FD_CLIENTCACERT  4
 
 struct ctl_script {
        objid_t          host;
@@ -393,6 +394,7 @@ union hashkey {
 #define F_TLSINSPECT           0x04000000
 #define F_HASHKEY              0x08000000
 #define        F_SNMP_TRAPONLY         0x10000000
+#define F_TLSVERIFY            0x20000000
 
 #define F_BITS                                                         \
        "\10\01DISABLE\02BACKUP\03USED\04DOWN\05ADD\06DEL\07CHANGED"    \
@@ -719,6 +721,7 @@ struct protocol {
        char                     tlscacert[PATH_MAX];
        char                     tlscakey[PATH_MAX];
        char                    *tlscapass;
+       char                     tlsclientca[PATH_MAX];
        char                     name[MAX_NAME_SIZE];
        int                      tickets;
        enum prototype           type;
@@ -798,6 +801,7 @@ struct relay {
        int                     rl_tls_cert_fd;
        int                     rl_tls_ca_fd;
        int                     rl_tls_cacert_fd;
+       int                     rl_tls_client_ca_fd;
        char                    *rl_tls_key;
        EVP_PKEY                *rl_tls_pkey;
        X509                    *rl_tls_cacertx509;
Index: regress/usr.sbin/relayd/Client.pm
===================================================================
RCS file: /home/kivikakk/cvsync/root/src/regress/usr.sbin/relayd/Client.pm,v
retrieving revision 1.12
retrieving revision 1.12.12.2
diff -u -p -r1.12 -r1.12.12.2
--- regress/usr.sbin/relayd/Client.pm   22 Sep 2016 01:16:29 -0000      1.12
+++ regress/usr.sbin/relayd/Client.pm   24 Nov 2018 16:28:45 -0000      
1.12.12.2
@@ -57,6 +57,11 @@ sub child {
            PeerAddr            => $self->{connectaddr},
            PeerPort            => $self->{connectport},
            SSL_verify_mode     => SSL_VERIFY_NONE,
+           SSL_use_cert        => $self->{offertlscert} ? 1 : 0,
+           SSL_cert_file       => $self->{offertlscert} ?
+                                       "client.crt" : "",
+           SSL_key_file        => $self->{offertlscert} ?
+                                       "client.key" : "",
        ) or die ref($self), " $iosocket socket connect failed: $!,$SSL_ERROR";
        if ($self->{sndbuf}) {
                setsockopt($cs, SOL_SOCKET, SO_SNDBUF,
@@ -86,6 +91,12 @@ sub child {
                print STDERR "ssl cipher: ",$cs->get_cipher(),"\n";
                print STDERR "ssl peer certificate:\n",
                    $cs->dump_peer_certificate();
+
+                if ($self->{offertlscert}) {
+                       print STDERR "ssl client certificate:\n";
+                       print STDERR "Subject Name: 
${\$cs->sock_certificate('subject')}\n";
+                       print STDERR "Issuer  Name: 
${\$cs->sock_certificate('issuer')}\n";
+                }
        }
 
        *STDIN = *STDOUT = $self->{cs} = $cs;
Index: regress/usr.sbin/relayd/Makefile
===================================================================
RCS file: /home/kivikakk/cvsync/root/src/regress/usr.sbin/relayd/Makefile,v
retrieving revision 1.15
retrieving revision 1.15.6.3
diff -u -p -r1.15 -r1.15.6.3
--- regress/usr.sbin/relayd/Makefile    6 Oct 2018 10:52:24 -0000       1.15
+++ regress/usr.sbin/relayd/Makefile    24 Nov 2018 16:36:09 -0000      1.15.6.3
@@ -96,7 +96,16 @@ server.req:
 server.crt: ca.crt server.req
        openssl x509 -CAcreateserial -CAkey ca.key -CA ca.crt -req -in 
server.req -out server.crt
 
-${REGRESS_TARGETS:M*ssl*} ${REGRESS_TARGETS:M*https*}: server.crt
+client-ca.crt:
+       openssl req -batch -new -subj 
/L=OpenBSD/O=relayd-regress/OU=client-ca/CN=root/ -nodes -newkey rsa -keyout 
client-ca.key -x509 -out client-ca.crt
+
+client.req:
+       openssl req -batch -new -subj 
/L=OpenBSD/O=relayd-regress/OU=client/CN=localhost/ -nodes -newkey rsa -keyout 
client.key -out client.req
+
+client.crt: client-ca.crt client.req
+       openssl x509 -CAcreateserial -CAkey client-ca.key -CA client-ca.crt 
-req -in client.req -out client.crt
+
+${REGRESS_TARGETS:M*ssl*} ${REGRESS_TARGETS:M*https*}: server.crt client.crt
 .if empty (REMOTE_SSH)
 ${REGRESS_TARGETS:M*ssl*} ${REGRESS_TARGETS:M*https*}: 127.0.0.1.crt
 .else
Index: regress/usr.sbin/relayd/Relayd.pm
===================================================================
RCS file: /home/kivikakk/cvsync/root/src/regress/usr.sbin/relayd/Relayd.pm,v
retrieving revision 1.17
retrieving revision 1.17.4.3
diff -u -p -r1.17 -r1.17.4.3
--- regress/usr.sbin/relayd/Relayd.pm   20 Oct 2018 10:49:09 -0000      1.17
+++ regress/usr.sbin/relayd/Relayd.pm   24 Nov 2018 16:36:09 -0000      1.17.4.3
@@ -84,6 +84,9 @@ sub new {
                print $fh "\n\ttls ca cert ca.crt";
                print $fh "\n\ttls ca key ca.key password ''";
        }
+       if ($self->{verifyclient}) {
+               print $fh "\n\ttls client ca client-ca.crt";
+       }
        # substitute variables in config file
        foreach (@protocol) {
                s/(\$[a-z]+)/$1/eeg;
Index: regress/usr.sbin/relayd/args-ssl-client-verify.pl
===================================================================
RCS file: regress/usr.sbin/relayd/args-ssl-client-verify.pl
diff -N regress/usr.sbin/relayd/args-ssl-client-verify.pl
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ regress/usr.sbin/relayd/args-ssl-client-verify.pl   24 Nov 2018 15:29:47 
-0000      1.1.2.2
@@ -0,0 +1,19 @@
+# test client ssl certificate verification
+
+use strict;
+use warnings;
+
+our %args = (
+    client => {
+       ssl => 1,
+       offertlscert => 1,
+    },
+    relayd => {
+       listenssl => 1,
+       verifyclient => 1,
+    },
+    len => 251,
+    md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;

Reply via email to