From dda5d0c767c20326169f23cc38b6b53e51875bc2 Mon Sep 17 00:00:00 2001 From: Reyk Floeter Date: Mon, 19 Jan 2015 17:48:18 +0100 Subject: Split auth from server_config into struct auth --- config.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- httpd.c | 45 ++++++++++++++++++++++++ httpd.conf.5 | 5 ++- httpd.h | 26 +++++++++++--- parse.y | 63 ++++++++++++++++++++++++---------- server.c | 4 +++ server_fcgi.c | 2 +- server_http.c | 5 +-- 8 files changed, 227 insertions(+), 30 deletions(-) diff --git a/config.c b/config.c index ba99593..5884c7b 100644 --- a/config.c +++ b/config.c @@ -45,6 +45,7 @@ int config_getserver_config(struct httpd *, struct server *, struct imsg *); +int config_getserver_auth(struct httpd *, struct server_config *); int config_init(struct httpd *env) @@ -57,7 +58,8 @@ config_init(struct httpd *env) env->sc_prefork_server = SERVER_NUMPROC; ps->ps_what[PROC_PARENT] = CONFIG_ALL; - ps->ps_what[PROC_SERVER] = CONFIG_SERVERS|CONFIG_MEDIA; + ps->ps_what[PROC_SERVER] = + CONFIG_SERVERS|CONFIG_MEDIA|CONFIG_AUTH; ps->ps_what[PROC_LOGGER] = CONFIG_SERVERS; } @@ -78,6 +80,13 @@ config_init(struct httpd *env) RB_INIT(env->sc_mediatypes); } + if (what & CONFIG_AUTH) { + if ((env->sc_auth = + calloc(1, sizeof(*env->sc_auth))) == NULL) + return (-1); + TAILQ_INIT(env->sc_auth); + } + return (0); } @@ -86,6 +95,7 @@ config_purge(struct httpd *env, u_int reset) { struct privsep *ps = env->sc_ps; struct server *srv; + struct auth *auth; u_int what; what = ps->ps_what[privsep_process] & reset; @@ -97,6 +107,13 @@ config_purge(struct httpd *env, u_int reset) if (what & CONFIG_MEDIA && env->sc_mediatypes != NULL) media_purge(env->sc_mediatypes); + + if (what & CONFIG_AUTH && env->sc_auth != NULL) { + while ((auth = TAILQ_FIRST(env->sc_auth)) != NULL) { + auth_free(env->sc_auth, auth); + free(auth); + } + } } int @@ -216,6 +233,22 @@ config_setserver(struct httpd *env, struct server *srv) return (0); } +int +config_getserver_auth(struct httpd *env, struct server_config *srv_conf) +{ + struct privsep *ps = env->sc_ps; + + if ((ps->ps_what[privsep_process] & CONFIG_AUTH) == 0 || + (srv_conf->flags & SRVFLAG_AUTH) == 0) + return (0); + + if ((srv_conf->auth = auth_byid(env->sc_auth, + srv_conf->auth_id)) == NULL) + return (-1); + + return (0); +} + int config_getserver_config(struct httpd *env, struct server *srv, struct imsg *imsg) @@ -243,6 +276,9 @@ config_getserver_config(struct httpd *env, struct server *srv, if (parent == NULL) parent = &srv->srv_conf; + if (config_getserver_auth(env, srv_conf) != 0) + return (-1); + if (srv_conf->flags & SRVFLAG_LOCATION) { /* Inherit configuration from the parent */ f = SRVFLAG_INDEX|SRVFLAG_NO_INDEX; @@ -284,6 +320,16 @@ config_getserver_config(struct httpd *env, struct server *srv, if ((srv_conf->flags & f) == 0) srv_conf->flags |= parent->flags & f; + f = SRVFLAG_AUTH|SRVFLAG_NO_AUTH; + if ((srv_conf->flags & f) == 0) { + srv_conf->flags |= parent->flags & f; + srv_conf->auth = parent->auth; + srv_conf->auth_id = parent->auth_id; + (void)strlcpy(srv_conf->auth_realm, + parent->auth_realm, + sizeof(srv_conf->auth_realm)); + } + f = SRVFLAG_TLS; srv_conf->flags |= parent->flags & f; @@ -368,14 +414,15 @@ config_getserver(struct httpd *env, struct imsg *imsg) fatalx("invalid location"); /* Otherwise create a new server */ - if ((srv = calloc(1, sizeof(*srv))) == NULL) { - close(imsg->fd); - return (-1); - } + if ((srv = calloc(1, sizeof(*srv))) == NULL) + goto fail; memcpy(&srv->srv_conf, &srv_conf, sizeof(srv->srv_conf)); srv->srv_s = imsg->fd; + if (config_getserver_auth(env, &srv->srv_conf) != 0) + goto fail; + SPLAY_INIT(&srv->srv_clients); TAILQ_INIT(&srv->srv_hosts); @@ -403,6 +450,8 @@ config_getserver(struct httpd *env, struct imsg *imsg) return (0); fail: + if (imsg->fd != -1) + close(imsg->fd); if (srv != NULL) { free(srv->srv_conf.tls_cert); free(srv->srv_conf.tls_key); @@ -459,3 +508,51 @@ config_getmedia(struct httpd *env, struct imsg *imsg) return (0); } + +int +config_setauth(struct httpd *env, struct auth *auth) +{ + struct privsep *ps = env->sc_ps; + int id; + u_int what; + + for (id = 0; id < PROC_MAX; id++) { + what = ps->ps_what[id]; + + if ((what & CONFIG_AUTH) == 0 || id == privsep_process) + continue; + + DPRINTF("%s: sending auth \"%s[%u]\" to %s", __func__, + auth->auth_htpasswd, auth->auth_id, ps->ps_title[id]); + + proc_compose_imsg(ps, id, -1, IMSG_CFG_AUTH, -1, + auth, sizeof(*auth)); + } + + return (0); +} + +int +config_getauth(struct httpd *env, struct imsg *imsg) +{ +#ifdef DEBUG + struct privsep *ps = env->sc_ps; +#endif + struct auth auth; + u_int8_t *p = imsg->data; + + IMSG_SIZE_CHECK(imsg, &auth); + memcpy(&auth, p, sizeof(auth)); + + if (auth_add(env->sc_auth, &auth) == NULL) { + log_debug("%s: failed to add auth \"%s[%u]\"", + __func__, auth.auth_htpasswd, auth.auth_id); + return (-1); + } + + DPRINTF("%s: %s %d received auth \"%s[%u]\"", __func__, + ps->ps_title[privsep_process], ps->ps_instance, + auth.auth_htpasswd, auth.auth_id); + + return (0); +} diff --git a/httpd.c b/httpd.c index c7d8f9f..79acce9 100644 --- a/httpd.c +++ b/httpd.c @@ -290,12 +290,18 @@ parent_configure(struct httpd *env) int ret = -1; struct server *srv; struct media_type *media; + struct auth *auth; RB_FOREACH(media, mediatypes, env->sc_mediatypes) { if (config_setmedia(env, media) == -1) fatal("send media"); } + TAILQ_FOREACH(auth, env->sc_auth, auth_entry) { + if (config_setauth(env, auth) == -1) + fatal("send auth"); + } + /* First send the servers... */ TAILQ_FOREACH(srv, env->sc_servers, srv_entry) { if (srv->srv_conf.flags & SRVFLAG_LOCATION) @@ -1146,3 +1152,42 @@ media_cmp(struct media_type *a, struct media_type *b) } RB_GENERATE(mediatypes, media_type, media_entry, media_cmp); + +struct auth * +auth_add(struct serverauth *serverauth, struct auth *auth) +{ + struct auth *entry; + + TAILQ_FOREACH(entry, serverauth, auth_entry) { + if (strcmp(entry->auth_htpasswd, auth->auth_htpasswd) == 0) + return (entry); + } + + if ((entry = calloc(1, sizeof(*entry))) == NULL) + return (NULL); + + memcpy(entry, auth, sizeof(*entry)); + + TAILQ_INSERT_TAIL(serverauth, entry, auth_entry); + + return (entry); +} + +struct auth * +auth_byid(struct serverauth *serverauth, u_int32_t id) +{ + struct auth *auth; + + TAILQ_FOREACH(auth, serverauth, auth_entry) { + if (auth->auth_id == id) + return (auth); + } + + return (NULL); +} + +void +auth_free(struct serverauth *serverauth, struct auth *auth) +{ + TAILQ_REMOVE(serverauth, auth, auth_entry); +} diff --git a/httpd.conf.5 b/httpd.conf.5 index d4a6a00..1d66c46 100644 --- a/httpd.conf.5 +++ b/httpd.conf.5 @@ -139,7 +139,7 @@ and include one or more lines of the following syntax: Specify an additional alias .Ar name for this server. -.It Ic authenticate Oo Ar realm Oc Ic with Pa htpasswd +.It Oo Ic no Oc Ic authenticate Oo Ar realm Oc Ic with Pa htpasswd Authenticate a remote user for .Ar realm by checking the credentials against the user authentication file @@ -147,6 +147,9 @@ by checking the credentials against the user authentication file The file name is relative to the .Ic chroot and must be readable by the www user. +Use the +.Ic no authenticate +directive to disable authentication in a location. .It Ic connection Ar option Set the specified options and limits for HTTP connections. Valid options are: diff --git a/httpd.h b/httpd.h index 0822a5b..8438d51 100644 --- a/httpd.h +++ b/httpd.h @@ -59,6 +59,7 @@ #define CONFIG_RELOAD 0x00 #define CONFIG_MEDIA 0x01 #define CONFIG_SERVERS 0x02 +#define CONFIG_AUTH 0x04 #define CONFIG_ALL 0xff #define FCGI_CONTENT_SIZE 65535 @@ -189,6 +190,7 @@ enum imsg_type { IMSG_CTL_REOPEN, IMSG_CFG_SERVER, IMSG_CFG_MEDIA, + IMSG_CFG_AUTH, IMSG_CFG_DONE, IMSG_LOG_ACCESS, IMSG_LOG_ERROR, @@ -325,13 +327,14 @@ SPLAY_HEAD(client_tree, client); #define SRVFLAG_TLS 0x00002000 #define SRVFLAG_ACCESS_LOG 0x00004000 #define SRVFLAG_ERROR_LOG 0x00008000 -#define SRVFLAG_AUTH_BASIC 0x00010000 +#define SRVFLAG_AUTH 0x00010000 +#define SRVFLAG_NO_AUTH 0x00020000 #define SRVFLAG_BITS \ "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX" \ "\05ROOT\06LOCATION\07FCGI\10NO_FCGI\11LOG\12NO_LOG\13SOCKET" \ "\14SYSLOG\15NO_SYSLOG\16TLS\17ACCESS_LOG\20ERROR_LOG" \ - "\21AUTH_BASIC" + "\21AUTH\22NO_AUTH" #define TCPFLAG_NODELAY 0x01 #define TCPFLAG_NNODELAY 0x02 @@ -361,6 +364,13 @@ struct log_file { }; TAILQ_HEAD(log_files, log_file) log_files; +struct auth { + char auth_htpasswd[PATH_MAX]; + u_int32_t auth_id; + TAILQ_ENTRY(auth) auth_entry; +}; +TAILQ_HEAD(serverauth, auth); + struct server_config { u_int32_t id; char name[HOST_NAME_MAX+1]; @@ -370,8 +380,6 @@ struct server_config { char socket[PATH_MAX]; char accesslog[NAME_MAX]; char errorlog[NAME_MAX]; - char auth_realm[NAME_MAX]; - char auth_htpasswd[PATH_MAX]; in_port_t port; struct sockaddr_storage ss; @@ -400,6 +408,10 @@ struct server_config { struct log_file *logaccess; struct log_file *logerror; + char auth_realm[NAME_MAX]; + u_int32_t auth_id; + struct auth *auth; + TAILQ_ENTRY(server_config) entry; }; TAILQ_HEAD(serverhosts, server_config); @@ -442,6 +454,7 @@ struct httpd { struct serverlist *sc_servers; struct mediatypes *sc_mediatypes; + struct serverauth *sc_auth; struct privsep *sc_ps; int sc_reload; @@ -587,6 +600,9 @@ struct media_type * int media_cmp(struct media_type *, struct media_type *); RB_PROTOTYPE(kvtree, kv, kv_node, kv_cmp); RB_PROTOTYPE(mediatypes, media_type, media_entry, media_cmp); +struct auth *auth_add(struct serverauth *, struct auth *); +struct auth *auth_byid(struct serverauth *, u_int32_t); +void auth_free(struct serverauth *, struct auth *); /* log.c */ void log_init(int); @@ -639,6 +655,8 @@ int config_setserver(struct httpd *, struct server *); int config_getserver(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 *); +int config_getauth(struct httpd *, struct imsg *); /* logger.c */ pid_t logger(struct privsep *, struct privsep_proc *); diff --git a/parse.y b/parse.y index b47173a..9afea2c 100644 --- a/parse.y +++ b/parse.y @@ -91,6 +91,7 @@ struct httpd *conf = NULL; static int errors = 0; static int loadcfg = 0; uint32_t last_server_id = 0; +uint32_t last_auth_id = 0; static struct server *srv = NULL, *parentsrv = NULL; static struct server_config *srv_conf = NULL; @@ -117,6 +118,7 @@ typedef struct { char *string; struct timeval tv; struct portrange port; + struct auth auth; struct { struct sockaddr_storage ss; char name[HOST_NAME_MAX+1]; @@ -138,6 +140,7 @@ typedef struct { %type opttls %type timeout %type numberstring +%type authopts %% @@ -645,35 +648,60 @@ rootflags : STRING { } ; -authenticate : AUTHENTICATE STRING WITH STRING { - if (strlcpy(srv->srv_conf.auth_realm, $2, +authenticate : NO AUTHENTICATE { + srv->srv_conf.flags |= SRVFLAG_NO_AUTH; + } + | AUTHENTICATE authopts { + struct auth *auth; + + if ((auth = auth_add(conf->sc_auth, &$2)) == NULL) { + yyerror("failed to add auth"); + YYERROR; + } + + if (auth->auth_id == 0) { + /* New htpasswd, get new Id */ + auth->auth_id = ++last_auth_id; + if (last_auth_id == INT_MAX) { + yyerror("too many auth ids defined"); + auth_free(conf->sc_auth, auth); + YYERROR; + } + } + + srv->srv_conf.auth_id = auth->auth_id; + srv->srv_conf.flags |= SRVFLAG_AUTH; + } + ; + +authopts : STRING WITH STRING { + if (strlcpy(srv->srv_conf.auth_realm, $1, sizeof(srv->srv_conf.auth_realm)) >= sizeof(srv->srv_conf.auth_realm)) { yyerror("basic auth realm name too long"); - free($2); + free($1); YYERROR; } - free($2); - if (strlcpy(srv->srv_conf.auth_htpasswd, $4, - sizeof(srv->srv_conf.auth_htpasswd)) >= - sizeof(srv->srv_conf.auth_htpasswd)) { + free($1); + if (strlcpy($$.auth_htpasswd, $3, + sizeof($$.auth_htpasswd)) >= + sizeof($$.auth_htpasswd)) { yyerror("password file name too long"); - free($4); + free($3); YYERROR; } - free($4); - srv->srv_conf.flags |= SRVFLAG_AUTH_BASIC; + free($3); + } - | AUTHENTICATE WITH STRING { - if (strlcpy(srv->srv_conf.auth_htpasswd, $3, - sizeof(srv->srv_conf.auth_htpasswd)) >= - sizeof(srv->srv_conf.auth_htpasswd)) { + | WITH STRING { + if (strlcpy($$.auth_htpasswd, $2, + sizeof($$.auth_htpasswd)) >= + sizeof($$.auth_htpasswd)) { yyerror("password file name too long"); - free($3); + free($2); YYERROR; } - free($3); - srv->srv_conf.flags |= SRVFLAG_AUTH_BASIC; + free($2); }; directory : DIRECTORY dirflags @@ -1400,6 +1428,7 @@ load_config(const char *filename, struct httpd *x_conf) loadcfg = 1; errors = 0; last_server_id = 0; + last_auth_id = 0; srv = NULL; diff --git a/server.c b/server.c index 943e60a..522deb1 100644 --- a/server.c +++ b/server.c @@ -337,6 +337,7 @@ serverconfig_reset(struct server_config *srv_conf) { srv_conf->tls_cert_file = srv_conf->tls_cert = srv_conf->tls_key_file = srv_conf->tls_key = NULL; + srv_conf->auth = NULL; } struct server * @@ -1122,6 +1123,9 @@ server_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) case IMSG_CFG_MEDIA: config_getmedia(env, imsg); break; + case IMSG_CFG_AUTH: + config_getauth(env, imsg); + break; case IMSG_CFG_SERVER: config_getserver(env, imsg); break; diff --git a/server_fcgi.c b/server_fcgi.c index 7305eb0..8a6f5e6 100644 --- a/server_fcgi.c +++ b/server_fcgi.c @@ -259,7 +259,7 @@ server_fcgi(struct httpd *env, struct client *clt) goto fail; } - if (srv_conf->flags & SRVFLAG_AUTH_BASIC) { + if (srv_conf->flags & SRVFLAG_AUTH) { if (fcgi_add_param(¶m, "REMOTE_USER", clt->clt_fcgi_remote_user, clt) == -1) { errstr = "failed to encode param"; diff --git a/server_http.c b/server_http.c index 5bf6bd5..b1338cf 100644 --- a/server_http.c +++ b/server_http.c @@ -136,6 +136,7 @@ server_http_authenticate(struct server_config *srv_conf, struct client *clt) { FILE *fp = NULL; struct http_descriptor *desc = clt->clt_descreq; + struct auth *auth = srv_conf->auth; struct kv *ba, key; size_t linesize = 0; ssize_t linelen; @@ -166,7 +167,7 @@ server_http_authenticate(struct server_config *srv_conf, struct client *clt) if (clt_pass == NULL) goto done; - if ((fp = fopen(srv_conf->auth_htpasswd, "r")) == NULL) + if ((fp = fopen(auth->auth_htpasswd, "r")) == NULL) goto done; while ((linelen = getline(&line, &linesize, fp)) != -1) { @@ -964,7 +965,7 @@ server_response(struct httpd *httpd, struct client *clt) /* Now search for the location */ srv_conf = server_getlocation(clt, desc->http_path); - if (srv_conf->flags & SRVFLAG_AUTH_BASIC && + if (srv_conf->flags & SRVFLAG_AUTH && server_http_authenticate(srv_conf, clt) == -1) { server_abort_http(clt, 401, srv_conf->auth_realm); return (-1); -- cgit v1.2.3-54-g00ecf