diff options
author | Joel Sing <jsing@cvs.openbsd.org> | 2018-05-19 13:56:57 +0000 |
---|---|---|
committer | Joel Sing <jsing@cvs.openbsd.org> | 2018-05-19 13:56:57 +0000 |
commit | 408aad5fa1d6b22b8d8f0f280e394cd53c0684f7 (patch) | |
tree | 19c7e3ae09ee71f8d7355670293c69533af713b6 /usr.sbin | |
parent | db9bfcc360f67644d93b38b4e9d2349469a3fc9a (diff) |
Add support for client certificate authentication to httpd.
From Jack Burton <jack at saosce dot com dot au> - thanks!
Also tested by Jan Klemkow <j.klemkow at wemelug dot de>.
ok beck@ reyk@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/httpd/config.c | 23 | ||||
-rw-r--r-- | usr.sbin/httpd/httpd.conf.5 | 25 | ||||
-rw-r--r-- | usr.sbin/httpd/httpd.h | 18 | ||||
-rw-r--r-- | usr.sbin/httpd/parse.y | 58 | ||||
-rw-r--r-- | usr.sbin/httpd/server.c | 71 | ||||
-rw-r--r-- | usr.sbin/httpd/server_fcgi.c | 11 |
6 files changed, 197 insertions, 9 deletions
diff --git a/usr.sbin/httpd/config.c b/usr.sbin/httpd/config.c index 3c31c3d4cd3..42ff4a44b51 100644 --- a/usr.sbin/httpd/config.c +++ b/usr.sbin/httpd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.53 2017/07/19 17:36:25 jsing Exp $ */ +/* $OpenBSD: config.c,v 1.54 2018/05/19 13:56:56 jsing Exp $ */ /* * Copyright (c) 2011 - 2015 Reyk Floeter <reyk@openbsd.org> @@ -304,10 +304,18 @@ config_setserver_tls(struct httpd *env, struct server *srv) 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); @@ -431,6 +439,7 @@ config_getserver_config(struct httpd *env, struct server *srv, f = SRVFLAG_TLS; srv_conf->flags |= parent->flags & f; + srv_conf->tls_flags = parent->tls_flags; f = SRVFLAG_ACCESS_LOG; if ((srv_conf->flags & f) == 0) { @@ -655,12 +664,24 @@ config_getserver_tls(struct httpd *env, struct imsg *imsg) } 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; + case TLS_CFG_KEY: if (config_gettls(env, srv_conf, &tls_conf, "key", p, len, &srv_conf->tls_key, &srv_conf->tls_key_len) != 0) diff --git a/usr.sbin/httpd/httpd.conf.5 b/usr.sbin/httpd/httpd.conf.5 index 34cdfa54953..affa2ee1656 100644 --- a/usr.sbin/httpd/httpd.conf.5 +++ b/usr.sbin/httpd/httpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: httpd.conf.5,v 1.93 2018/05/18 15:04:07 reyk Exp $ +.\" $OpenBSD: httpd.conf.5,v 1.94 2018/05/19 13:56:56 jsing Exp $ .\" .\" Copyright (c) 2014, 2015 Reyk Floeter <reyk@openbsd.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 18 2018 $ +.Dd $Mdocdate: May 19 2018 $ .Dt HTTPD.CONF 5 .Os .Sh NAME @@ -342,6 +342,10 @@ The revision of the HTTP specification used. .It Ic SERVER_SOFTWARE The server software name of .Xr httpd 8 . +.It Ic TLS_PEER_VERIFY +A variable that is set to a comma separated list of TLS client verification +features in use +.Pq omitted when TLS client verification is not in use . .El .It Ic hsts Oo Ar option Oc Enable HTTP Strict Transport Security. @@ -526,6 +530,23 @@ will be used (strong crypto cipher suites without anonymous DH). See the CIPHERS section of .Xr openssl 1 for information about SSL/TLS cipher suites and preference lists. +.It Ic client ca Ar cafile Oo Ic crl Ar crlfile Oc Op Ic optional +Require +.Po +or, if +.Ic optional +is specified, request but do not require +.Pc +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. diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h index 6901af7d1af..aff1a6c5e2a 100644 --- a/usr.sbin/httpd/httpd.h +++ b/usr.sbin/httpd/httpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.h,v 1.136 2018/04/11 15:50:46 florian Exp $ */ +/* $OpenBSD: httpd.h,v 1.137 2018/05/19 13:56:56 jsing Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org> @@ -424,6 +424,11 @@ SPLAY_HEAD(client_tree, client); #define HSTSFLAG_PRELOAD 0x02 #define HSTSFLAG_BITS "\10\01SUBDOMAINS\02PRELOAD" +#define TLSFLAG_CA 0x01 +#define TLSFLAG_CRL 0x02 +#define TLSFLAG_OPTIONAL 0x04 +#define TLSFLAG_BITS "\10\01CA\02CRL\03OPTIONAL" + enum log_format { LOG_FORMAT_COMMON, LOG_FORMAT_COMBINED, @@ -480,12 +485,19 @@ 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[HTTPD_TLS_CONFIG_MAX]; + uint8_t *tls_crl; + char *tls_crl_file; + size_t tls_crl_len; char tls_dhe_params[HTTPD_TLS_CONFIG_MAX]; char tls_ecdhe_curves[HTTPD_TLS_CONFIG_MAX]; + uint8_t tls_flags; uint8_t *tls_key; size_t tls_key_len; char *tls_key_file; @@ -524,7 +536,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, }; @@ -598,6 +612,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 *); diff --git a/usr.sbin/httpd/parse.y b/usr.sbin/httpd/parse.y index 07a47e324dc..e794441a3bb 100644 --- a/usr.sbin/httpd/parse.y +++ b/usr.sbin/httpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.97 2018/05/18 15:20:46 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.98 2018/05/19 13:56:56 jsing Exp $ */ /* * Copyright (c) 2007 - 2015 Reyk Floeter <reyk@openbsd.org> @@ -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 OPTIONAL %token <v.string> STRING %token <v.number> NUMBER %type <v.port> port @@ -346,6 +347,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); @@ -527,6 +544,7 @@ serveroptsl : LISTEN ON STRING opttls port { sizeof(s->srv_conf.ss)); s->srv_conf.port = srv->srv_conf.port; s->srv_conf.prefixlen = srv->srv_conf.prefixlen; + s->srv_conf.tls_flags = srv->srv_conf.tls_flags; if (last_server_id == INT_MAX) { yyerror("too many servers/locations defined"); @@ -700,6 +718,13 @@ tlsopts : CERTIFICATE STRING { } free($2); } + | CLIENT CA STRING tlsclientopt { + srv_conf->tls_flags |= TLSFLAG_CA; + 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)) >= @@ -748,6 +773,18 @@ tlsopts : CERTIFICATE STRING { } ; +tlsclientopt : /* empty */ + | tlsclientopt CRL STRING { + srv_conf->tls_flags = TLSFLAG_CRL; + free(srv_conf->tls_crl_file); + if ((srv_conf->tls_crl_file = strdup($3)) == NULL) + fatal("out of memory"); + free($3); + } + | tlsclientopt OPTIONAL { + srv_conf->tls_flags |= TLSFLAG_OPTIONAL; + } + ; root : ROOT rootflags | ROOT '{' optnl rootflags_l '}' ; @@ -1180,12 +1217,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 }, @@ -1210,6 +1250,7 @@ lookup(char *s) { "nodelay", NODELAY }, { "ocsp", OCSP }, { "on", ON }, + { "optional", OPTIONAL }, { "pass", PASS }, { "port", PORT }, { "prefork", PREFORK }, @@ -2042,6 +2083,21 @@ server_inherit(struct server *src, struct server_config *alias, return (NULL); } + if (server_tls_load_ca(dst) == -1) { + yyerror("falied to load ca cert(s) for server %s", + dst->srv_conf.name); + serverconfig_free(&dst->srv_conf); + 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); + return NULL; + } + if (server_tls_load_ocsp(dst) == -1) { yyerror("failed to load ocsp staple " "for server %s", dst->srv_conf.name); diff --git a/usr.sbin/httpd/server.c b/usr.sbin/httpd/server.c index 2a505ace8de..5f4304705d8 100644 --- a/usr.sbin/httpd/server.c +++ b/usr.sbin/httpd/server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server.c,v 1.113 2017/11/29 16:55:08 beck Exp $ */ +/* $OpenBSD: server.c,v 1.114 2018/05/19 13:56:56 jsing Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org> @@ -134,6 +134,8 @@ server_tls_cmp(struct server *s1, struct server *s2, int match_keypair) sc1 = &s1->srv_conf; sc2 = &s2->srv_conf; + if (sc1->tls_flags != sc2->tls_flags) + return (-1); if (sc1->tls_protocols != sc2->tls_protocols) return (-1); if (sc1->tls_ticket_lifetime != sc2->tls_ticket_lifetime) @@ -207,6 +209,40 @@ server_tls_load_ocsp(struct server *srv) } int +server_tls_load_ca(struct server *srv) +{ + if ((srv->srv_conf.tls_flags & TLSFLAG_CA) == 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.tls_flags & TLSFLAG_CA) == 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; @@ -264,6 +300,23 @@ 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); + } + 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); + } + if (srv->srv_conf.tls_flags & TLSFLAG_OPTIONAL) + tls_config_verify_client_optional(srv->srv_tls_config); + else + 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; @@ -310,13 +363,19 @@ server_tls_init(struct server *srv) return (-1); } - /* We're now done with the public/private key... */ + /* We're now done with the public/private key & ca/crl... */ tls_config_clear_keys(srv->srv_tls_config); freezero(srv->srv_conf.tls_cert, srv->srv_conf.tls_cert_len); freezero(srv->srv_conf.tls_key, srv->srv_conf.tls_key_len); + free(srv->srv_conf.tls_ca); + free(srv->srv_conf.tls_crl); + srv->srv_conf.tls_ca = NULL; srv->srv_conf.tls_cert = NULL; + srv->srv_conf.tls_crl = 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_crl_len = 0; srv->srv_conf.tls_key_len = 0; return (0); @@ -422,7 +481,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); @@ -435,8 +498,12 @@ serverconfig_reset(struct server_config *srv_conf) { 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; diff --git a/usr.sbin/httpd/server_fcgi.c b/usr.sbin/httpd/server_fcgi.c index 920b2cfff3c..17fab77c493 100644 --- a/usr.sbin/httpd/server_fcgi.c +++ b/usr.sbin/httpd/server_fcgi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_fcgi.c,v 1.75 2017/07/31 08:02:49 ians Exp $ */ +/* $OpenBSD: server_fcgi.c,v 1.76 2018/05/19 13:56:56 jsing Exp $ */ /* * Copyright (c) 2014 Florian Obser <florian@openbsd.org> @@ -282,11 +282,18 @@ server_fcgi(struct httpd *env, struct client *clt) 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_flags != 0 && fcgi_add_param(¶m, + "TLS_PEER_VERIFY", printb_flags(srv_conf->tls_flags, + TLSFLAG_BITS), 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) { |