On Thu, 30 Mar 2017 22:17:46 +1030
Jack Burton <[email protected]> wrote:
> One of our sites has a need to require/verify TLS client certs,
> without the overhead & complexity of apache / nginx, etc.
> 
> OpenBSD's httpd seemed the obvious candidate, and I figured that the
> feature would be useful to others too -- see attached diff for an
> initial implementation.

Here's an updated diff.

Changes since my first diff are:

* passes through the whole client cert chain to fastcgi (as suggested
  by William Ahern) as TLS_PEER_CHAIN (since beck@'s 5 Apr libtls patch
  now lets us do that easily)

* uses chunked imsg transfers for setting/updating the client CA
  cert chain, as suggested by Jan Klemkow (but I couldn't get his
  implementation of that from last year to work reliably, so I rewrote
  it), to support long CA chains

* adds a regression test, also as suggested by Jan Klemkow (but keeps
  it separate from existing tls regression tests).

In & of itself, my diff still only adds support to httpd for verifying
client certificate authenticity (not for checking certificate
revocation status).

But at least it's now sufficiently complete (which my first diff
wasn't) to allow fastcgi responders to implement certificate revocation
status checking.

So I figured that this would be a good place to stop and ask for review
& approval or criticism of this diff (before returning to the question
of whether CRL [or whatever other form of client cert revocation
status] checking most "belongs" in libtls, in httpd or in the fastcgi
responders).


Index: usr.sbin/httpd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/config.c,v
retrieving revision 1.51
diff -u -p -r1.51 config.c
--- usr.sbin/httpd/config.c     25 Mar 2017 17:25:34 -0000      1.51
+++ usr.sbin/httpd/config.c     27 May 2017 06:14:27 -0000
@@ -326,6 +326,57 @@ config_settls(struct httpd *env, struct 
                }
        }
 
+       if (srv_conf->tls_ca_len != 0)
+               return config_settls_ca(env, srv);
+
+       return (0);
+}
+
+int
+config_settls_ca(struct httpd *env, struct server *srv)
+{
+       struct privsep          *ps = env->sc_ps;
+       struct server_config    *srv_conf = &srv->srv_conf;
+       struct tls_config        tls;
+       struct iovec             iov[2];
+       size_t                   c, chunk_len, i, sent;
+
+       if ((srv_conf->flags & SRVFLAG_TLS) == 0)
+               return (0);
+
+       log_debug("%s: configuring tls ca for %s", __func__, srv_conf->name);
+
+       if (srv_conf->tls_ca_len != 0) {
+               DPRINTF("%s: sending ca cert(s) for \"%s[%u]\" to %s fd %d",
+                   __func__, srv_conf->name, srv_conf->id,
+                   ps->ps_title[PROC_SERVER], srv->srv_s);
+
+               memset(&tls, 0, sizeof(tls));
+               tls.id = srv_conf->id;
+               tls.tls_ca_len = srv_conf->tls_ca_len;
+
+               chunk_len = MAX_IMSGSIZE - (ssize_t)IMSG_HEADER_SIZE - 
sizeof(tls);
+               for (sent = 0, i = 1; sent < tls.tls_ca_len;
+                   sent += chunk_len, i++) {
+                       if (tls.tls_ca_len - sent < chunk_len)
+                               chunk_len = tls.tls_ca_len - sent;
+                       tls.tls_ca_off = sent;
+                       c = 0;
+                       iov[c].iov_base = &tls;
+                       iov[c++].iov_len = sizeof(tls);
+                       iov[c].iov_base = srv_conf->tls_ca + sent;
+                       iov[c++].iov_len = chunk_len;
+
+                       if (proc_composev(ps, PROC_SERVER, IMSG_CFG_TLS_CA,
+                           iov, c) != 0) {
+                               log_warn("%s: failed to compose chunk %lu of "
+                                   "IMSG_CFG_TLS_CA imsg for `%s'", __func__,
+                                   i, srv_conf->name);
+                               return (-1);
+                       }
+               }
+       }
+
        return (0);
 }
 
@@ -644,6 +695,60 @@ config_gettls(struct httpd *env, struct 
                    tls_conf.tls_ocsp_staple_len)) == NULL)
                        goto fail;
                s += tls_conf.tls_ocsp_staple_len;
