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(¶m, "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(¶m, "TLS_PEER_CHAIN", chainc, + clt); + free(chainc); + if (ret == -1) { + errstr = "failed to encode param"; + goto fail; + } + if (fcgi_add_param(¶m, "TLS_PEER_SUBJECT", + tls_peer_cert_subject(clt->clt_tls_ctx), clt) == -1 + || fcgi_add_param(¶m, "TLS_PEER_ISSUER", + tls_peer_cert_issuer(clt->clt_tls_ctx), clt) == -1 + || fcgi_add_param(¶m, "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(¶m, "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(¶m, "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(¶m, + "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(¶m, "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;