ping On Fri, Jan 15, 2016 at 12:54:55PM +0100, 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. Documentation of this > feature in httpd.conf.5 is included. > > I tested this diff with certificates created by this script: > https://github.com/younix/ucspi/blob/master/tests.mk > > and with firefox, chromium and 'openssl s_client...'. > > 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.44 > diff -u -p -r1.44 config.c > --- config.c 2 Dec 2015 15:13:00 -0000 1.44 > +++ config.c 15 Jan 2016 11:15:11 -0000 > @@ -235,55 +235,57 @@ config_settls(struct httpd *env, struct > struct privsep *ps = env->sc_ps; > struct tls_config tls; > struct iovec iov[2]; > - size_t c; > + size_t n; > + char *p; > > if ((srv->srv_conf.flags & SRVFLAG_TLS) == 0) > return (0); > > - log_debug("%s: configuring TLS for %s", __func__, srv->srv_conf.name); > - > - if (srv->srv_conf.tls_cert_len != 0) { > - DPRINTF("%s: sending TLS cert \"%s[%u]\" to %s fd %d", __func__, > - srv->srv_conf.name, srv->srv_conf.id, > - ps->ps_title[PROC_SERVER], srv->srv_s); > - > - memset(&tls, 0, sizeof(tls)); > - tls.id = srv->srv_conf.id; > - tls.port = srv->srv_conf.port; > - memcpy(&tls.ss, &srv->srv_conf.ss, sizeof(tls.ss)); > - tls.tls_cert_len = srv->srv_conf.tls_cert_len; > - > - c = 0; > - iov[c].iov_base = &tls; > - iov[c++].iov_len = sizeof(tls); > - iov[c].iov_base = srv->srv_conf.tls_cert; > - iov[c++].iov_len = srv->srv_conf.tls_cert_len; > + if ((p = malloc(srv->srv_conf.tls_ca_len + srv->srv_conf.tls_cert_len + > + srv->srv_conf.tls_key_len)) == NULL) { > + log_warn("%s: failed to compose IMSG_CFG_TLS imsg for " > + "`%s'", __func__, srv->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->srv_conf.name); > - return (-1); > - } > + memcpy(p, srv->srv_conf.tls_ca, srv->srv_conf.tls_ca_len); > + n = srv->srv_conf.tls_ca_len; > + memcpy(p + n, srv->srv_conf.tls_cert, srv->srv_conf.tls_cert_len); > + n += srv->srv_conf.tls_cert_len; > + memcpy(p + n, srv->srv_conf.tls_key, srv->srv_conf.tls_key_len); > + n += srv->srv_conf.tls_key_len; > + > + /* send tls_config structure */ > + memset(&tls, 0, sizeof(tls)); > + tls.id = srv->srv_conf.id; > + tls.port = srv->srv_conf.port; > + memcpy(&tls.ss, &srv->srv_conf.ss, sizeof(tls.ss)); > + tls.tls_ca_optional = srv->srv_conf.tls_ca_optional; > + tls.tls_ca_len = srv->srv_conf.tls_ca_len; > + tls.tls_cert_len = srv->srv_conf.tls_cert_len; > + tls.tls_key_len = srv->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->srv_conf.name); > + return (-1); > } > > - if (srv->srv_conf.tls_key_len != 0) { > - DPRINTF("%s: sending TLS key \"%s[%u]\" to %s fd %d", __func__, > - srv->srv_conf.name, srv->srv_conf.id, > - ps->ps_title[PROC_SERVER], srv->srv_s); > - > - memset(&tls, 0, sizeof(tls)); > - tls.id = srv->srv_conf.id; > - tls.port = srv->srv_conf.port; > - memcpy(&tls.ss, &srv->srv_conf.ss, sizeof(tls.ss)); > - tls.tls_key_len = srv->srv_conf.tls_key_len; > - > - c = 0; > - iov[c].iov_base = &tls; > - iov[c++].iov_len = sizeof(tls); > - iov[c].iov_base = srv->srv_conf.tls_key; > - iov[c++].iov_len = srv->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->srv_conf.name); > return (-1); > @@ -558,6 +560,54 @@ config_getserver(struct httpd *env, stru > } > > int > +config_gettls_chunk(struct httpd *env, struct imsg *imsg) > +{ > + struct server *srv = 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); > + > + /* Find server with matching listening socket. */ > + if ((srv = server_byaddr((struct sockaddr *) > + &tls_conf.ss, tls_conf.port)) == 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->srv_conf.tls_ca, srv->srv_conf.tls_ca_len, > + srv->srv_conf.tls_ca_size, p, IMSG_DATA_SIZE(imsg), s); > + > + CHUNK_CPY(srv->srv_conf.tls_cert, srv->srv_conf.tls_cert_len, > + srv->srv_conf.tls_cert_size, p, IMSG_DATA_SIZE(imsg), s); > + > + CHUNK_CPY(srv->srv_conf.tls_key, srv->srv_conf.tls_key_len, > + srv->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 > @@ -572,12 +622,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; > - } > - > /* Find server with matching listening socket. */ > if ((srv = server_byaddr((struct sockaddr *) > &tls_conf.ss, tls_conf.port)) == NULL) { > @@ -589,20 +633,25 @@ config_gettls(struct httpd *env, struct > ps->ps_title[privsep_process], ps->ps_instance, > srv->srv_conf.name, srv->srv_conf.id); > > - if (tls_conf.tls_cert_len != 0) { > - srv->srv_conf.tls_cert_len = tls_conf.tls_cert_len; > - if ((srv->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->srv_conf.tls_key_len = tls_conf.tls_key_len; > - if ((srv->srv_conf.tls_key = get_data(p + s, > - tls_conf.tls_key_len)) == NULL) > - goto fail; > - s += tls_conf.tls_key_len; > - } > + srv->srv_conf.tls_ca_optional = tls_conf.tls_ca_optional; > + srv->srv_conf.tls_ca_len = 0; > + srv->srv_conf.tls_cert_len = 0; > + srv->srv_conf.tls_key_len = 0; > + > + srv->srv_conf.tls_ca_size = tls_conf.tls_ca_len; > + srv->srv_conf.tls_cert_size = tls_conf.tls_cert_len; > + srv->srv_conf.tls_key_size = tls_conf.tls_key_len; > + > + free(srv->srv_conf.tls_ca); > + free(srv->srv_conf.tls_cert); > + free(srv->srv_conf.tls_key); > + > + if ((srv->srv_conf.tls_ca = malloc(tls_conf.tls_ca_len)) == NULL) > + goto fail; > + if ((srv->srv_conf.tls_cert = malloc(tls_conf.tls_cert_len)) == NULL) > + goto fail; > + if ((srv->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.68 > diff -u -p -r1.68 httpd.conf.5 > --- httpd.conf.5 19 Jul 2015 05:17:27 -0000 1.68 > +++ httpd.conf.5 15 Jan 2016 11:04:23 -0000 > @@ -434,6 +434,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.102 > diff -u -p -r1.102 httpd.h > --- httpd.h 2 Dec 2015 15:13:00 -0000 1.102 > +++ httpd.h 15 Jan 2016 11:20:19 -0000 > @@ -50,6 +50,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 "HIGH:!aNULL" > @@ -203,6 +204,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 +426,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; > > @@ -468,8 +477,12 @@ struct tls_config { > 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 { > @@ -723,6 +736,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 15 Jan 2016 11:31:09 -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,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"); > @@ -663,7 +666,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 +1165,7 @@ lookup(char *s) > { "block", BLOCK }, > { "body", BODY }, > { "buffer", BUFFER }, > + { "ca", CA}, > { "certificate", CERTIFICATE }, > { "chroot", CHROOT }, > { "ciphers", CIPHERS }, > @@ -1176,6 +1194,7 @@ lookup(char *s) > { "no", NO }, > { "nodelay", NODELAY }, > { "on", ON }, > + { "optional", OPTIONAL }, > { "pass", PASS }, > { "port", PORT }, > { "prefork", PREFORK }, > @@ -1970,16 +1989,24 @@ 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"); > 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 = > Index: server.c > =================================================================== > RCS file: /cvs/src/usr.sbin/httpd/server.c,v > retrieving revision 1.83 > diff -u -p -r1.83 server.c > --- server.c 2 Dec 2015 15:13:00 -0000 1.83 > +++ server.c 15 Jan 2016 02:40:24 -0000 > @@ -138,6 +138,13 @@ server_tls_load_keypair(struct server *s > if ((srv->srv_conf.flags & SRVFLAG_TLS) == 0) > 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 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) > @@ -196,6 +203,17 @@ server_tls_init(struct server *srv) > return (-1); > } > > + if (srv->srv_conf.tls_ca_len > 0) { > + if (tls_config_set_ca_mem(srv->srv_tls_config, > + srv->srv_conf.tls_ca, srv->srv_conf.tls_ca_len) != 0) { > + log_warn("%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_cert_mem(srv->srv_tls_config, > srv->srv_conf.tls_cert, srv->srv_conf.tls_cert_len) != 0) { > log_warn("%s: failed to set tls cert", __func__); > @@ -215,12 +233,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 +339,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 +364,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 +1162,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); >
signature.asc
Description: PGP signature