+       }
+
+       return (0);
+
+ fail:
+       return (-1);
+}
+
+int
+config_gettls_ca(struct httpd *env, struct imsg *imsg)
+{
+#ifdef DEBUG
+       struct privsep          *ps = env->sc_ps;
+#endif
+       struct server_config    *srv_conf = NULL;
+       struct tls_config        tls_conf;
+       uint8_t                 *p = imsg->data;
+       size_t                   s, chunk_len, min_chunk_len;
+
+       IMSG_SIZE_CHECK(imsg, &tls_conf);
+       memcpy(&tls_conf, p, sizeof(tls_conf));
+       s = sizeof(tls_conf);
+       chunk_len = MAX_IMSGSIZE - IMSG_HEADER_SIZE - s;
+       min_chunk_len = (tls_conf.tls_ca_len - tls_conf.tls_ca_off) > chunk_len
+           ? chunk_len : tls_conf.tls_ca_len - tls_conf.tls_ca_off;
+
+       if ((IMSG_DATA_SIZE(imsg) - s) < min_chunk_len ||
+               tls_conf.tls_ca_off >= tls_conf.tls_ca_len) {
+               log_debug("%s: invalid message length", __func__);
+               goto fail;
+       }
+
+       if ((srv_conf = serverconfig_byid(tls_conf.id)) == NULL) {
+               log_debug("%s: server not found", __func__);
+               goto fail;
+       }
+
+       DPRINTF("%s: %s %d tls ca configuration \"%s[%u]\" chunk %lu",
+           __func__, ps->ps_title[privsep_process], ps->ps_instance,
+           srv_conf->name, srv_conf->id,
+           (tls_conf.tls_ca_len - tls_conf.tls_ca_off) / chunk_len);
+
+       if (tls_conf.tls_ca_len != 0) {
+               if (tls_conf.tls_ca_off == 0) {
+                       free(srv_conf->tls_ca);
+                       if ((srv_conf->tls_ca = calloc(tls_conf.tls_ca_len + 1,
+                           sizeof(*srv_conf->tls_ca))) == NULL)
+                               goto fail;
+               }
+               if (tls_conf.tls_ca_len - tls_conf.tls_ca_off < chunk_len)
+                       chunk_len = tls_conf.tls_ca_len - tls_conf.tls_ca_off;
+               srv_conf->tls_ca_len = tls_conf.tls_ca_len;
+               memcpy(srv_conf->tls_ca + tls_conf.tls_ca_off, p + s,
+                   chunk_len);
        }
 
        return (0);
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 27 May 2017 06:14:27 -0000
@@ -342,6 +342,22 @@ 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_EXPIRES
+The time at which the TLS client 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.
@@ -511,6 +527,14 @@ Set the TLS configuration for the server
 These options are only used if TLS has been enabled via the listen directive.
 Valid options are:
 .Bl -tag -width Ds
+.It Ic ca Ar file
+Specify the CA certificate(s) for this server to use when verifying client
+certificates.
+The
+.Ar file
+should contain one or more PEM encoded CA certificates.
+The default is
+.Pa /etc/ssl/ca.crt .
 .It Ic certificate Ar file
 Specify the certificate to use for this server.
 The
@@ -571,6 +595,11 @@ session lifetime.
 It is possible to set
 .Ar seconds
 to default to use the httpd default timeout of 2 hours.
+.It Ic verify
+Require a valid TLS client certificate (checked using the CA certificate(s)
+specified by the
+.Ic tls ca
+option) for a TLS connection to proceed beyond the handshake.
 .El
 .El
 .Sh TYPES
Index: usr.sbin/httpd/httpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v
retrieving revision 1.131
diff -u -p -r1.131 httpd.h
--- usr.sbin/httpd/httpd.h      25 Mar 2017 17:25:34 -0000      1.131
+++ usr.sbin/httpd/httpd.h      27 May 2017 06:14:27 -0000
@@ -55,6 +55,7 @@
 #define HTTPD_ERROR_LOG                "error.log"
 #define HTTPD_DEFAULT_TYPE     { "bin", "application", "octet-stream", NULL }
 #define HTTPD_LOGVIS           VIS_NL|VIS_TAB|VIS_CSTYLE
+#define HTTPD_TLS_CA           "/etc/ssl/ca.crt"
 #define HTTPD_TLS_CERT         "/etc/ssl/server.crt"
 #define HTTPD_TLS_KEY          "/etc/ssl/private/server.key"
 #define HTTPD_TLS_CIPHERS      "compat"
@@ -218,6 +219,7 @@ enum imsg_type {
        IMSG_CTL_REOPEN,
        IMSG_CFG_SERVER,
        IMSG_CFG_TLS,
+       IMSG_CFG_TLS_CA,
        IMSG_CFG_MEDIA,
        IMSG_CFG_AUTH,
        IMSG_CFG_DONE,
@@ -475,10 +477,14 @@ 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;
        char                     tls_ciphers[NAME_MAX];
+       int                      tls_client_verify;
        char                     tls_dhe_params[NAME_MAX];
        char                     tls_ecdhe_curve[NAME_MAX];
        uint8_t                 *tls_key;
@@ -521,9 +527,12 @@ TAILQ_HEAD(serverhosts, server_config);
 struct tls_config {
        uint32_t                 id;
 
+       size_t                   tls_ca_len;
        size_t                   tls_cert_len;
        size_t                   tls_key_len;
        size_t                   tls_ocsp_staple_len;
+
+       size_t                   tls_ca_off;
 };
 
 struct server {
@@ -586,6 +595,7 @@ 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_keypair(struct server *);
 int     server_tls_load_ocsp(struct server *);
 void    server_generate_ticket_key(struct server_config *);
@@ -787,8 +797,10 @@ int         config_getreset(struct httpd *, str
 int     config_getcfg(struct httpd *, struct imsg *);
 int     config_setserver(struct httpd *, struct server *);
 int     config_settls(struct httpd *, struct server *);
+int     config_settls_ca(struct httpd *, struct server *);
 int     config_getserver(struct httpd *, struct imsg *);
 int     config_gettls(struct httpd *, struct imsg *);
+int     config_gettls_ca(struct httpd *, struct imsg *);
 int     config_setmedia(struct httpd *, struct media_type *);
 int     config_getmedia(struct httpd *, struct imsg *);
 int     config_setauth(struct httpd *, struct auth *);
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      27 May 2017 06:14:28 -0000
@@ -129,11 +129,12 @@ typedef struct {
 
 %}
 
-%token ACCESS ALIAS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT CIPHERS COMMON
-%token COMBINED CONNECTION DHE DIRECTORY ECDHE ERR FCGI INDEX IP KEY LIFETIME
-%token LISTEN LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY OCSP ON PORT PREFORK
-%token PROTOCOLS REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP TICKET
-%token TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD REQUEST
+%token ACCESS ALIAS AUTO BACKLOG BODY BUFFER CA CERTIFICATE CHROOT CIPHERS
+%token COMMON COMBINED CONNECTION DHE DIRECTORY ECDHE ERR FCGI INDEX IP KEY
+%token LIFETIME LISTEN LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY OCSP ON
+%token PORT PREFORK PROTOCOLS REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE
+%token SYSLOG TCP TICKET TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS VERIFY
+%token DEFAULT PRELOAD REQUEST
 %token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
@@ -254,6 +255,9 @@ server              : SERVER optmatch STRING        {
                                s->srv_conf.flags |= SRVFLAG_SERVER_MATCH;
                        s->srv_conf.logformat = LOG_FORMAT_COMMON;
                        s->srv_conf.tls_protocols = TLS_PROTOCOLS_DEFAULT;
+                       if ((s->srv_conf.tls_ca_file =
+                          strdup(HTTPD_TLS_CA)) == NULL)
+                               fatal("out of memory");
                        if ((s->srv_conf.tls_cert_file =
                            strdup(HTTPD_TLS_CERT)) == NULL)
                                fatal("out of memory");
@@ -344,6 +348,14 @@ 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_ocsp(srv) == -1) {
                                yyerror("server \"%s\": failed to load "
                                    "ocsp staple", srv->srv_conf.name);
@@ -727,6 +739,15 @@ tlsopts            : CERTIFICATE STRING            {
                                fatal("out of memory");
                        free($2);
                }
+               | CA STRING                     {
+                       free(srv_conf->tls_ca_file);
+                       if ((srv_conf->tls_ca_file = strdup($2)) == NULL)
+                               fatal("out of memory");
+                       free($2);
+               }
+               | VERIFY                        {
+                       srv_conf->tls_client_verify = 1;
+               }
                | CIPHERS STRING                {
                        if (strlcpy(srv_conf->tls_ciphers, $2,
                            sizeof(srv_conf->tls_ciphers)) >=
@@ -1217,6 +1238,7 @@ lookup(char *s)
                { "block",              BLOCK },
                { "body",               BODY },
                { "buffer",             BUFFER },
+               { "ca",                 CA },
                { "certificate",        CERTIFICATE },
                { "chroot",             CHROOT },
                { "ciphers",            CIPHERS },
@@ -1269,6 +1291,7 @@ lookup(char *s)
                { "tls",                TLS },
                { "type",               TYPE },
                { "types",              TYPES },
+               { "verify",             VERIFY },
                { "with",               WITH }
        };
        const struct keywords   *p;
@@ -2043,6 +2066,9 @@ server_inherit(struct server *src, struc
 
        /* Copy the source server and assign a new Id */
        memcpy(&dst->srv_conf, &src->srv_conf, sizeof(dst->srv_conf));
+       if ((dst->srv_conf.tls_ca_file =
+           strdup(src->srv_conf.tls_ca_file)) == NULL)
+               fatal("out of memory");
        if ((dst->srv_conf.tls_cert_file =
            strdup(src->srv_conf.tls_cert_file)) == NULL)
                fatal("out of memory");
@@ -2080,6 +2106,7 @@ server_inherit(struct server *src, struc
                dst->srv_conf.flags |= SRVFLAG_TLS;
        else
                dst->srv_conf.flags &= ~SRVFLAG_TLS;
+       dst->srv_conf.tls_client_verify = src->srv_conf.tls_client_verify;
 
        /* Don't inherit the "match" option, use it from the alias */
        dst->srv_conf.flags &= ~SRVFLAG_SERVER_MATCH;
@@ -2087,6 +2114,14 @@ 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);
Index: usr.sbin/httpd/server.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server.c,v
retrieving revision 1.109
diff -u -p -r1.109 server.c
--- usr.sbin/httpd/server.c     17 Apr 2017 21:58:27 -0000      1.109
+++ usr.sbin/httpd/server.c     27 May 2017 06:14:28 -0000
@@ -197,6 +197,26 @@ 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_client_verify == 0)
+               return (0);
+
+       if (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_init(struct server *srv)
 {
        struct server_config *srv_conf;
@@ -254,6 +274,15 @@ server_tls_init(struct server *srv)
                return (-1);
        }
 
+       if (srv->srv_conf.tls_ca != NULL && srv->srv_conf.tls_client_verify) {
+               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);
+       }
+
        TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
                if (srv_conf->tls_cert == NULL || srv_conf->tls_key == NULL)
                        continue;
@@ -267,6 +296,16 @@ server_tls_init(struct server *srv)
                        log_warnx("%s: failed to add tls keypair", __func__);
                        return (-1);
                }
+               if (srv_conf->tls_ca != NULL) {
+                       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);
+                       }
+               }
        }
 
        /* set common session ID among all processes */
@@ -412,6 +451,8 @@ 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_key_file);
        free(srv_conf->tls_ocsp_staple_file);
@@ -425,6 +466,8 @@ 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_key = NULL;
@@ -1281,6 +1324,9 @@ server_dispatch_parent(int fd, struct pr
                break;
        case IMSG_CFG_TLS:
                config_gettls(httpd_env, imsg);
+               break;
+       case IMSG_CFG_TLS_CA:
+               config_gettls_ca(httpd_env, imsg);
                break;
        case IMSG_CFG_DONE:
                config_getcfg(httpd_env, imsg);
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        27 May 2017 06:14:29 -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,52 @@ 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_client_verify) {
+                       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;
+                       }
+                       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 (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;
+                       }
+               }
+       }
 
        (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      27 May 2017 06:14:33 -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       27 May 2017 06:14:33 -0000
@@ -72,6 +72,10 @@ sub new {
            print $fh "\n";
            print $fh "\ttls certificate \"".$args{chroot}."/server.crt\"\n";
            print $fh "\ttls key \"".$args{chroot}."/server.key\"";
+           if ($self->{verifytls}) {
+               print $fh "\ntls ca \"".$args{chroot}."/ca.crt\"";
+               print $fh "\ntls verify";
+           }
        }
        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.7
diff -u -p -r1.7 Makefile
--- regress/usr.sbin/httpd/tests/Makefile       30 Jan 2017 21:18:24 -0000      
1.7
+++ regress/usr.sbin/httpd/tests/Makefile       27 May 2017 06:14:33 -0000
@@ -80,10 +80,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     27 May 2017 06:14:33 
-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