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(¶m, "HTTPS", "on", clt) == -1) {
errstr = "failed to encode param";
goto fail;
}
+ if (srv_conf->tls_client_verify) {
+ 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;
+ }
+ 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 (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;
+ }
+ }
+ }
(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 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;