ping.
Tested with current source status.
On Tue, May 10, 2016 at 06:59:36PM +0200, Jan Klemkow wrote:
> Hi,
>
> This diff adds client side certificate checks to httpd. Most parts are
> straight forward. But, to transfer the whole certificate authority
> store to the server process through the imsg infrastructure I had to
> change this in an chunked transfer.
>
> The Documentation of this feature is included in httpd.conf.5.
>
> This time, I also extend an httpd tls regression tests to use a client
> side certificate. All tests pass :-)
>
> If anything is wrong with this diff, just notify me, I will fix it.
>
> Bye,
> Jan
>
> Index: config.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/httpd/config.c,v
> retrieving revision 1.45
> diff -u -p -r1.45 config.c
> --- config.c 28 Apr 2016 14:20:11 -0000 1.45
> +++ config.c 3 May 2016 19:51:57 -0000
> @@ -236,51 +236,59 @@ config_settls(struct httpd *env, struct
> struct server_config *srv_conf = &srv->srv_conf;
> struct tls_config tls;
> struct iovec iov[2];
> - size_t c;
> + size_t n;
> + char *p;
>
> if ((srv_conf->flags & SRVFLAG_TLS) == 0)
> return (0);
>
> log_debug("%s: configuring TLS for %s", __func__, srv_conf->name);
>
> - if (srv_conf->tls_cert_len != 0) {
> - DPRINTF("%s: sending TLS cert \"%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_cert_len = srv_conf->tls_cert_len;
> -
> - c = 0;
> - iov[c].iov_base = &tls;
> - iov[c++].iov_len = sizeof(tls);
> - iov[c].iov_base = srv_conf->tls_cert;
> - iov[c++].iov_len = srv_conf->tls_cert_len;
> + if ((p = malloc(srv_conf->tls_ca_len + srv_conf->tls_cert_len +
> + srv_conf->tls_key_len)) == NULL) {
> + log_warn("%s: failed to compose IMSG_CFG_TLS imsg for "
> + "`%s'", __func__, srv_conf->name);
> + return (-1);
> + }
>
> - if (proc_composev(ps, PROC_SERVER, IMSG_CFG_TLS, iov, c) != 0) {
> - log_warn("%s: failed to compose IMSG_CFG_TLS imsg for "
> - "`%s'", __func__, srv_conf->name);
> - return (-1);
> - }
> + memcpy(p, srv_conf->tls_ca, srv_conf->tls_ca_len);
> + n = srv_conf->tls_ca_len;
> + memcpy(p + n, srv_conf->tls_cert, srv_conf->tls_cert_len);
> + n += srv_conf->tls_cert_len;
> + memcpy(p + n, srv_conf->tls_key, srv_conf->tls_key_len);
> + n += srv_conf->tls_key_len;
> +
> + /* send tls_config structure */
> + memset(&tls, 0, sizeof(tls));
> + tls.id = srv_conf->id;
> + tls.port = srv_conf->port;
> + memcpy(&tls.ss, &srv_conf->ss, sizeof(tls.ss));
> + tls.tls_ca_optional = srv_conf->tls_ca_optional;
> + tls.tls_ca_len = srv_conf->tls_ca_len;
> + tls.tls_cert_len = srv_conf->tls_cert_len;
> + tls.tls_key_len = srv_conf->tls_key_len;
> +
> + if (proc_compose(ps, PROC_SERVER, IMSG_CFG_TLS, &tls, sizeof(tls)) !=
> 0) {
> + log_warn("%s: failed to compose IMSG_CFG_TLS imsg for "
> + "`%s'", __func__, srv_conf->name);
> + return (-1);
> }
>
> - if (srv_conf->tls_key_len != 0) {
> - DPRINTF("%s: sending TLS key \"%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_key_len = srv_conf->tls_key_len;
> -
> - c = 0;
> - iov[c].iov_base = &tls;
> - iov[c++].iov_len = sizeof(tls);
> - iov[c].iov_base = srv_conf->tls_key;
> - iov[c++].iov_len = srv_conf->tls_key_len;
> + iov[0].iov_base = &tls;
> + iov[0].iov_len = sizeof(tls);
> +
> +#define MSG_CHUNK_SIZE (MAX_IMSGSIZE - (ssize_t)IMSG_HEADER_SIZE -
> iov[0].iov_len)
> +
> + /* transfer in chunks */
> + for (; n > 0; p += MSG_CHUNK_SIZE) {
> + size_t len = n > MSG_CHUNK_SIZE ? MSG_CHUNK_SIZE : n;
> + n -= len;
>
> - if (proc_composev(ps, PROC_SERVER, IMSG_CFG_TLS, iov, c) != 0) {
> + iov[1].iov_base = p;
> + iov[1].iov_len = len;
> +
> + if (proc_composev(ps, PROC_SERVER, IMSG_CFG_TLS_CHNK, iov, 2)
> + != 0) {
> log_warn("%s: failed to compose IMSG_CFG_TLS imsg for "
> "`%s'", __func__, srv_conf->name);
> return (-1);
> @@ -555,6 +563,52 @@ config_getserver(struct httpd *env, stru
> }
>
> int
> +config_gettls_chunk(struct httpd *env, struct imsg *imsg)
> +{
> + struct server_config *srv_conf = NULL;
> + struct tls_config tls_conf;
> + uint8_t *p = imsg->data;
> + size_t s = 0;
> +
> + IMSG_SIZE_CHECK(imsg, &tls_conf);
> + memcpy(&tls_conf, p, sizeof(tls_conf));
> + s = sizeof(tls_conf);
> +
> + if ((srv_conf = serverconfig_byid(tls_conf.id)) == NULL) {
> + log_debug("%s: server not found", __func__);
> + goto fail;
> + }
> +
> +#define CHUNK_CPY(dst, dst_len, dst_size, src, src_size, src_off) \
> + do { \
> + if (dst_len < dst_size) { \
> + size_t n = dst_size - dst_len; \
> + if (n > src_size - src_off) \
> + n = src_size - src_off; \
> + memcpy(dst + dst_len, src + src_off, n); \
> + dst_len += n; \
> + src_off += n; \
> + } \
> + } while(0)
> +
> + CHUNK_CPY(srv_conf->tls_ca, srv_conf->tls_ca_len,
> + srv_conf->tls_ca_size, p, IMSG_DATA_SIZE(imsg), s);
> +
> + CHUNK_CPY(srv_conf->tls_cert, srv_conf->tls_cert_len,
> + srv_conf->tls_cert_size, p, IMSG_DATA_SIZE(imsg), s);
> +
> + CHUNK_CPY(srv_conf->tls_key, srv_conf->tls_key_len,
> + srv_conf->tls_key_size, p, IMSG_DATA_SIZE(imsg), s);
> +
> +#undef CHUNK_CPY
> +
> + return (0);
> +
> + fail:
> + return (-1);
> +}
> +
> +int
> config_gettls(struct httpd *env, struct imsg *imsg)
> {
> #ifdef DEBUG
> @@ -569,12 +623,6 @@ config_gettls(struct httpd *env, struct
> memcpy(&tls_conf, p, sizeof(tls_conf));
> s = sizeof(tls_conf);
>
> - if ((IMSG_DATA_SIZE(imsg) - s) <
> - (tls_conf.tls_cert_len + tls_conf.tls_key_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;
> @@ -584,20 +632,25 @@ config_gettls(struct httpd *env, struct
> ps->ps_title[privsep_process], ps->ps_instance,
> srv_conf->name, srv->srv_conf.id);
>
> - if (tls_conf.tls_cert_len != 0) {
> - srv_conf->tls_cert_len = tls_conf.tls_cert_len;
> - if ((srv_conf->tls_cert = get_data(p + s,
> - tls_conf.tls_cert_len)) == NULL)
> - goto fail;
> - s += tls_conf.tls_cert_len;
> - }
> - if (tls_conf.tls_key_len != 0) {
> - srv_conf->tls_key_len = tls_conf.tls_key_len;
> - if ((srv_conf->tls_key = get_data(p + s,
> - tls_conf.tls_key_len)) == NULL)
> - goto fail;
> - s += tls_conf.tls_key_len;
> - }
> + srv_conf->tls_ca_optional = tls_conf.tls_ca_optional;
> + srv_conf->tls_ca_len = 0;
> + srv_conf->tls_cert_len = 0;
> + srv_conf->tls_key_len = 0;
> +
> + srv_conf->tls_ca_size = tls_conf.tls_ca_len;
> + srv_conf->tls_cert_size = tls_conf.tls_cert_len;
> + srv_conf->tls_key_size = tls_conf.tls_key_len;
> +
> + free(srv_conf->tls_ca);
> + free(srv_conf->tls_cert);
> + free(srv_conf->tls_key);
> +
> + if ((srv_conf->tls_ca = malloc(tls_conf.tls_ca_len)) == NULL)
> + goto fail;
> + if ((srv_conf->tls_cert = malloc(tls_conf.tls_cert_len)) == NULL)
> + goto fail;
> + if ((srv_conf->tls_key = malloc(tls_conf.tls_key_len)) == NULL)
> + goto fail;
>
> return (0);
>
> Index: httpd.conf.5
> ===================================================================
> RCS file: /cvs/src/usr.sbin/httpd/httpd.conf.5,v
> retrieving revision 1.73
> diff -u -p -r1.73 httpd.conf.5
> --- httpd.conf.5 9 May 2016 19:36:54 -0000 1.73
> +++ httpd.conf.5 10 May 2016 16:18:04 -0000
> @@ -498,6 +498,13 @@ 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 Oo Ic optional Oc
> +Specify a certificate authority for verifying client side certificates.
> +.Ar file
> +should contain a PEM encoded certificate of the certificate authority.
> +The parameter
> +.Ic optional
> +does not requiring the client to send a certificate.
> .It Ic certificate Ar file
> Specify the certificate to use for this server.
> The
> Index: httpd.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v
> retrieving revision 1.103
> diff -u -p -r1.103 httpd.h
> --- httpd.h 28 Apr 2016 14:20:11 -0000 1.103
> +++ httpd.h 7 May 2016 02:53:08 -0000
> @@ -203,6 +203,7 @@ enum imsg_type {
> IMSG_CTL_REOPEN,
> IMSG_CFG_SERVER,
> IMSG_CFG_TLS,
> + IMSG_CFG_TLS_CHNK,
> IMSG_CFG_MEDIA,
> IMSG_CFG_AUTH,
> IMSG_CFG_DONE,
> @@ -424,14 +425,21 @@ struct server_config {
> uint32_t maxrequests;
> size_t maxrequestbody;
>
> + uint8_t *tls_ca;
> + size_t tls_ca_len;
> + size_t tls_ca_size;
> + char *tls_ca_file;
> + uint8_t tls_ca_optional;
> uint8_t *tls_cert;
> size_t tls_cert_len;
> + size_t tls_cert_size;
> char *tls_cert_file;
> char tls_ciphers[NAME_MAX];
> char tls_dhe_params[NAME_MAX];
> char tls_ecdhe_curve[NAME_MAX];
> uint8_t *tls_key;
> size_t tls_key_len;
> + size_t tls_key_size;
> char *tls_key_file;
> uint32_t tls_protocols;
>
> @@ -465,8 +473,15 @@ TAILQ_HEAD(serverhosts, server_config);
> struct tls_config {
> uint32_t id;
>
> + in_port_t port;
> + struct sockaddr_storage ss;
> +
> + int tls_ca_optional;
> + size_t tls_ca_len;
> size_t tls_cert_len;
> size_t tls_key_len;
> +
> + size_t chunk_size;
> };
>
> struct server {
> @@ -720,6 +735,7 @@ int config_setserver(struct httpd *, st
> int config_settls(struct httpd *, struct server *);
> int config_getserver(struct httpd *, struct imsg *);
> int config_gettls(struct httpd *, struct imsg *);
> +int config_gettls_chunk(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: parse.y
> ===================================================================
> RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
> retrieving revision 1.77
> diff -u -p -r1.77 parse.y
> --- parse.y 22 Nov 2015 13:27:13 -0000 1.77
> +++ parse.y 7 May 2016 02:45:39 -0000
> @@ -129,12 +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
> LISTEN
> -%token LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY ON PORT PREFORK
> PROTOCOLS
> -%token REQUEST REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP
> TIMEOUT
> -%token TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD
> -%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS
> +%token ACCESS ALIAS AUTO BACKLOG BODY BUFFER CA CERTIFICATE CHROOT
> CIPHERS
> +%token COMMON COMBINED CONNECTION DHE DIRECTORY ECDHE ERR FCGI INDEX
> IP KEY
> +%token LISTEN LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY ON PORT
> PREFORK
> +%token PROTOCOLS REQUEST REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE
> SYSLOG
> +%token TCP TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT
> PRELOAD
> +%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS OPTIONAL
> %token <v.string> STRING
> %token <v.number> NUMBER
> %type <v.port> port
> @@ -244,6 +244,7 @@ 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;
> + s->srv_conf.tls_ca_file = NULL;
> if ((s->srv_conf.tls_cert_file =
> strdup(HTTPD_TLS_CERT)) == NULL)
> fatal("out of memory");
> @@ -315,7 +316,7 @@ server : SERVER optmatch STRING {
> }
>
> if (server_tls_load_keypair(srv) == -1) {
> - yyerror("failed to load public/private keys "
> + yyerror("XXfailed to load public/private keys "
> "for server %s", srv->srv_conf.name);
> serverconfig_free(srv_conf);
> free(srv);
> @@ -663,7 +664,21 @@ tlsopts_l : tlsopts optcommanl tlsopts_l
> | tlsopts optnl
> ;
>
> -tlsopts : CERTIFICATE STRING {
> +tlsopts : CA STRING {
> + srv_conf->tls_ca_optional = 0;
> + free(srv_conf->tls_ca_file);
> + if ((srv_conf->tls_ca_file = strdup($2)) == NULL)
> + fatal("out of memory");
> + free($2);
> + }
> + | CA STRING OPTIONAL {
> + srv_conf->tls_ca_optional = 1;
> + free(srv_conf->tls_ca_file);
> + if ((srv_conf->tls_ca_file = strdup($2)) == NULL)
> + fatal("out of memory");
> + free($2);
> + }
> + | CERTIFICATE STRING {
> free(srv_conf->tls_cert_file);
> if ((srv_conf->tls_cert_file = strdup($2)) == NULL)
> fatal("out of memory");
> @@ -1148,6 +1163,7 @@ lookup(char *s)
> { "block", BLOCK },
> { "body", BODY },
> { "buffer", BUFFER },
> + { "ca", CA},
> { "certificate", CERTIFICATE },
> { "chroot", CHROOT },
> { "ciphers", CIPHERS },
> @@ -1176,6 +1192,7 @@ lookup(char *s)
> { "no", NO },
> { "nodelay", NODELAY },
> { "on", ON },
> + { "optional", OPTIONAL },
> { "pass", PASS },
> { "port", PORT },
> { "prefork", PREFORK },
> @@ -1970,16 +1987,26 @@ 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 (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 ((dst->srv_conf.tls_cert_file =
> strdup(src->srv_conf.tls_cert_file)) == NULL)
> fatal("out of memory");
> if ((dst->srv_conf.tls_key_file =
> strdup(src->srv_conf.tls_key_file)) == NULL)
> fatal("out of memory");
> + dst->srv_conf.tls_ca = NULL;
> dst->srv_conf.tls_cert = NULL;
> dst->srv_conf.tls_key = NULL;
> + dst->srv_conf.tls_ca_len = 0;
> dst->srv_conf.tls_cert_len = 0;
> dst->srv_conf.tls_key_len = 0;
> + dst->srv_conf.tls_ca_size = 0;
> + dst->srv_conf.tls_cert_size = 0;
> + dst->srv_conf.tls_key_size = 0;
>
> if (src->srv_conf.return_uri != NULL &&
> (dst->srv_conf.return_uri =
> @@ -2012,7 +2039,7 @@ server_inherit(struct server *src, struc
> dst->srv_conf.flags |= (alias->flags & SRVFLAG_SERVER_MATCH);
>
> if (server_tls_load_keypair(dst) == -1) {
> - yyerror("failed to load public/private keys "
> + yyerror("YYfailed to load public/private keys "
> "for server %s", dst->srv_conf.name);
> serverconfig_free(&dst->srv_conf);
> free(dst);
> Index: server.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/httpd/server.c,v
> retrieving revision 1.85
> diff -u -p -r1.85 server.c
> --- server.c 28 Apr 2016 17:18:06 -0000 1.85
> +++ server.c 7 May 2016 22:58:20 -0000
> @@ -138,6 +138,15 @@ server_tls_load_keypair(struct server *s
> if ((srv->srv_conf.flags & SRVFLAG_TLS) == 0)
> return (0);
>
> + if (srv->srv_conf.tls_ca_file != NULL) {
> + 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 certificate authority %s", __func__,
> + srv->srv_conf.tls_ca_file);
> + }
> +
> if ((srv->srv_conf.tls_cert = tls_load_file(
> srv->srv_conf.tls_cert_file, &srv->srv_conf.tls_cert_len,
> NULL)) == NULL)
> @@ -199,6 +208,20 @@ server_tls_init(struct server *srv)
> return (-1);
> }
>
> + if (srv->srv_conf.tls_ca_len > 0) {
> + log_warnx("%s: set tls ca len: %zu", __func__,
> + srv->srv_conf.tls_ca_len);
> + 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 set tls ca", __func__);
> + return (-1);
> + }
> + if (srv->srv_conf.tls_ca_optional)
> + tls_config_verify_client_optional(srv->srv_tls_config);
> + else
> + tls_config_verify_client(srv->srv_tls_config);
> + }
> +
> if (tls_config_set_keypair_mem(srv->srv_tls_config,
> srv->srv_conf.tls_cert, srv->srv_conf.tls_cert_len,
> srv->srv_conf.tls_key, srv->srv_conf.tls_key_len) != 0) {
> @@ -215,12 +238,16 @@ server_tls_init(struct server *srv)
>
> /* We're now done with the public/private key... */
> tls_config_clear_keys(srv->srv_tls_config);
> + explicit_bzero(srv->srv_conf.tls_ca, srv->srv_conf.tls_ca_len);
> explicit_bzero(srv->srv_conf.tls_cert, srv->srv_conf.tls_cert_len);
> explicit_bzero(srv->srv_conf.tls_key, srv->srv_conf.tls_key_len);
> + free(srv->srv_conf.tls_ca);
> free(srv->srv_conf.tls_cert);
> free(srv->srv_conf.tls_key);
> + srv->srv_conf.tls_ca = NULL;
> srv->srv_conf.tls_cert = NULL;
> srv->srv_conf.tls_key = NULL;
> + srv->srv_conf.tls_ca_len = 0;
> srv->srv_conf.tls_cert_len = 0;
> srv->srv_conf.tls_key_len = 0;
>
> @@ -317,9 +344,15 @@ void
> serverconfig_free(struct server_config *srv_conf)
> {
> free(srv_conf->return_uri);
> + free(srv_conf->tls_ca_file);
> free(srv_conf->tls_cert_file);
> free(srv_conf->tls_key_file);
>
> + if (srv_conf->tls_ca != NULL) {
> + explicit_bzero(srv_conf->tls_ca, srv_conf->tls_ca_len);
> + free(srv_conf->tls_ca);
> + }
> +
> if (srv_conf->tls_cert != NULL) {
> explicit_bzero(srv_conf->tls_cert, srv_conf->tls_cert_len);
> free(srv_conf->tls_cert);
> @@ -336,6 +369,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;
> @@ -1132,6 +1167,9 @@ server_dispatch_parent(int fd, struct pr
> break;
> case IMSG_CFG_TLS:
> config_gettls(env, imsg);
> + break;
> + case IMSG_CFG_TLS_CHNK:
> + config_gettls_chunk(env, imsg);
> break;
> case IMSG_CFG_DONE:
> config_getcfg(env, imsg);
> Index: 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
> --- tests/Client.pm 16 Jul 2015 16:35:57 -0000 1.1
> +++ tests/Client.pm 10 May 2016 16:09:23 -0000
> @@ -59,6 +59,9 @@ sub child {
> PeerAddr => $self->{connectaddr},
> PeerPort => $self->{connectport},
> SSL_verify_mode => SSL_VERIFY_NONE,
> + SSL_use_cert => $self->{clientcert} ? 1 : 0,
> + SSL_cert_file => $self->{clientcert} ?
> $self->{chroot}."/client.crt" : "",
> + SSL_key_file => $self->{clientcert} ?
> $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: tests/Httpd.pm
> ===================================================================
> RCS file: /cvs/src/regress/usr.sbin/httpd/tests/Httpd.pm,v
> retrieving revision 1.1
> diff -u -p -r1.1 Httpd.pm
> --- tests/Httpd.pm 16 Jul 2015 16:35:57 -0000 1.1
> +++ tests/Httpd.pm 10 May 2016 16:03:01 -0000
> @@ -71,6 +71,9 @@ sub new {
> print $fh "\n";
> print $fh "\ttls certificate \"".$args{chroot}."/server.crt\"\n";
> print $fh "\ttls key \"".$args{chroot}."/server.key\"";
> + if ($self->{clientcert}) {
> + print $fh "\n\ttls ca \"".$args{chroot}."/ca.crt\"";
> + }
> }
> print $fh "\n\troot \"/\"";
> print $fh "\n\tlog style combined";
> Index: tests/Makefile
> ===================================================================
> RCS file: /cvs/src/regress/usr.sbin/httpd/tests/Makefile,v
> retrieving revision 1.2
> diff -u -p -r1.2 Makefile
> --- tests/Makefile 16 Jul 2015 17:00:41 -0000 1.2
> +++ tests/Makefile 10 May 2016 14:18:13 -0000
> @@ -88,7 +88,13 @@ 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*tls*} ${REGRESS_TARGETS:M*https*}: server.crt
> +client.req:
> + openssl req -batch -new -subj
> /L=OpenBSD/O=httpd-regress/OU=client/CN=user/ -nodes -newkey rsa -keyout
> client.key -out client.req
> +
> +client.crt: ca.crt client.req
> + openssl x509 -CAcreateserial -CAkey ca.key -CA ca.crt -req -in
> client.req -out client.crt
> +
> +${REGRESS_TARGETS:M*tls*} ${REGRESS_TARGETS:M*https*}: server.crt client.crt
>
> # make perl syntax check for all args files
>
> Index: tests/args-tls.pl
> ===================================================================
> RCS file: /cvs/src/regress/usr.sbin/httpd/tests/args-tls.pl,v
> retrieving revision 1.1
> diff -u -p -r1.1 args-tls.pl
> --- tests/args-tls.pl 16 Jul 2015 16:35:57 -0000 1.1
> +++ tests/args-tls.pl 10 May 2016 16:09:35 -0000
> @@ -7,9 +7,11 @@ our %args = (
> client => {
> tls => 1,
> loggrep => 'Issuer.*/OU=ca/',
> + clientcert => 1,
> },
> httpd => {
> listentls => 1,
> + clientcert => 1,
> },
> len => 512,
> md5 => path_md5("512")
>