Commit Diff


commit - e240092c03160c453e59bea3aff1b6ec606b1539
commit + 9e6d4144ce019af2f5268ad4c9554f3a005c53dc
blob - 29487341845ed5d0e00ec472e2593c862e4503d1
blob + 4021e113af0bec3a58a00ea8dc9e1e77f377c553
--- gotwebd/access.c
+++ gotwebd/access.c
@@ -21,6 +21,7 @@
 #include <event.h>
 #include <imsg.h>
 #include <pwd.h>
+#include <signal.h>
 #include <grp.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -30,6 +31,51 @@
 #include "gotwebd.h"
 #include "log.h"
 
+static char auth_token_secret[32];
+
+static void
+access_shutdown(void)
+{
+	struct gotwebd *env = gotwebd_env;
+	int i;
+
+	imsgbuf_clear(&env->iev_parent->ibuf);
+
+	for (i = 0; i < env->nserver; i++)
+		imsgbuf_clear(&env->iev_login[i].ibuf);
+
+	free(env->iev_parent);
+	free(env->iev_login);
+	free(env);
+
+	exit(0);
+}
+
+static void
+access_sighdlr(int sig, short event, void *arg)
+{
+	switch (sig) {
+	case SIGHUP:
+		log_info("%s: ignoring SIGHUP", __func__);
+		break;
+	case SIGPIPE:
+		log_info("%s: ignoring SIGPIPE", __func__);
+		break;
+	case SIGUSR1:
+		log_info("%s: ignoring SIGUSR1", __func__);
+		break;
+	case SIGCHLD:
+		break;
+	case SIGINT:
+	case SIGTERM:
+		access_shutdown();
+		break;
+	default:
+		log_warn("unexpected signal %d", sig);
+		break;
+	}
+}
+
 #if 0
 static int
 parseuid(const char *s, uid_t *uid)
@@ -143,3 +189,228 @@ access_check(uid_t uid, struct gotwebd_access_rule_lis
 	return access;
 #endif
 }
