Decouple auth parameters from struct server_config into struct auth.
authorreyk <reyk@openbsd.org>
Mon, 19 Jan 2015 19:37:50 +0000 (19:37 +0000)
committerreyk <reyk@openbsd.org>
Mon, 19 Jan 2015 19:37:50 +0000 (19:37 +0000)
OK florian@

usr.sbin/httpd/config.c
usr.sbin/httpd/httpd.c
usr.sbin/httpd/httpd.conf.5
usr.sbin/httpd/httpd.h
usr.sbin/httpd/parse.y
usr.sbin/httpd/server.c
usr.sbin/httpd/server_fcgi.c
usr.sbin/httpd/server_http.c

index ba99593..07d8e32 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.29 2015/01/13 09:21:15 reyk Exp $        */
+/*     $OpenBSD: config.c,v 1.30 2015/01/19 19:37:50 reyk Exp $        */
 
 /*
  * Copyright (c) 2011 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -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);
+}
index c7d8f9f..f229cc5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: httpd.c,v 1.29 2015/01/16 06:40:17 deraadt Exp $      */
+/*     $OpenBSD: httpd.c,v 1.30 2015/01/19 19:37:50 reyk Exp $ */
 
 /*
  * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
@@ -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);
+}
index d4a6a00..f40273c 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: httpd.conf.5,v 1.46 2015/01/18 18:39:46 florian Exp $
+.\"    $OpenBSD: httpd.conf.5,v 1.47 2015/01/19 19:37:50 reyk 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: January 18 2015 $
+.Dd $Mdocdate: January 19 2015 $
 .Dt HTTPD.CONF 5
 .Os
 .Sh NAME
@@ -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:
index 0822a5b..e0473cb 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: httpd.h,v 1.69 2015/01/18 14:01:17 florian Exp $      */
+/*     $OpenBSD: httpd.h,v 1.70 2015/01/19 19:37:50 reyk Exp $ */
 
 /*
  * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -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 *);
index b47173a..1b48940 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.55 2015/01/18 14:01:17 florian Exp $      */
+/*     $OpenBSD: parse.y,v 1.56 2015/01/19 19:37:50 reyk Exp $ */
 
 /*
  * Copyright (c) 2007 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -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  <v.number>      opttls
 %type  <v.tv>          timeout
 %type  <v.string>      numberstring
+%type  <v.auth>        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;
 
index 943e60a..dfaa15c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server.c,v 1.52 2015/01/16 06:40:17 deraadt Exp $     */
+/*     $OpenBSD: server.c,v 1.53 2015/01/19 19:37:50 reyk Exp $        */
 
 /*
  * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -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;
index 7305eb0..8ae1f2f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_fcgi.c,v 1.47 2015/01/18 14:01:17 florian Exp $        */
+/*     $OpenBSD: server_fcgi.c,v 1.48 2015/01/19 19:37:50 reyk Exp $   */
 
 /*
  * Copyright (c) 2014 Florian Obser <florian@openbsd.org>
@@ -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(&param, "REMOTE_USER",
                    clt->clt_fcgi_remote_user, clt) == -1) {
                        errstr = "failed to encode param";
index 5bf6bd5..99b6175 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_http.c,v 1.65 2015/01/18 14:01:17 florian Exp $        */
+/*     $OpenBSD: server_http.c,v 1.66 2015/01/19 19:37:50 reyk Exp $   */
 
 /*
  * Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -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);