commit 9e6d4144ce019af2f5268ad4c9554f3a005c53dc from: Stefan Sperling date: Mon Sep 01 14:21:45 2025 UTC prepare for running access.c as a dedicated child process commit - e240092c03160c453e59bea3aff1b6ec606b1539 commit + 9e6d4144ce019af2f5268ad4c9554f3a005c53dc blob - 29487341845ed5d0e00ec472e2593c862e4503d1 blob + 4021e113af0bec3a58a00ea8dc9e1e77f377c553 --- gotwebd/access.c +++ gotwebd/access.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -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);