+
+static void
+access_launch(struct gotwebd *env)
+{
+	int i;
+
+	for (i = 0; i < env->nserver; i++)
+		event_add(&env->iev_login[i].ev, NULL);
+}
+
+static void
+access_dispatch_login(int fd, short event, void *arg)
+{
+	struct imsgev		*iev = arg;
+	struct imsgbuf		*ibuf;
+	struct imsg		 imsg;
+	ssize_t			 n;
+	int			 shut = 0;
+
+	ibuf = &iev->ibuf;
+
+	if (event & EV_READ) {
+		if ((n = imsgbuf_read(ibuf)) == -1)
+			fatal("imsgbuf_read error");
+		if (n == 0)	/* Connection closed */
+			shut = 1;
+	}
+	if (event & EV_WRITE) {
+		if (imsgbuf_write(ibuf) == -1)
+			fatal("imsgbuf_write");
+	}
+
+	for (;;) {
+		if ((n = imsg_get(ibuf, &imsg)) == -1)
+			fatal("imsg_get");
+		if (n == 0)	/* No more messages. */
+			break;
+
+		switch (imsg.hdr.type) {
+		default:
+			fatalx("%s: unknown imsg type %d", __func__,
+			    imsg.hdr.type);
+		}
+
+		imsg_free(&imsg);
+	}
+
+	if (!shut)
+		imsg_event_add(iev);
+	else {
+		/* This pipe is dead.  Remove its event handler */
+		event_del(&iev->ev);
+		event_loopexit(NULL);
+	}
+}
+
+static void
+recv_login_pipe(struct gotwebd *env, struct imsg *imsg)
+{
+	struct imsgev *iev;
+	int fd;
+
+	if (env->server_cnt >= env->nserver)
+		fatalx("too many login pipes received");
+
+	fd = imsg_get_fd(imsg);
+	if (fd == -1)
+		fatalx("invalid login pipe fd");
+
+	iev = &env->iev_login[env->server_cnt];
+	if (imsgbuf_init(&iev->ibuf, fd) == -1)
+		fatal("imsgbuf_init");
+
+	iev->handler = access_dispatch_login;
+	iev->data = iev;
+	event_set(&iev->ev, fd, EV_READ, access_dispatch_login, iev);
+	imsg_event_add(iev);
+
+	env->server_cnt++;
+}
+
+static void
+access_dispatch_main(int fd, short event, void *arg)
+{
+	struct imsgev		*iev = arg;
+	struct imsgbuf		*ibuf;
+	struct imsg		 imsg;
+	struct gotwebd		*env = gotwebd_env;
+	struct server		*srv;
+	struct gotwebd_repo	*repo;
+	ssize_t			 n;
+	int			 shut = 0;
+
+	ibuf = &iev->ibuf;
+
+	if (event & EV_READ) {
+		if ((n = imsgbuf_read(ibuf)) == -1)
+			fatal("imsgbuf_read error");
+		if (n == 0)	/* Connection closed */
+			shut = 1;
+	}
+	if (event & EV_WRITE) {
+		if (imsgbuf_write(ibuf) == -1)
+			fatal("imsgbuf_write");
+	}
+
+	for (;;) {
+		if ((n = imsg_get(ibuf, &imsg)) == -1)
+			fatal("imsg_get");
+		if (n == 0)	/* No more messages. */
+			break;
+
+		switch (imsg.hdr.type) {
+		case GOTWEBD_IMSG_CFG_ACCESS_RULE:
+			if (TAILQ_EMPTY(&env->servers)) {
+				/* global access rule */
+				config_get_access_rule(&env->access_rules,
+				    &imsg);
+			} else {
+				srv = TAILQ_LAST(&env->servers, serverlist);
+				if (TAILQ_EMPTY(&srv->repos)) {
+					/* per-server access rule */
+					config_get_access_rule(
+					    &srv->access_rules, &imsg);
+				} else {
+					/* per-repository access rule */
+					repo = TAILQ_LAST(&srv->repos,
+					    gotwebd_repolist);
+					config_get_access_rule(
+					    &repo->access_rules, &imsg);
+				}
+			}
+			break;
+		case GOTWEBD_IMSG_CFG_SRV:
+			config_getserver(gotwebd_env, &imsg);
+			break;
+		case GOTWEBD_IMSG_CFG_REPO:
+			if (TAILQ_EMPTY(&env->servers))
+				fatalx("%s: unexpected CFG_REPO msg", __func__);
+			srv = TAILQ_LAST(&env->servers, serverlist);
+			config_get_repository(&srv->repos, &imsg);
+			break;
+		case GOTWEBD_IMSG_CTL_PIPE:
+			recv_login_pipe(env, &imsg);
+			break;
+		case GOTWEBD_IMSG_CTL_START:
+			access_launch(env);
+			break;
+		case GOTWEBD_IMSG_AUTH_SECRET:
+			if (imsg_get_data(&imsg, auth_token_secret,
+			    sizeof(auth_token_secret)) == -1)
+				fatalx("%s: invalid AUTH_SECRET msg", __func__);
+			break;
+		case GOTWEBD_IMSG_AUTH_CONF:
+			if (imsg_get_data(&imsg, &env->auth_config,
+			    sizeof(env->auth_config)) == -1)
+				fatalx("%s: invalid AUTH_CONF msg", __func__);
+			break;
+		case GOTWEBD_IMSG_WWW_UID:
+			if (imsg_get_data(&imsg, &env->www_uid,
+			    sizeof(env->www_uid)) == -1)
+				fatalx("%s: invalid WWW_UID msg", __func__);
+			break;
+		default:
+			fatalx("%s: unknown imsg type %d", __func__,
+			    imsg.hdr.type);
+		}
+
+		imsg_free(&imsg);
+	}
+
+	if (!shut)
+		imsg_event_add(iev);
+	else {
+		/* This pipe is dead.  Remove its event handler */
+		event_del(&iev->ev);
+		event_loopexit(NULL);
+	}
+}
+
+void
+gotwebd_access(struct gotwebd *env, int fd)
+{
+	struct event	 sighup, sigint, sigusr1, sigchld, sigterm;
+	struct event_base *evb;
+
+	evb = event_init();
+
+	if ((env->iev_parent = malloc(sizeof(*env->iev_parent))) == NULL)
+		fatal("malloc");
+	if (imsgbuf_init(&env->iev_parent->ibuf, fd) == -1)
+		fatal("imsgbuf_init");
+	imsgbuf_allow_fdpass(&env->iev_parent->ibuf);
+	env->iev_parent->handler = access_dispatch_main;
+	env->iev_parent->data = env->iev_parent;
+	event_set(&env->iev_parent->ev, fd, EV_READ, access_dispatch_main,
+	    env->iev_parent);
+	event_add(&env->iev_parent->ev, NULL);
+
+	env->nserver = env->prefork_gotwebd;
+	env->iev_login = calloc(env->nserver, sizeof(*env->iev_login));
+	if (env->iev_login == NULL)
+		fatal("calloc");
+
+	signal(SIGPIPE, SIG_IGN);
+
+	signal_set(&sighup, SIGHUP, access_sighdlr, env);
+	signal_add(&sighup, NULL);
+	signal_set(&sigint, SIGINT, access_sighdlr, env);
+	signal_add(&sigint, NULL);
+	signal_set(&sigusr1, SIGUSR1, access_sighdlr, env);
+	signal_add(&sigusr1, NULL);
+	signal_set(&sigchld, SIGCHLD, access_sighdlr, env);
+	signal_add(&sigchld, NULL);
+	signal_set(&sigterm, SIGTERM, access_sighdlr, env);
+	signal_add(&sigterm, NULL);
+
+#ifndef PROFILE
+	if (pledge("stdio getpw recvfd", NULL) == -1)
+		fatal("pledge");
+#endif
+	event_dispatch();
+	event_base_free(evb);
+	access_shutdown();
+}
blob - a2bcb51ecdc5ad72372dc40a7d33332c84a54576
blob + 1145399328b3be0c33705a4e62c64acf5072b386
--- gotwebd/gotwebd.c
+++ gotwebd/gotwebd.c
@@ -53,6 +53,7 @@ void	 gotwebd_configure_done(struct gotwebd *);
 void	 gotwebd_sighdlr(int sig, short event, void *arg);
 void	 gotwebd_shutdown(void);
 void	 gotwebd_dispatch_auth(int, short, void *);
