On Sat, 29 Jul 2017 20:49:18 +0200
Jan Klemkow <j.klem...@wemelug.de> wrote:
> On Fri, Jul 28, 2017 at 02:05:34AM +0930, Jack Burton wrote:
> > On Thu, 27 Jul 2017 13:10:14 +0200
> >   
> > > But, I found a bug in the part of the FastCGI variables.  The
> > > following condition is always false.
> > >   
> > > > Index: usr.sbin/httpd/server_fcgi.c
> > > > ===================================================================
> > > > RCS file: /cvs/src/usr.sbin/httpd/server_fcgi.c,v
> > > > retrieving revision 1.74
> > > > diff -u -p -r1.74 server_fcgi.c
> > > > --- usr.sbin/httpd/server_fcgi.c        21 Jan 2017 11:32:04
> > > > -0000   1.74 +++ usr.sbin/httpd/server_fcgi.c   21 Jul
> > > > 2017 08:25:57 -0000 @@ -282,11 +283,57 @@ server_fcgi(struct
> > > > httpd *env, struct cl    
> > > ...  
> > > > +               if (srv_conf->tls_ca != NULL) {    
> > > ...  
> > 
> > That's odd -- I'm not seeing that behaviour here -- in my tests
> > srv_conf->tls_ca always behaved just as expected (it's NULL iff the
> > "tls client ca" directive is not given for that server).  
> 
> In the End, I found and fixed the real bug here:
> 
> @@ -430,7 +438,11 @@ config_getserver_config(struct httpd *en
>                 }
> 
>                 f = SRVFLAG_TLS;
> -               srv_conf->flags |= parent->flags & f;
> +               if ((srv_conf->flags & f) == 0) {
> +                       srv_conf->flags |= parent->flags & f;
> +                       srv_conf->tls_ca = parent->tls_ca;
> +                       srv_conf->tls_crl = parent->tls_crl;
> +               }
> 
>                 f = SRVFLAG_ACCESS_LOG;
>                 if ((srv_conf->flags & f) == 0) {

Thanks Jan -- well caught!

I didn't notice that.

Perhaps we also need to change this comment then:

        /*
         * Get variable-length values for the virtual host.  The tls_*
        ones
         * aren't needed in the virtual hosts unless we implement SNI.
         */

...since at least two of the tls_* ones *are* needed when there's a
location block involved...

...and SNI *has* been implemented (by jsing@ last year) without needing
to make any changes to config_getserver_config().

> This additional copy fixes the bug I have seen by this config:
> 
> server "default" {
>         listen on 127.0.0.1 tls port 443
> 
>         # TLS certificate and key files created with acme-client(1)
>         tls certificate "/root/ca/server.crt"
>         tls key "/root/ca/server.key"
>         #tls client ca "/root/ca/ca.crt" crl "/root/ca/ca.crl"
>         tls client ca "/root/ca/ca.crt"
> 
>         location "*.cgi" {
>                 fastcgi
>                 root "/var/www/cgi-bin/env.cgi"
>         }
> 
>         root "/htdocs/"
> }

Thanks. Yes that, together with your short diff above, helps me
understand exactly why I wasn't seeing the issue that you were: clearly
the problem was in the extra srv_conf instance created by the location
block.

Where that gets created in parse.y there's no reference to tls_* at
all (presumably for the same reason as given by the comment in
config.c), which might explain why I missed it in my first cut back
in March...

...well, that and none of my tests involved location blocks (force of
habit -- in production I tend to keep configs as constant as possible
within each server for simplicity's sake, so don't often need to use
location blocks).

Still, it's something I should have tested but didn't.

Thanks for finding & fixing it.

> You find the whole diff below.
> I tested:
>  - TLS without client certs
>  - TLS with client certs and without CRL
>  - TLS with client certs and with CRL
>  - as well as environment variables in CGI-Scripts
> 
> Everything should work now.

It does, but I'm going to suggest one more set of tweaks...

<...>
> Index: usr.sbin/httpd/httpd.h
> ===================================================================
> RCS file: /mount/openbsd/cvs/src/usr.sbin/httpd/httpd.h,v
> retrieving revision 1.133
> diff -u -p -r1.133 httpd.h
> --- usr.sbin/httpd/httpd.h    19 Jul 2017 17:36:25 -0000
> 1.133 +++ usr.sbin/httpd/httpd.h      28 Jul 2017 11:28:25 -0000
> @@ -324,8 +324,8 @@ struct range_data {
>  struct client {
>       uint32_t                 clt_id;
>       pid_t                    clt_pid;
> -     void                    *clt_srv;
> -     void                    *clt_srv_conf;
> +     struct server           *clt_srv;
> +     struct server_config    *clt_srv_conf;

Those declaration changes aren't necessary. The code changes in the
relevant part of your diff consist only of two assignment statements,
where the implicit casts are obvious.

Because I don't *know* the original authors' intentions re planning
for other possible types of config structure in future (see my last
post), I'm suggesting we leave those declarations as-is for now.

If it turns out that they *do* need changing, I'd suggest that might be
better done in a separate commit, with its own message explaining why
(as that change isn't needed for this one, and if a tiny change like
that is rolled in with a huge one like this, that will make it rather
difficult to spot by anyone examining the cvs history in future).

Finally, your full diff omitted my regression test (it included the
changes to the regress perl modules & makefile, but not the actual test
itself), so I'm adding that back in.

Here's a diff that incorporates the code (but not declaration) changes
from your fix, amends the comment to suit [*] & adds the regression
test back in.

[* rather than lengthen the comment to note exceptions-to-the-exception
for tls_ca & tls_crl, I've shortened it to remove the reference to
implementing SNI -- one could argue that that belongs in a diff of its
own, by my thinking was that the introduction of tls_ca & tls_crl makes
the existing comment *more* misleading, so I think it makes sense to
change the comment in this diff]

Thanks again for finding & fixing the tls ca / location interactions
bug.

Index: usr.sbin/httpd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/config.c,v
retrieving revision 1.53
diff -u -p -r1.53 config.c
--- usr.sbin/httpd/config.c     19 Jul 2017 17:36:25 -0000      1.53
+++ usr.sbin/httpd/config.c     30 Jul 2017 07:08:08 -0000
@@ -304,10 +304,18 @@ config_setserver_tls(struct httpd *env, 
 
        log_debug("%s: configuring tls for %s", __func__, srv_conf->name);
 
+       if (config_settls(env, srv, TLS_CFG_CA, "ca", srv_conf->tls_ca,
+           srv_conf->tls_ca_len) != 0)
+               return (-1);
+
        if (config_settls(env, srv, TLS_CFG_CERT, "cert", srv_conf->tls_cert,
            srv_conf->tls_cert_len) != 0)
                return (-1);
 
+       if (config_settls(env, srv, TLS_CFG_CRL, "crl", srv_conf->tls_crl,
+           srv_conf->tls_crl_len) != 0)
+               return (-1);
+
        if (config_settls(env, srv, TLS_CFG_KEY, "key", srv_conf->tls_key,
            srv_conf->tls_key_len) != 0)
                return (-1);
@@ -367,10 +375,7 @@ config_getserver_config(struct httpd *en
        if (config_getserver_auth(env, srv_conf) != 0)
                goto fail;
 
-       /*
-        * Get variable-length values for the virtual host.  The tls_* ones
-        * aren't needed in the virtual hosts unless we implement SNI.
-        */
+       /* Get variable-length values for the virtual host */
        if (srv_conf->return_uri_len != 0) {
                if ((srv_conf->return_uri = get_data(p + s,
                    srv_conf->return_uri_len)) == NULL)
@@ -430,7 +435,11 @@ config_getserver_config(struct httpd *en
                }
 
                f = SRVFLAG_TLS;
-               srv_conf->flags |= parent->flags & f;
+               if ((srv_conf->flags & f) == 0) {
+                       srv_conf->flags |= parent->flags & f;
+                       srv_conf->tls_ca = parent->tls_ca;
+                       srv_conf->tls_crl = parent->tls_crl;
+               }
 
                f = SRVFLAG_ACCESS_LOG;
                if ((srv_conf->flags & f) == 0) {
@@ -655,9 +664,21 @@ config_getserver_tls(struct httpd *env, 
        }
 
        switch (tls_conf.tls_type) {
+       case TLS_CFG_CA:
+               if (config_gettls(env, srv_conf, &tls_conf, "ca", p, len,
+                   &srv_conf->tls_ca, &srv_conf->tls_ca_len) != 0)
+                       goto fail;
+               break;
+
        case TLS_CFG_CERT:
                if (config_gettls(env, srv_conf, &tls_conf, "cert", p, len,
                    &srv_conf->tls_cert, &srv_conf->tls_cert_len) != 0)
+                       goto fail;
+               break;
+
+       case TLS_CFG_CRL:
+               if (config_gettls(env, srv_conf, &tls_conf, "crl", p, len,
+                   &srv_conf->tls_crl, &srv_conf->tls_crl_len) != 0)
                        goto fail;
                break;
 
Index: usr.sbin/httpd/httpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.conf.5,v
retrieving revision 1.82
diff -u -p -r1.82 httpd.conf.5
--- usr.sbin/httpd/httpd.conf.5 9 Apr 2017 09:13:28 -0000       1.82
+++ usr.sbin/httpd/httpd.conf.5 30 Jul 2017 07:08:08 -0000
@@ -342,6 +342,28 @@ The revision of the HTTP specification u
 .It Ic SERVER_SOFTWARE
 The server software name of
 .Xr httpd 8 .
+.It Ic TLS_PEER_CHAIN
+The TLS client certificate chain, if any, PEM encoded, with
+newlines translated to tabs.
+.It Ic TLS_PEER_CRL
+A variable that is set to
+.Qq on
+when the server has been configured to check the client certificate chain
+against CRL(s).
+This variable is omitted otherwise.
+.It Ic TLS_PEER_EXPIRES
+The time at which the TLS cient certificate, if any, expires,
+in seconds since the epoch.
+.It Ic TLS_PEER_HASH
+A hash of the TLS client certificate, if any.
+See
+.Xr tls_peer_cert_hash 3 .
+.It Ic TLS_PEER_ISSUER
+The issuer of the TLS client certificate, if any.
+.It Ic TLS_PEER_OCSP_URL
+The OCSP URL from the TLS client certificate, if any.
+.It Ic TLS_PEER_SUBJECT
+The subject of the TLS client certificate, if any.
 .El
 .It Ic hsts Oo Ar option Oc
 Enable HTTP Strict Transport Security.
@@ -526,6 +548,17 @@ will be used (strong crypto cipher suite
 See the CIPHERS section of
 .Xr openssl 1
 for information about SSL/TLS cipher suites and preference lists.
+.It Ic client ca Ar cafile Op Ic crl Ar crlfile
+Require TLS client certificates whose authenticity can be verified
+against the CA certificate(s) in
+.Ar cafile
+in order to proceed beyond the TLS handshake.
+With
+.Ic crl
+specified, additionally require that no certificate in the client chain be
+listed as revoked in the CRL(s) in
+.Ar crlfile .
+CA certificates and CRLs should be PEM encoded.
 .It Ic dhe Ar params
 Specify the DHE parameters to use for DHE cipher suites.
 Valid parameter values are none, legacy and auto.
@@ -688,6 +721,7 @@ server "www.example.com" {
 .Ed
 .Sh SEE ALSO
 .Xr htpasswd 1 ,
+.Xr tls_peer_cert_hash 3 ,
 .Xr patterns 7 ,
 .Xr httpd 8 ,
 .Xr ocspcheck 8 ,
Index: usr.sbin/httpd/httpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v
retrieving revision 1.133
diff -u -p -r1.133 httpd.h
--- usr.sbin/httpd/httpd.h      19 Jul 2017 17:36:25 -0000      1.133
+++ usr.sbin/httpd/httpd.h      30 Jul 2017 07:08:08 -0000
@@ -476,9 +476,15 @@ struct server_config {
        uint32_t                 maxrequests;
        size_t                   maxrequestbody;
 
+       uint8_t                 *tls_ca;
+       char                    *tls_ca_file;
+       size_t                   tls_ca_len;
        uint8_t                 *tls_cert;
        size_t                   tls_cert_len;
        char                    *tls_cert_file;
+       uint8_t                 *tls_crl;
+       char                    *tls_crl_file;
+       size_t                   tls_crl_len;
        char                     tls_ciphers[NAME_MAX];
        char                     tls_dhe_params[NAME_MAX];
        char                     tls_ecdhe_curve[NAME_MAX];
@@ -520,7 +526,9 @@ struct server_config {
 TAILQ_HEAD(serverhosts, server_config);
 
 enum tls_config_type {
+       TLS_CFG_CA,
        TLS_CFG_CERT,
+       TLS_CFG_CRL,
        TLS_CFG_KEY,
        TLS_CFG_OCSP_STAPLE,
 };
@@ -594,6 +602,8 @@ int  cmdline_symset(char *);
 /* server.c */
 void    server(struct privsep *, struct privsep_proc *);
 int     server_tls_cmp(struct server *, struct server *, int);
+int     server_tls_load_ca(struct server *);
+int     server_tls_load_crl(struct server *);
 int     server_tls_load_keypair(struct server *);
 int     server_tls_load_ocsp(struct server *);
 void    server_generate_ticket_key(struct server_config *);
Index: usr.sbin/httpd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.90
diff -u -p -r1.90 parse.y
--- usr.sbin/httpd/parse.y      25 Mar 2017 17:25:34 -0000      1.90
+++ usr.sbin/httpd/parse.y      30 Jul 2017 07:08:09 -0000
@@ -135,6 +135,7 @@ typedef struct {
 %token PROTOCOLS REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP TICKET
 %token TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD REQUEST
 %token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS
+%token CA CLIENT CRL
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.port>        port
@@ -344,6 +345,22 @@ server             : SERVER optmatch STRING        {
                                YYERROR;
                        }
 
+                       if (server_tls_load_ca(srv) == -1) {
+                               yyerror("server \"%s\": failed to load "
+                                   "ca cert(s)", srv->srv_conf.name);
+                               serverconfig_free(srv_conf);
+                               free(srv);
+                               YYERROR;
+                       }
+
+                       if (server_tls_load_crl(srv) == -1) {
+                               yyerror("server \"%s\": failed to load crl(s)",
+                                   srv->srv_conf.name);
+                               serverconfig_free(srv_conf);
+                               free(srv);
+                               YYERROR;
+                       }
+
                        if (server_tls_load_ocsp(srv) == -1) {
                                yyerror("server \"%s\": failed to load "
                                    "ocsp staple", srv->srv_conf.name);
@@ -737,6 +754,12 @@ tlsopts            : CERTIFICATE STRING            {
                        }
                        free($2);
                }
+               | CLIENT CA STRING tlsclientopt {
+                       free(srv_conf->tls_ca_file);
+                       if ((srv_conf->tls_ca_file = strdup($3)) == NULL)
+                               fatal("out of memory");
+                       free($3);
+               }
                | DHE STRING                    {
                        if (strlcpy(srv_conf->tls_dhe_params, $2,
                            sizeof(srv_conf->tls_dhe_params)) >=
@@ -785,6 +808,14 @@ tlsopts            : CERTIFICATE STRING            {
                }
                ;
 
+tlsclientopt   : /* empty */
+               | CRL STRING    {
+                       free(srv_conf->tls_crl_file);
+                       if ((srv_conf->tls_crl_file = strdup($2)) == NULL)
+                               fatal("out of memory");
+                       free($2);
+               }
+
 root           : ROOT rootflags
                | ROOT '{' optnl rootflags_l '}'
                ;
@@ -1217,12 +1248,15 @@ lookup(char *s)
                { "block",              BLOCK },
                { "body",               BODY },
                { "buffer",             BUFFER },
+               { "ca",                 CA },
                { "certificate",        CERTIFICATE },
                { "chroot",             CHROOT },
                { "ciphers",            CIPHERS },
+               { "client",             CLIENT },
                { "combined",           COMBINED },
                { "common",             COMMON },
                { "connection",         CONNECTION },
+               { "crl",                CRL },
                { "default",            DEFAULT },
                { "dhe",                DHE },
                { "directory",          DIRECTORY },
@@ -2049,6 +2083,15 @@ server_inherit(struct server *src, struc
        if ((dst->srv_conf.tls_key_file =
            strdup(src->srv_conf.tls_key_file)) == NULL)
                fatal("out of memory");
+       if (src->srv_conf.tls_ca_file != NULL) {
+               if ((dst->srv_conf.tls_ca_file =
+                   strdup(src->srv_conf.tls_ca_file)) == NULL)
+                       fatal("out of memory");
+               if (src->srv_conf.tls_crl_file != NULL &&
+                   (dst->srv_conf.tls_crl_file =
+                   strdup(src->srv_conf.tls_crl_file)) == NULL)
+                       fatal("out of memory");
+       }
        if (src->srv_conf.tls_ocsp_staple_file != NULL) {
                if ((dst->srv_conf.tls_ocsp_staple_file =
                    strdup(src->srv_conf.tls_ocsp_staple_file)) == NULL)
@@ -2087,6 +2130,22 @@ server_inherit(struct server *src, struc
 
        if (server_tls_load_keypair(dst) == -1) {
                yyerror("failed to load public/private keys "
+                   "for server %s", dst->srv_conf.name);
+               serverconfig_free(&dst->srv_conf);
+               free(dst);
+               return (NULL);
+       }
+
+       if (server_tls_load_ca(dst) == -1) {
+               yyerror("failed to load ca cert(s) "
+                   "for server %s", dst->srv_conf.name);
+               serverconfig_free(&dst->srv_conf);
+               free(dst);
+               return (NULL);
+       }
+
+       if (server_tls_load_crl(dst) == -1) {
+               yyerror("failed to load crl(s) "
                    "for server %s", dst->srv_conf.name);
                serverconfig_free(&dst->srv_conf);
                free(dst);
Index: usr.sbin/httpd/server.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server.c,v
retrieving revision 1.110
diff -u -p -r1.110 server.c
--- usr.sbin/httpd/server.c     19 Jul 2017 17:36:25 -0000      1.110
+++ usr.sbin/httpd/server.c     30 Jul 2017 07:08:09 -0000
@@ -197,6 +197,40 @@ server_tls_load_ocsp(struct server *srv)
 }
 
 int
+server_tls_load_ca(struct server *srv)
+{
+       if ((srv->srv_conf.flags & SRVFLAG_TLS) == 0 ||
+           srv->srv_conf.tls_ca_file == NULL)
+               return (0);
+
+       if ((srv->srv_conf.tls_ca = tls_load_file(
+           srv->srv_conf.tls_ca_file,
+           &srv->srv_conf.tls_ca_len, NULL)) == NULL)
+               return (-1);
+       log_debug("%s: using ca cert(s) from %s", __func__,
+           srv->srv_conf.tls_ca_file);
+
+       return (0);
+}
+
+int
+server_tls_load_crl(struct server *srv)
+{
+       if ((srv->srv_conf.flags & SRVFLAG_TLS) == 0 ||
+           srv->srv_conf.tls_crl_file == NULL)
+               return (0);
+
+       if ((srv->srv_conf.tls_crl = tls_load_file(
+           srv->srv_conf.tls_crl_file,
+           &srv->srv_conf.tls_crl_len, NULL)) == NULL)
+               return (-1);
+       log_debug("%s: using crl(s) from %s", __func__,
+           srv->srv_conf.tls_crl_file);
+
+       return (0);
+}
+
+int
 server_tls_init(struct server *srv)
 {
        struct server_config *srv_conf;
@@ -254,6 +288,24 @@ server_tls_init(struct server *srv)
                return (-1);
        }
 
+       if (srv->srv_conf.tls_ca != NULL) {
+               if (tls_config_set_ca_mem(srv->srv_tls_config,
+                   srv->srv_conf.tls_ca, srv->srv_conf.tls_ca_len) != 0) {
+                       log_warnx("%s: failed to add ca cert(s)", __func__);
+                       return (-1);
+               }
+               tls_config_verify_client(srv->srv_tls_config);
+
+               if (srv->srv_conf.tls_crl != NULL) {
+                       if (tls_config_set_crl_mem(srv->srv_tls_config,
+                           srv->srv_conf.tls_crl,
+                           srv->srv_conf.tls_crl_len) != 0) {
+                               log_warnx("%s: failed to add crl(s)", __func__);
+                               return (-1);
+                       }
+               }
+       }
+
        TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
                if (srv_conf->tls_cert == NULL || srv_conf->tls_key == NULL)
                        continue;
@@ -267,6 +319,28 @@ server_tls_init(struct server *srv)
                        log_warnx("%s: failed to add tls keypair", __func__);
                        return (-1);
                }
+
+               if (srv->srv_conf.tls_ca == NULL)
+                       continue;
+
+               log_debug("%s: adding ca cert(s) for server %s", __func__,
+                   srv->srv_conf.name);
+               if (tls_config_set_ca_mem(srv->srv_tls_config,
+                   srv_conf->tls_ca, srv_conf->tls_ca_len) != 0) {
+                       log_warnx("%s: failed to add ca cert(s)", __func__);
+                       return (-1);
+               }
+
+               if (srv->srv_conf.tls_crl == NULL)
+                       continue;
+
+               log_debug("%s: adding crl(s) for server %s", __func__,
+                   srv->srv_conf.name);
+               if (tls_config_set_crl_mem(srv->srv_tls_config,
+                   srv_conf->tls_crl, srv_conf->tls_crl_len) != 0) {
+                       log_warnx("%s: failed to add crl(s)", __func__);
+                       return (-1);
+               }
        }
 
        /* set common session ID among all processes */
@@ -412,7 +486,11 @@ void
 serverconfig_free(struct server_config *srv_conf)
 {
        free(srv_conf->return_uri);
+       free(srv_conf->tls_ca_file);
+       free(srv_conf->tls_ca);
        free(srv_conf->tls_cert_file);
+       free(srv_conf->tls_crl_file);
+       free(srv_conf->tls_crl);
        free(srv_conf->tls_key_file);
        free(srv_conf->tls_ocsp_staple_file);
        free(srv_conf->tls_ocsp_staple);
@@ -425,8 +503,12 @@ serverconfig_reset(struct server_config 
 {
        srv_conf->auth = NULL;
        srv_conf->return_uri = NULL;
+       srv_conf->tls_ca = NULL;
+       srv_conf->tls_ca_file = NULL;
        srv_conf->tls_cert = NULL;
        srv_conf->tls_cert_file = NULL;
+       srv_conf->tls_crl = NULL;
+       srv_conf->tls_crl_file = NULL;
        srv_conf->tls_key = NULL;
        srv_conf->tls_key_file = NULL;
        srv_conf->tls_ocsp_staple = NULL;
Index: usr.sbin/httpd/server_fcgi.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_fcgi.c,v
retrieving revision 1.74
diff -u -p -r1.74 server_fcgi.c
--- usr.sbin/httpd/server_fcgi.c        21 Jan 2017 11:32:04 -0000      1.74
+++ usr.sbin/httpd/server_fcgi.c        30 Jul 2017 07:08:09 -0000
@@ -93,11 +93,12 @@ server_fcgi(struct httpd *env, struct cl
        struct fcgi_record_header       *h;
        struct fcgi_begin_request_body  *begin;
        char                             hbuf[HOST_NAME_MAX+1];
-       size_t                           scriptlen;
+       size_t                           chainlen, scriptlen;
        int                              pathlen;
        int                              fd = -1, ret;
        const char                      *stripped, *p, *alias, *errstr = NULL;
-       char                            *str, *script = NULL;
+       char                            *chainc, *chainp, *str, *script = NULL;
+       const uint8_t                   *chain;
 
        if (srv_conf->socket[0] == ':') {
                struct sockaddr_storage  ss;
@@ -282,11 +283,57 @@ server_fcgi(struct httpd *env, struct cl
                goto fail;
        }
 
-       if (srv_conf->flags & SRVFLAG_TLS)
+       if (srv_conf->flags & SRVFLAG_TLS) {
                if (fcgi_add_param(&param, "HTTPS", "on", clt) == -1) {
                        errstr = "failed to encode param";
                        goto fail;
                }
+               if (srv_conf->tls_ca != NULL) {
+                       chain = tls_peer_cert_chain_pem(clt->clt_tls_ctx,
+                           &chainlen);
+                       if ((chainc = strndup(chain, chainlen)) == NULL)
+                               goto fail;
+                       chainp = chainc;
+                       while ((chainp = strchr(chainp, '\n')) != NULL)
+                               *chainp = '\t';
+                       ret = fcgi_add_param(&param, "TLS_PEER_CHAIN", chainc,
+                           clt);
+                       free(chainc);
+                       if (ret == -1) {
+                               errstr = "failed to encode param";
+                               goto fail;
+                       }
+                       if (fcgi_add_param(&param, "TLS_PEER_SUBJECT",
+                           tls_peer_cert_subject(clt->clt_tls_ctx), clt) == -1
+                           || fcgi_add_param(&param, "TLS_PEER_ISSUER",
+                           tls_peer_cert_issuer(clt->clt_tls_ctx), clt) == -1
+                           || fcgi_add_param(&param, "TLS_PEER_HASH",
+                           tls_peer_cert_hash(clt->clt_tls_ctx), clt) == -1) {
+                               errstr = "failed to encode param";
+                               goto fail;
+                       }
+                       if (asprintf(&str, "%lld",
+                           tls_peer_cert_notafter(clt->clt_tls_ctx)) == -1
+                           || fcgi_add_param(&param, "TLS_PEER_EXPIRES", str,
+                           clt) == -1) {
+                               errstr = "failed to encode param";
+                               goto fail;
+                       }
+                       free(str);
+                       str = NULL;
+                       if (tls_peer_ocsp_url(clt->clt_tls_ctx) != NULL
+                           && fcgi_add_param(&param, "TLS_PEER_OCSP_URL",
+                           tls_peer_ocsp_url(clt->clt_tls_ctx), clt) == -1) {
+                               errstr = "failed to encode param";
+                               goto fail;
+                       }
+                       if (srv_conf->tls_crl != NULL && fcgi_add_param(&param,
+                           "TLS_PEER_CRL", "on", clt) == -1) {
+                               errstr = "failed to encode param";
+                               goto fail;
+                       }
+               }
+       }
 
        (void)print_host(&clt->clt_ss, hbuf, sizeof(hbuf));
        if (fcgi_add_param(&param, "REMOTE_ADDR", hbuf, clt) == -1) {
Index: regress/usr.sbin/httpd/tests/Client.pm
===================================================================
RCS file: /cvs/src/regress/usr.sbin/httpd/tests/Client.pm,v
retrieving revision 1.1
diff -u -p -r1.1 Client.pm
--- regress/usr.sbin/httpd/tests/Client.pm      16 Jul 2015 16:35:57 -0000      
1.1
+++ regress/usr.sbin/httpd/tests/Client.pm      30 Jul 2017 07:08:09 -0000
@@ -59,6 +59,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} ?
+                                       $self->{chroot}."/client.crt" : "",
+           SSL_key_file        => $self->{offertlscert} ?
+                                       $self->{chroot}."/client.key" : "",
        ) or die ref($self), " $iosocket socket connect failed: $!,$SSL_ERROR";
        print STDERR "connect sock: ",$cs->sockhost()," ",$cs->sockport(),"\n";
        print STDERR "connect peer: ",$cs->peerhost()," ",$cs->peerport(),"\n";
Index: regress/usr.sbin/httpd/tests/Httpd.pm
===================================================================
RCS file: /cvs/src/regress/usr.sbin/httpd/tests/Httpd.pm,v
retrieving revision 1.2
diff -u -p -r1.2 Httpd.pm
--- regress/usr.sbin/httpd/tests/Httpd.pm       30 Jan 2017 21:18:24 -0000      
1.2
+++ regress/usr.sbin/httpd/tests/Httpd.pm       30 Jul 2017 07:08:10 -0000
@@ -72,6 +72,8 @@ sub new {
            print $fh "\n";
            print $fh "\ttls certificate \"".$args{chroot}."/server.crt\"\n";
            print $fh "\ttls key \"".$args{chroot}."/server.key\"";
+           $self->{verifytls}
+               and print $fh "\ntls client ca \"".$args{chroot}."/ca.crt\"";
        }
        print $fh "\n\troot \"/\"";
        print $fh "\n\tlog style combined";
Index: regress/usr.sbin/httpd/tests/Makefile
===================================================================
RCS file: /cvs/src/regress/usr.sbin/httpd/tests/Makefile,v
retrieving revision 1.8
diff -u -p -r1.8 Makefile
--- regress/usr.sbin/httpd/tests/Makefile       14 Jul 2017 13:31:44 -0000      
1.8
+++ regress/usr.sbin/httpd/tests/Makefile       30 Jul 2017 07:08:10 -0000
@@ -77,10 +77,16 @@ ca.crt:
 server.req:
        openssl req -batch -new -subj 
/L=OpenBSD/O=httpd-regress/OU=server/CN=localhost/ -nodes -newkey rsa -keyout 
server.key -out server.req
 
+client.req:
+       openssl req -batch -new -subj 
/L=OpenBSD/O=httpd-regress/OU=client/CN=localhost/ -nodes -newkey rsa -keyout 
client.key -out $@
+
 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*tls*} ${REGRESS_TARGETS:M*https*}: server.crt
+client.crt: ca.crt client.req
+       openssl x509 -CAcreateserial -CAkey ca.key -CA ca.crt -req -in 
client.req -out $@
+
+${REGRESS_TARGETS:M*tls*} ${REGRESS_TARGETS:M*https*}: server.crt client.crt
 
 # make perl syntax check for all args files
 
Index: regress/usr.sbin/httpd/tests/args-tls-verify.pl
===================================================================
RCS file: regress/usr.sbin/httpd/tests/args-tls-verify.pl
diff -N regress/usr.sbin/httpd/tests/args-tls-verify.pl
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ regress/usr.sbin/httpd/tests/args-tls-verify.pl     30 Jul 2017 07:08:10 
-0000
@@ -0,0 +1,20 @@
+# test https connection, verifying client cert
+
+use strict;
+use warnings;
+
+our %args = (
+    client => {
+       tls => 1,
+       offertlscert => 1,
+       loggrep => 'Issuer.*/OU=ca/',
+    },
+    httpd => {
+       listentls => 1,
+       verifytls => 1,
+    },
+    len => 512,
+    md5 => path_md5("512")
+);
+
+1;

Reply via email to