+void	 gotwebd_dispatch_access(int, short, void *);
 void	 gotwebd_dispatch_server(int, short, void *);
 void	 gotwebd_dispatch_login(int, short, void *);
 void	 gotwebd_dispatch_gotweb(int, short, void *);
@@ -181,6 +182,52 @@ sockets_compose_main(struct gotwebd *env, uint32_t typ
 
 void
 gotwebd_dispatch_auth(int fd, short event, void *arg)
+{
+	struct imsgev		*iev = arg;
+	struct imsgbuf		*ibuf;
+	struct imsg		 imsg;
+	ssize_t			 n;
+	int			 shut = 0;
+
+	ibuf = &iev->ibuf;
+
+	if (event & EV_READ) {
+		if ((n = imsgbuf_read(ibuf)) == -1)
+			fatal("imsgbuf_read error");
+		if (n == 0)	/* Connection closed */
+			shut = 1;
+	}
+	if (event & EV_WRITE) {
+		if (imsgbuf_write(ibuf) == -1)
+			fatal("imsgbuf_write");
+	}
+
+	for (;;) {
+		if ((n = imsg_get(ibuf, &imsg)) == -1)
+			fatal("imsg_get");
+		if (n == 0)	/* No more messages. */
+			break;
+
+		switch (imsg.hdr.type) {
+		default:
+			fatalx("%s: unknown imsg type %d", __func__,
+			    imsg.hdr.type);
+		}
+
+		imsg_free(&imsg);
+	}
+
+	if (!shut)
+		imsg_event_add(iev);
+	else {
+		/* This pipe is dead.  Remove its event handler */
+		event_del(&iev->ev);
+		event_loopexit(NULL);
+	}
+}
+
+void
+gotwebd_dispatch_access(int fd, short event, void *arg)
 {
 	struct imsgev		*iev = arg;
 	struct imsgbuf		*ibuf;
@@ -442,6 +489,9 @@ spawn_process(struct gotwebd *env, const char *argv0, 
 	} else if (proc_type == GOTWEBD_PROC_LOGIN) {
 		argv[argc++] = "-L";
 		argv[argc++] = username;
+	} else if (proc_type == GOTWEBD_PROC_ACCESS) {
+		argv[argc++] = "-C";
+		argv[argc++] = username;
 	} else if (proc_type == GOTWEBD_PROC_GOTWEB) {
 		argv[argc++] = "-G";
 		argv[argc++] = username;
@@ -505,12 +555,16 @@ main(int argc, char **argv)
 		fatal("%s: calloc", __func__);
 	config_init(env);
 
-	while ((ch = getopt(argc, argv, "A:D:dG:f:L:nS:vW:")) != -1) {
+	while ((ch = getopt(argc, argv, "A:C:D:dG:f:L:nS:vW:")) != -1) {
 		switch (ch) {
 		case 'A':
 			proc_type = GOTWEBD_PROC_AUTH;
 			gotwebd_username = optarg;
 			break;
+		case 'C':
+			proc_type = GOTWEBD_PROC_ACCESS;
+			gotwebd_username = optarg;
+			break;
 		case 'D':
 			if (cmdline_symset(optarg) < 0)
 				log_warnx("could not parse macro definition %s",
@@ -632,6 +686,17 @@ main(int argc, char **argv)
 
 		gotwebd_login(env, GOTWEBD_SOCK_FILENO);
 		return 1;
+	case GOTWEBD_PROC_ACCESS:
+		setproctitle("access");
+		log_procinit("access");
+
+		if (setgroups(1, &pw->pw_gid) == -1 ||
+		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
+		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
+			fatal("failed to drop privileges");
+
+		gotwebd_access(env, GOTWEBD_SOCK_FILENO);
+		return 1;
 	case GOTWEBD_PROC_GOTWEB:
 		setproctitle("gotweb");
 		log_procinit("gotweb");
@@ -655,6 +720,9 @@ main(int argc, char **argv)
 	env->iev_auth = calloc(1, sizeof(*env->iev_auth));
 	if (env->iev_auth == NULL)
 		fatal("calloc");
+	env->iev_access = calloc(1, sizeof(*env->iev_access));
+	if (env->iev_access == NULL)
+		fatal("calloc");
 	env->nserver = env->prefork_gotwebd;
 	env->iev_server = calloc(env->nserver, sizeof(*env->iev_server));
 	if (env->iev_server == NULL)
@@ -668,6 +736,8 @@ main(int argc, char **argv)
 
 	spawn_process(env, argv0, env->iev_auth, GOTWEBD_PROC_AUTH,
 	    gotwebd_username, gotwebd_dispatch_auth);
+	spawn_process(env, argv0, env->iev_access, GOTWEBD_PROC_ACCESS,
+	    gotwebd_username, gotwebd_dispatch_access);
 
 	for (i = 0; i < env->nserver; ++i) {
 		spawn_process(env, argv0, &env->iev_server[i],
@@ -784,6 +854,7 @@ gotwebd_configure(struct gotwebd *env, uid_t uid, gid_
 	arc4random_buf(auth_token_secret, sizeof(auth_token_secret));
 
 	/* send our global access rules */
+	config_set_access_rules(env->iev_access, &env->access_rules);
 	for (i = 0; i < env->nserver; ++i) {
 		config_set_access_rules(&env->iev_login[i],
 		    &env->access_rules);
@@ -802,8 +873,12 @@ gotwebd_configure(struct gotwebd *env, uid_t uid, gid_
 		if (main_compose_gotweb(env, GOTWEBD_IMSG_CFG_SRV,
 		    -1, srv, sizeof(*srv)) == -1)
 			fatal("main_compose_gotweb GOTWEBD_IMSG_CFG_SRV");
+		if (send_imsg(env->iev_access, GOTWEBD_IMSG_CFG_SRV,
+		    -1, srv, sizeof(*srv)) == -1)
+			fatal("send_imsg GOTWEBD_IMSG_CFG_SRV");
 
 		/* send per-server access rules */
+		config_set_access_rules(env->iev_access, &srv->access_rules);
 		for (i = 0; i < env->nserver; ++i) {
 			config_set_access_rules(&env->iev_login[i],
 			    &srv->access_rules);
@@ -811,15 +886,18 @@ gotwebd_configure(struct gotwebd *env, uid_t uid, gid_
 			    &srv->access_rules);
 		}
 
-		/* send repositories */
+		/* send repositories and per-repository access rules */
 		TAILQ_FOREACH(repo, &srv->repos, entry) {
+			config_set_repository(env->iev_access, repo);
+			config_set_access_rules(env->iev_access,
+			    &repo->access_rules);
+
 			for (i = 0; i < env->nserver; i++) {
 				config_set_repository(&env->iev_login[i],
 				    repo);
 				config_set_repository(&env->iev_gotweb[i],
 				    repo);
 
-				/* send per-repository access rules */
 				config_set_access_rules(&env->iev_login[i],
 				    &repo->access_rules);
 				config_set_access_rules(&env->iev_gotweb[i],
@@ -827,6 +905,10 @@ gotwebd_configure(struct gotwebd *env, uid_t uid, gid_
 			}
 		}
 
+		if (imsgbuf_flush(&env->iev_access->ibuf) == -1)
+			fatal("imsgbuf_flush");
+		imsg_event_add(env->iev_access);
+
 		for (i = 0; i < env->nserver; i++) {
 			if (imsgbuf_flush(&env->iev_login[i].ibuf) == -1)
 				fatal("imsgbuf_flush");
@@ -850,6 +932,9 @@ gotwebd_configure(struct gotwebd *env, uid_t uid, gid_
 	if (main_compose_gotweb(env, GOTWEBD_IMSG_AUTH_CONF, -1,
 	    &env->auth_config, sizeof(env->auth_config)) == -1)
 		fatal("main_compose_login GOTWEB_IMSG_AUTH_CONF");
+	if (send_imsg(env->iev_access, GOTWEBD_IMSG_AUTH_CONF, -1,
+	    &env->auth_config, sizeof(env->auth_config)) == -1)
+		fatal("send_imsg GOTWEB_IMSG_AUTH_CONF");
 
 	if (main_compose_login(env, GOTWEBD_IMSG_WWW_UID, -1,
 	    &env->www_uid, sizeof(env->www_uid)) == -1)
@@ -857,6 +942,9 @@ gotwebd_configure(struct gotwebd *env, uid_t uid, gid_
 	if (main_compose_gotweb(env, GOTWEBD_IMSG_WWW_UID, -1,
 	    &env->www_uid, sizeof(env->www_uid)) == -1)
 		fatal("main_compose_login GOTWEB_IMSG_WWW_UID");
+	if (send_imsg(env->iev_access, GOTWEBD_IMSG_WWW_UID, -1,
+	    &env->www_uid, sizeof(env->www_uid)) == -1)
+		fatal("send_imsg GOTWEB_IMSG_WWW_UID");
 
 	if (main_compose_login(env, GOTWEBD_IMSG_AUTH_SECRET, -1,
 	    auth_token_secret, sizeof(auth_token_secret)) == -1)
@@ -864,6 +952,9 @@ gotwebd_configure(struct gotwebd *env, uid_t uid, gid_
 	if (main_compose_auth(env, GOTWEBD_IMSG_AUTH_SECRET, -1,
 	    auth_token_secret, sizeof(auth_token_secret)) == -1)
 		fatal("main_compose_gotweb GOTWEB_IMSG_AUTH_SECRET");
+	if (send_imsg(env->iev_access, GOTWEBD_IMSG_AUTH_SECRET, -1,
+	    auth_token_secret, sizeof(auth_token_secret)) == -1)
+		fatal("send_imsg GOTWEB_IMSG_AUTH_SECRET");
 	
 	explicit_bzero(auth_token_secret, sizeof(auth_token_secret));
 
@@ -934,6 +1025,12 @@ gotwebd_shutdown(void)
 	env->iev_auth->ibuf.fd = -1;
 	free(env->iev_auth);
 
+	event_del(&env->iev_access->ev);
+	imsgbuf_clear(&env->iev_access->ibuf);
+	close(env->iev_access->ibuf.fd);
+	env->iev_access->ibuf.fd = -1;
+	free(env->iev_access);
+
 	for (i = 0; i < env->nserver; ++i) {
 		event_del(&env->iev_server[i].ev);
 		imsgbuf_clear(&env->iev_server[i].ibuf);
blob - cc02ed610da95de3915587d1b15df67214fff46e
blob + e3a745e3df95c9878c6d2fe4843e85f4d2aaafbe
--- gotwebd/gotwebd.h
+++ gotwebd/gotwebd.h
@@ -128,11 +128,12 @@ struct got_tree_entry;
 struct got_reflist_head;
 
 enum gotwebd_proc_type {
-	GOTWEBD_PROC_PARENT,
-	GOTWEBD_PROC_AUTH,
-	GOTWEBD_PROC_SERVER,
-	GOTWEBD_PROC_LOGIN,
-	GOTWEBD_PROC_GOTWEB,
+	GOTWEBD_PROC_PARENT,	/* read configuration and start processes */
+	GOTWEBD_PROC_AUTH,	/* authenticate user via sshd + getpeereid(3) */
+	GOTWEBD_PROC_SERVER,	/* FastCGI server on unix-socket or TCP */
+	GOTWEBD_PROC_LOGIN,	/* drop unauthenticated/unauthorized requests */
+	GOTWEBD_PROC_ACCESS,	/* handle auth secrets and access permissions */
+	GOTWEBD_PROC_GOTWEB,	/* read repositories and render HTML */
 };
 
 enum imsg_type {
@@ -425,6 +426,7 @@ struct gotwebd {
 
 	struct imsgev	*iev_parent;
 	struct imsgev	*iev_auth;
+	struct imsgev	*iev_access;
 	struct imsgev	*iev_server;
 	struct imsgev	*iev_login;
 	struct imsgev	*iev_gotweb;
@@ -631,3 +633,4 @@ int auth_privinit(struct gotwebd *env, uid_t, gid_t);
 
 /* access.c */
 enum gotwebd_access access_check(uid_t, struct gotwebd_access_rule_list *);
+void gotwebd_access(struct gotwebd *, int);