implement gotwebd test harness
[got-portable.git] / gotd / gotd.c
blob5581b7217ad0f7a30a56bc775838f98929306494
1 /*
2 * Copyright (c) 2022 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include "got_compat.h"
19 #include <sys/queue.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <sys/wait.h>
26 #include <sys/resource.h>
28 #include <fcntl.h>
29 #include <err.h>
30 #include <errno.h>
31 #include <event.h>
32 #include <limits.h>
33 #include <pwd.h>
34 #include <imsg.h>
35 #include <signal.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <syslog.h>
41 #include <unistd.h>
43 #include "got_error.h"
44 #include "got_opentemp.h"
45 #include "got_path.h"
46 #include "got_repository.h"
47 #include "got_object.h"
48 #include "got_reference.h"
49 #include "got_diff.h"
51 #include "got_lib_delta.h"
52 #include "got_lib_object.h"
53 #include "got_lib_object_cache.h"
54 #include "got_lib_hash.h"
55 #include "got_lib_gitproto.h"
56 #include "got_lib_pack.h"
57 #include "got_lib_repository.h"
59 #include "gotd.h"
60 #include "log.h"
61 #include "listen.h"
62 #include "auth.h"
63 #include "session_read.h"
64 #include "session_write.h"
65 #include "repo_read.h"
66 #include "repo_write.h"
67 #include "notify.h"
68 #include "secrets.h"
70 #ifndef nitems
71 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
72 #endif
74 enum gotd_client_state {
75 GOTD_CLIENT_STATE_NEW,
76 GOTD_CLIENT_STATE_ACCESS_GRANTED,
79 struct gotd_child_proc {
80 pid_t pid;
81 enum gotd_procid type;
82 char repo_name[NAME_MAX];
83 char repo_path[PATH_MAX];
84 int pipe[2];
85 struct gotd_imsgev iev;
86 struct event tmo;
88 TAILQ_ENTRY(gotd_child_proc) entry;
90 TAILQ_HEAD(gotd_procs, gotd_child_proc) procs;
92 struct gotd_client {
93 STAILQ_ENTRY(gotd_client) entry;
94 enum gotd_client_state state;
95 uint32_t id;
96 int fd;
97 struct gotd_imsgev iev;
98 struct event tmo;
99 uid_t euid;
100 gid_t egid;
101 char *username;
102 struct gotd_child_proc *repo;
103 struct gotd_child_proc *auth;
104 struct gotd_child_proc *session;
105 int required_auth;
107 STAILQ_HEAD(gotd_clients, gotd_client);
109 static struct gotd_clients gotd_clients[GOTD_CLIENT_TABLE_SIZE];
110 static SIPHASH_KEY clients_hash_key;
111 volatile int client_cnt;
112 static struct timeval auth_timeout = { 5, 0 };
113 static struct gotd gotd;
115 void gotd_sighdlr(int sig, short event, void *arg);
116 static void gotd_shutdown(void);
117 static const struct got_error *start_session_child(struct gotd_client *,
118 struct gotd_repo *, char *, const char *, int, int);
119 static const struct got_error *start_repo_child(struct gotd_client *,
120 enum gotd_procid, struct gotd_repo *, char *, const char *, int, int);
121 static const struct got_error *start_auth_child(struct gotd_client *, int,
122 struct gotd_repo *, char *, const char *, int, int);
123 static void kill_proc(struct gotd_child_proc *, int);
124 static void disconnect(struct gotd_client *);
125 static void drop_privs(struct passwd *);
127 __dead static void
128 usage(void)
130 fprintf(stderr, "usage: %s [-dnv] [-f config-file] [-s secrets]\n",
131 getprogname());
132 exit(1);
135 static void
136 drop_privs(struct passwd *pw)
138 /* Drop root privileges. */
139 if (setgid(pw->pw_gid) == -1)
140 fatal("setgid %d failed", pw->pw_gid);
141 if (setuid(pw->pw_uid) == -1)
142 fatal("setuid %d failed", pw->pw_uid);
145 static int
146 unix_socket_listen(const char *unix_socket_path, uid_t uid, gid_t gid)
148 struct sockaddr_un sun;
149 int fd = -1;
150 mode_t old_umask, mode;
151 int sock_flags = SOCK_STREAM | SOCK_NONBLOCK;
153 #ifdef SOCK_CLOEXEC
154 sock_flags |= SOCK_CLOEXEC;
155 #endif
157 fd = socket(AF_UNIX, sock_flags, 0);
158 if (fd == -1) {
159 log_warn("socket");
160 return -1;
163 sun.sun_family = AF_UNIX;
164 if (strlcpy(sun.sun_path, unix_socket_path,
165 sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
166 log_warnx("%s: name too long", unix_socket_path);
167 close(fd);
168 return -1;
171 if (unlink(unix_socket_path) == -1) {
172 if (errno != ENOENT) {
173 log_warn("unlink %s", unix_socket_path);
174 close(fd);
175 return -1;
179 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
180 mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
182 if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
183 log_warn("bind: %s", unix_socket_path);
184 close(fd);
185 umask(old_umask);
186 return -1;
189 umask(old_umask);
191 if (chmod(unix_socket_path, mode) == -1) {
192 log_warn("chmod %o %s", mode, unix_socket_path);
193 close(fd);
194 unlink(unix_socket_path);
195 return -1;
198 if (chown(unix_socket_path, uid, gid) == -1) {
199 log_warn("chown %s uid=%d gid=%d", unix_socket_path, uid, gid);
200 close(fd);
201 unlink(unix_socket_path);
202 return -1;
205 if (listen(fd, GOTD_UNIX_SOCKET_BACKLOG) == -1) {
206 log_warn("listen");
207 close(fd);
208 unlink(unix_socket_path);
209 return -1;
212 return fd;
215 static uint64_t
216 client_hash(uint32_t client_id)
218 return SipHash24(&clients_hash_key, &client_id, sizeof(client_id));
221 static void
222 add_client(struct gotd_client *client)
224 uint64_t slot = client_hash(client->id) % nitems(gotd_clients);
225 STAILQ_INSERT_HEAD(&gotd_clients[slot], client, entry);
226 client_cnt++;
229 static struct gotd_client *
230 find_client(uint32_t client_id)
232 uint64_t slot;
233 struct gotd_client *c;
235 slot = client_hash(client_id) % nitems(gotd_clients);
236 STAILQ_FOREACH(c, &gotd_clients[slot], entry) {
237 if (c->id == client_id)
238 return c;
241 return NULL;
244 static struct gotd_client *
245 find_client_by_proc_fd(int fd)
247 uint64_t slot;
249 for (slot = 0; slot < nitems(gotd_clients); slot++) {
250 struct gotd_client *c;
252 STAILQ_FOREACH(c, &gotd_clients[slot], entry) {
253 if (c->repo && c->repo->iev.ibuf.fd == fd)
254 return c;
255 if (c->auth && c->auth->iev.ibuf.fd == fd)
256 return c;
257 if (c->session && c->session->iev.ibuf.fd == fd)
258 return c;
262 return NULL;
265 static int
266 client_is_reading(struct gotd_client *client)
268 return (client->required_auth &
269 (GOTD_AUTH_READ | GOTD_AUTH_WRITE)) == GOTD_AUTH_READ;
272 static int
273 client_is_writing(struct gotd_client *client)
275 return (client->required_auth &
276 (GOTD_AUTH_READ | GOTD_AUTH_WRITE)) ==
277 (GOTD_AUTH_READ | GOTD_AUTH_WRITE);
280 static const struct got_error *
281 ensure_client_is_not_writing(struct gotd_client *client)
283 if (client_is_writing(client)) {
284 return got_error_fmt(GOT_ERR_BAD_PACKET,
285 "uid %d made a read-request but is writing to "
286 "a repository", client->euid);
289 return NULL;
292 static const struct got_error *
293 ensure_client_is_not_reading(struct gotd_client *client)
295 if (client_is_reading(client)) {
296 return got_error_fmt(GOT_ERR_BAD_PACKET,
297 "uid %d made a write-request but is reading from "
298 "a repository", client->euid);
301 return NULL;
304 static void
305 proc_done(struct gotd_child_proc *proc)
307 struct gotd_client *client;
309 TAILQ_REMOVE(&procs, proc, entry);
311 client = find_client_by_proc_fd(proc->iev.ibuf.fd);
312 if (client != NULL) {
313 if (proc == client->repo)
314 client->repo = NULL;
315 if (proc == client->auth)
316 client->auth = NULL;
317 if (proc == client->session)
318 client->session = NULL;
319 disconnect(client);
322 if (proc == gotd.notify_proc)
323 gotd.notify_proc = NULL;
325 evtimer_del(&proc->tmo);
327 if (proc->iev.ibuf.fd != -1) {
328 event_del(&proc->iev.ev);
329 imsgbuf_clear(&proc->iev.ibuf);
330 close(proc->iev.ibuf.fd);
333 free(proc);
336 static void
337 kill_repo_proc(struct gotd_client *client)
339 if (client->repo == NULL)
340 return;
342 kill_proc(client->repo, 0);
343 client->repo = NULL;
346 static void
347 kill_auth_proc(struct gotd_client *client)
349 if (client->auth == NULL)
350 return;
352 kill_proc(client->auth, 0);
353 client->auth = NULL;
356 static void
357 kill_session_proc(struct gotd_client *client)
359 if (client->session == NULL)
360 return;
362 kill_proc(client->session, 0);
363 client->session = NULL;
366 static void
367 disconnect(struct gotd_client *client)
369 struct gotd_imsg_disconnect idisconnect;
370 struct gotd_child_proc *listen_proc = gotd.listen_proc;
371 uint64_t slot;
373 log_debug("uid %d: disconnecting", client->euid);
375 kill_auth_proc(client);
376 kill_session_proc(client);
377 kill_repo_proc(client);
379 idisconnect.client_id = client->id;
380 if (gotd_imsg_compose_event(&listen_proc->iev,
381 GOTD_IMSG_DISCONNECT, PROC_GOTD, -1,
382 &idisconnect, sizeof(idisconnect)) == -1)
383 log_warn("imsg compose DISCONNECT");
385 slot = client_hash(client->id) % nitems(gotd_clients);
386 STAILQ_REMOVE(&gotd_clients[slot], client, gotd_client, entry);
387 imsgbuf_clear(&client->iev.ibuf);
388 event_del(&client->iev.ev);
389 evtimer_del(&client->tmo);
390 if (client->fd != -1)
391 close(client->fd);
392 else if (client->iev.ibuf.fd != -1)
393 close(client->iev.ibuf.fd);
394 free(client->username);
395 free(client);
396 client_cnt--;
399 static void
400 disconnect_on_error(struct gotd_client *client, const struct got_error *err)
402 struct imsgbuf ibuf;
404 if (err->code != GOT_ERR_EOF) {
405 log_warnx("uid %d: %s", client->euid, err->msg);
406 if (client->fd != -1) {
407 if (imsgbuf_init(&ibuf, client->fd) != -1) {
408 gotd_imsg_send_error(&ibuf, 0, PROC_GOTD,
409 err);
410 imsgbuf_clear(&ibuf);
411 } else
412 log_warn("%s: imsgbuf_init failed", __func__);
415 disconnect(client);
418 static const struct got_error *
419 send_repo_info(struct gotd_imsgev *iev, struct gotd_repo *repo)
421 const struct got_error *err = NULL;
422 struct gotd_imsg_info_repo irepo;
424 memset(&irepo, 0, sizeof(irepo));
426 if (strlcpy(irepo.repo_name, repo->name, sizeof(irepo.repo_name))
427 >= sizeof(irepo.repo_name))
428 return got_error_msg(GOT_ERR_NO_SPACE, "repo name too long");
429 if (strlcpy(irepo.repo_path, repo->path, sizeof(irepo.repo_path))
430 >= sizeof(irepo.repo_path))
431 return got_error_msg(GOT_ERR_NO_SPACE, "repo path too long");
433 if (gotd_imsg_compose_event(iev, GOTD_IMSG_INFO_REPO, PROC_GOTD, -1,
434 &irepo, sizeof(irepo)) == -1) {
435 err = got_error_from_errno("imsg compose INFO_REPO");
436 if (err)
437 return err;
440 return NULL;
443 static const struct got_error *
444 send_client_info(struct gotd_imsgev *iev, struct gotd_client *client)
446 const struct got_error *err = NULL;
447 struct gotd_imsg_info_client iclient;
448 struct gotd_child_proc *proc;
450 memset(&iclient, 0, sizeof(iclient));
451 iclient.euid = client->euid;
452 iclient.egid = client->egid;
454 proc = client->repo;
455 if (proc) {
456 if (strlcpy(iclient.repo_name, proc->repo_path,
457 sizeof(iclient.repo_name)) >= sizeof(iclient.repo_name)) {
458 return got_error_msg(GOT_ERR_NO_SPACE,
459 "repo name too long");
461 if (client_is_writing(client))
462 iclient.is_writing = 1;
464 iclient.repo_child_pid = proc->pid;
467 if (client->session)
468 iclient.session_child_pid = client->session->pid;
470 if (gotd_imsg_compose_event(iev, GOTD_IMSG_INFO_CLIENT, PROC_GOTD, -1,
471 &iclient, sizeof(iclient)) == -1) {
472 err = got_error_from_errno("imsg compose INFO_CLIENT");
473 if (err)
474 return err;
477 return NULL;
480 static const struct got_error *
481 send_info(struct gotd_client *client)
483 const struct got_error *err = NULL;
484 struct gotd_imsg_info info;
485 uint64_t slot;
486 struct gotd_repo *repo;
488 if (client->euid != 0)
489 return got_error_set_errno(EPERM, "info");
491 info.pid = gotd.pid;
492 info.verbosity = gotd.verbosity;
493 info.nrepos = gotd.nrepos;
494 info.nclients = client_cnt - 1;
496 if (gotd_imsg_compose_event(&client->iev, GOTD_IMSG_INFO, PROC_GOTD, -1,
497 &info, sizeof(info)) == -1) {
498 err = got_error_from_errno("imsg compose INFO");
499 if (err)
500 return err;
503 TAILQ_FOREACH(repo, &gotd.repos, entry) {
504 err = send_repo_info(&client->iev, repo);
505 if (err)
506 return err;
509 for (slot = 0; slot < nitems(gotd_clients); slot++) {
510 struct gotd_client *c;
511 STAILQ_FOREACH(c, &gotd_clients[slot], entry) {
512 if (c->id == client->id)
513 continue;
514 err = send_client_info(&client->iev, c);
515 if (err)
516 return err;
520 return NULL;
523 static const struct got_error *
524 stop_gotd(struct gotd_client *client)
526 if (client->euid != 0)
527 return got_error_set_errno(EPERM, "stop");
529 gotd_shutdown();
530 /* NOTREACHED */
531 return NULL;
534 static const struct got_error *
535 start_client_authentication(struct gotd_client *client, struct imsg *imsg)
537 const struct got_error *err;
538 struct gotd_imsg_list_refs ireq;
539 struct gotd_repo *repo = NULL;
540 size_t datalen;
542 log_debug("list-refs request from uid %d", client->euid);
544 if (client->state != GOTD_CLIENT_STATE_NEW)
545 return got_error_msg(GOT_ERR_BAD_REQUEST,
546 "unexpected list-refs request received");
548 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
549 if (datalen != sizeof(ireq))
550 return got_error(GOT_ERR_PRIVSEP_LEN);
552 memcpy(&ireq, imsg->data, datalen);
554 if (ireq.client_is_reading) {
555 err = ensure_client_is_not_writing(client);
556 if (err)
557 return err;
558 repo = gotd_find_repo_by_name(ireq.repo_name, &gotd.repos);
559 if (repo == NULL)
560 return got_error(GOT_ERR_NOT_GIT_REPO);
561 err = start_auth_child(client, GOTD_AUTH_READ, repo,
562 gotd.argv0, gotd.confpath, gotd.daemonize,
563 gotd.verbosity);
564 if (err)
565 return err;
566 } else {
567 err = ensure_client_is_not_reading(client);
568 if (err)
569 return err;
570 repo = gotd_find_repo_by_name(ireq.repo_name, &gotd.repos);
571 if (repo == NULL)
572 return got_error(GOT_ERR_NOT_GIT_REPO);
573 err = start_auth_child(client,
574 GOTD_AUTH_READ | GOTD_AUTH_WRITE,
575 repo, gotd.argv0, gotd.confpath, gotd.daemonize,
576 gotd.verbosity);
577 if (err)
578 return err;
581 evtimer_add(&client->tmo, &auth_timeout);
583 /* Flow continues upon authentication success/failure or timeout. */
584 return NULL;
587 static void
588 gotd_request(int fd, short events, void *arg)
590 struct gotd_imsgev *iev = arg;
591 struct imsgbuf *ibuf = &iev->ibuf;
592 struct gotd_client *client = iev->handler_arg;
593 const struct got_error *err = NULL;
594 struct imsg imsg;
595 ssize_t n;
597 if (events & EV_WRITE) {
598 err = gotd_imsg_flush(ibuf);
599 if (err) {
601 * The client has closed its socket. This can
602 * happen when Git clients are done sending
603 * pack file data.
605 if (err->code == GOT_ERR_ERRNO && errno == EPIPE) {
606 disconnect(client);
607 return;
609 disconnect_on_error(client, err);
610 return;
613 /* Disconnect gotctl(8) if all messages have been sent. */
614 if (!client_is_reading(client) && !client_is_writing(client) &&
615 imsgbuf_queuelen(ibuf) == 0) {
616 disconnect(client);
617 return;
621 if (events & EV_READ) {
622 n = imsgbuf_read(ibuf);
623 if (n == -1) {
624 err = got_error_from_errno("imsgbuf_read");
625 disconnect_on_error(client, err);
626 return;
628 if (n == 0) {
629 err = got_error(GOT_ERR_EOF);
630 disconnect_on_error(client, err);
631 return;
635 while (err == NULL) {
636 n = imsg_get(ibuf, &imsg);
637 if (n == -1) {
638 err = got_error_from_errno("imsg_get");
639 break;
641 if (n == 0)
642 break;
644 evtimer_del(&client->tmo);
646 switch (imsg.hdr.type) {
647 case GOTD_IMSG_INFO:
648 err = send_info(client);
649 break;
650 case GOTD_IMSG_STOP:
651 err = stop_gotd(client);
652 break;
653 case GOTD_IMSG_LIST_REFS:
654 err = start_client_authentication(client, &imsg);
655 break;
656 default:
657 log_debug("unexpected imsg %d", imsg.hdr.type);
658 err = got_error(GOT_ERR_PRIVSEP_MSG);
659 break;
662 imsg_free(&imsg);
665 if (err) {
666 disconnect_on_error(client, err);
667 } else {
668 gotd_imsg_event_add(&client->iev);
672 static void
673 gotd_auth_timeout(int fd, short events, void *arg)
675 struct gotd_client *client = arg;
677 log_debug("disconnecting uid %d due to authentication timeout",
678 client->euid);
679 disconnect(client);
682 static const struct got_error *
683 recv_connect(uint32_t *client_id, struct imsg *imsg)
685 const struct got_error *err = NULL;
686 struct gotd_imsg_connect iconnect;
687 size_t datalen;
688 struct gotd_client *client = NULL;
690 *client_id = 0;
692 datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
693 if (datalen != sizeof(iconnect))
694 return got_error(GOT_ERR_PRIVSEP_LEN);
695 memcpy(&iconnect, imsg->data, sizeof(iconnect));
697 if (find_client(iconnect.client_id)) {
698 err = got_error_msg(GOT_ERR_CLIENT_ID, "duplicate client ID");
699 goto done;
702 client = calloc(1, sizeof(*client));
703 if (client == NULL) {
704 err = got_error_from_errno("calloc");
705 goto done;
708 *client_id = iconnect.client_id;
710 client->state = GOTD_CLIENT_STATE_NEW;
711 client->id = iconnect.client_id;
712 /* The auth process will verify UID/GID for us. */
713 client->euid = iconnect.euid;
714 client->egid = iconnect.egid;
716 client->fd = imsg_get_fd(imsg);
717 if (client->fd == -1) {
718 err = got_error(GOT_ERR_PRIVSEP_NO_FD);
719 goto done;
721 if (imsgbuf_init(&client->iev.ibuf, client->fd) == -1) {
722 err = got_error_from_errno("imsgbuf_init");
723 goto done;
725 imsgbuf_allow_fdpass(&client->iev.ibuf);
726 client->iev.handler = gotd_request;
727 client->iev.events = EV_READ;
728 client->iev.handler_arg = client;
730 event_set(&client->iev.ev, client->fd, EV_READ, gotd_request,
731 &client->iev);
732 gotd_imsg_event_add(&client->iev);
734 evtimer_set(&client->tmo, gotd_auth_timeout, client);
736 add_client(client);
737 log_debug("%s: new client uid %d connected on fd %d", __func__,
738 client->euid, client->fd);
739 done:
740 if (err && client) {
741 struct gotd_child_proc *listen_proc = gotd.listen_proc;
742 struct gotd_imsg_disconnect idisconnect;
744 idisconnect.client_id = client->id;
745 if (gotd_imsg_compose_event(&listen_proc->iev,
746 GOTD_IMSG_DISCONNECT, PROC_GOTD, -1,
747 &idisconnect, sizeof(idisconnect)) == -1)
748 log_warn("imsg compose DISCONNECT");
750 if (client->fd != -1)
751 close(client->fd);
752 free(client);
755 return err;
758 static const char *gotd_proc_names[PROC_MAX] = {
759 "parent",
760 "listen",
761 "auth",
762 "session_read",
763 "session_write",
764 "repo_read",
765 "repo_write",
766 "gitwrapper",
767 "notify"
770 static void
771 kill_proc(struct gotd_child_proc *proc, int fatal)
773 struct timeval tv = { 5, 0 };
775 log_debug("kill -%d %d", fatal ? SIGKILL : SIGTERM, proc->pid);
777 if (proc->iev.ibuf.fd != -1) {
778 event_del(&proc->iev.ev);
779 imsgbuf_clear(&proc->iev.ibuf);
780 close(proc->iev.ibuf.fd);
781 proc->iev.ibuf.fd = -1;
784 if (!evtimer_pending(&proc->tmo, NULL) && !fatal)
785 evtimer_add(&proc->tmo, &tv);
787 if (fatal) {
788 log_warnx("sending SIGKILL to PID %d", proc->pid);
789 kill(proc->pid, SIGKILL);
790 } else
791 kill(proc->pid, SIGTERM);
794 static void
795 kill_proc_timeout(int fd, short ev, void *d)
797 struct gotd_child_proc *proc = d;
799 log_warnx("timeout waiting for PID %d to terminate;"
800 " retrying with force", proc->pid);
801 kill_proc(proc, 1);
804 static void
805 gotd_shutdown(void)
807 uint64_t slot;
809 log_debug("shutting down");
810 for (slot = 0; slot < nitems(gotd_clients); slot++) {
811 struct gotd_client *c, *tmp;
813 STAILQ_FOREACH_SAFE(c, &gotd_clients[slot], entry, tmp)
814 disconnect(c);
817 kill_proc(gotd.listen_proc, 0);
819 log_info("terminating");
820 exit(0);
823 static struct gotd_child_proc *
824 find_proc_by_pid(pid_t pid)
826 struct gotd_child_proc *proc = NULL;
828 TAILQ_FOREACH(proc, &procs, entry)
829 if (proc->pid == pid)
830 break;
832 return proc;
835 void
836 gotd_sighdlr(int sig, short event, void *arg)
838 struct gotd_child_proc *proc;
839 pid_t pid;
840 int status;
843 * Normal signal handler rules don't apply because libevent
844 * decouples for us.
847 switch (sig) {
848 case SIGHUP:
849 log_info("%s: ignoring SIGHUP", __func__);
850 break;
851 case SIGUSR1:
852 log_info("%s: ignoring SIGUSR1", __func__);
853 break;
854 case SIGTERM:
855 case SIGINT:
856 gotd_shutdown();
857 break;
858 case SIGCHLD:
859 for (;;) {
860 pid = waitpid(WAIT_ANY, &status, WNOHANG);
861 if (pid == -1) {
862 if (errno == EINTR)
863 continue;
864 if (errno == ECHILD)
865 break;
866 fatal("waitpid");
868 if (pid == 0)
869 break;
871 log_debug("reaped pid %d", pid);
872 proc = find_proc_by_pid(pid);
873 if (proc == NULL) {
874 log_info("caught exit of unknown child %d",
875 pid);
876 continue;
879 if (WIFSIGNALED(status)) {
880 log_warnx("child PID %d terminated with"
881 " signal %d", pid, WTERMSIG(status));
884 proc_done(proc);
886 break;
887 default:
888 fatalx("unexpected signal");
892 static const struct got_error *
893 ensure_proc_is_reading(struct gotd_client *client,
894 struct gotd_child_proc *proc)
896 if (!client_is_reading(client)) {
897 kill_proc(proc, 1);
898 return got_error_fmt(GOT_ERR_BAD_PACKET,
899 "PID %d handled a read-request for uid %d but this "
900 "user is not reading from a repository", proc->pid,
901 client->euid);
904 return NULL;
907 static const struct got_error *
908 ensure_proc_is_writing(struct gotd_client *client,
909 struct gotd_child_proc *proc)
911 if (!client_is_writing(client)) {
912 kill_proc(proc, 1);
913 return got_error_fmt(GOT_ERR_BAD_PACKET,
914 "PID %d handled a write-request for uid %d but this "
915 "user is not writing to a repository", proc->pid,
916 client->euid);
919 return NULL;
922 static int
923 verify_imsg_src(struct gotd_client *client, struct gotd_child_proc *proc,
924 struct imsg *imsg)
926 const struct got_error *err;
927 int ret = 0;
929 if (proc->type == PROC_REPO_READ || proc->type == PROC_REPO_WRITE) {
930 if (client->repo == NULL)
931 fatalx("no process found for uid %d", client->euid);
932 if (proc->pid != client->repo->pid) {
933 kill_proc(proc, 1);
934 log_warnx("received message from PID %d for uid %d, "
935 "while PID %d is the process serving this user",
936 proc->pid, client->euid, client->repo->pid);
937 return 0;
940 if (proc->type == PROC_SESSION_READ ||
941 proc->type == PROC_SESSION_WRITE) {
942 if (client->session == NULL) {
943 log_warnx("no session found for uid %d", client->euid);
944 return 0;
946 if (proc->pid != client->session->pid) {
947 kill_proc(proc, 1);
948 log_warnx("received message from PID %d for uid %d, "
949 "while PID %d is the process serving this user",
950 proc->pid, client->euid, client->session->pid);
951 return 0;
955 switch (imsg->hdr.type) {
956 case GOTD_IMSG_ERROR:
957 ret = 1;
958 break;
959 case GOTD_IMSG_CONNECT:
960 if (proc->type != PROC_LISTEN) {
961 err = got_error_fmt(GOT_ERR_BAD_PACKET,
962 "new connection for uid %d from PID %d "
963 "which is not the listen process",
964 client->euid, proc->pid);
965 } else
966 ret = 1;
967 break;
968 case GOTD_IMSG_ACCESS_GRANTED:
969 if (proc->type != PROC_AUTH) {
970 err = got_error_fmt(GOT_ERR_BAD_PACKET,
971 "authentication of uid %d from PID %d "
972 "which is not the auth process",
973 client->euid, proc->pid);
974 } else
975 ret = 1;
976 break;
977 case GOTD_IMSG_CLIENT_SESSION_READY:
978 if (proc->type != PROC_SESSION_READ &&
979 proc->type != PROC_SESSION_WRITE) {
980 err = got_error_fmt(GOT_ERR_BAD_PACKET,
981 "unexpected \"ready\" signal from PID %d",
982 proc->pid);
983 } else
984 ret = 1;
985 break;
986 case GOTD_IMSG_REPO_CHILD_READY:
987 if (proc->type != PROC_REPO_READ &&
988 proc->type != PROC_REPO_WRITE) {
989 err = got_error_fmt(GOT_ERR_BAD_PACKET,
990 "unexpected \"ready\" signal from PID %d",
991 proc->pid);
992 } else
993 ret = 1;
994 break;
995 case GOTD_IMSG_PACKFILE_DONE:
996 err = ensure_proc_is_reading(client, proc);
997 if (err)
998 log_warnx("uid %d: %s", client->euid, err->msg);
999 else
1000 ret = 1;
1001 break;
1002 case GOTD_IMSG_PACKFILE_INSTALL:
1003 case GOTD_IMSG_REF_UPDATES_START:
1004 case GOTD_IMSG_REF_UPDATE:
1005 err = ensure_proc_is_writing(client, proc);
1006 if (err)
1007 log_warnx("uid %d: %s", client->euid, err->msg);
1008 else
1009 ret = 1;
1010 break;
1011 default:
1012 log_debug("%s: unexpected imsg %d", __func__, imsg->hdr.type);
1013 break;
1016 return ret;
1019 static const struct got_error *
1020 connect_repo_child(struct gotd_client *client,
1021 struct gotd_child_proc *repo_proc)
1023 static const struct got_error *err;
1024 struct gotd_imsgev *session_iev = &client->session->iev;
1025 struct gotd_imsg_connect_repo_child ireq;
1026 int pipe[2];
1027 int sock_flags = SOCK_STREAM | SOCK_NONBLOCK;
1029 #ifdef SOCK_CLOEXEC
1030 sock_flags |= SOCK_CLOEXEC;
1031 #endif
1033 if (client->state != GOTD_CLIENT_STATE_ACCESS_GRANTED)
1034 return got_error_msg(GOT_ERR_BAD_REQUEST,
1035 "unexpected repo child ready signal received");
1037 if (socketpair(AF_UNIX, sock_flags, PF_UNSPEC, pipe) == -1)
1038 fatal("socketpair");
1040 memset(&ireq, 0, sizeof(ireq));
1041 ireq.proc_id = repo_proc->type;
1043 /* Pass repo child pipe to session child process. */
1044 if (gotd_imsg_compose_event(session_iev, GOTD_IMSG_CONNECT_REPO_CHILD,
1045 PROC_GOTD, pipe[0], &ireq, sizeof(ireq)) == -1) {
1046 err = got_error_from_errno("imsg compose CONNECT_REPO_CHILD");
1047 close(pipe[0]);
1048 close(pipe[1]);
1049 return err;
1052 /* Pass session child pipe to repo child process. */
1053 if (gotd_imsg_compose_event(&repo_proc->iev,
1054 GOTD_IMSG_CONNECT_REPO_CHILD, PROC_GOTD, pipe[1], NULL, 0) == -1) {
1055 err = got_error_from_errno("imsg compose CONNECT_REPO_CHILD");
1056 close(pipe[1]);
1057 return err;
1060 return NULL;
1063 static void
1064 gotd_dispatch_listener(int fd, short event, void *arg)
1066 const struct got_error *err = NULL;
1067 struct gotd_imsgev *iev = arg;
1068 struct imsgbuf *ibuf = &iev->ibuf;
1069 struct gotd_child_proc *proc = gotd.listen_proc;
1070 ssize_t n;
1071 int shut = 0;
1072 struct imsg imsg;
1074 if (proc->iev.ibuf.fd != fd)
1075 fatalx("%s: unexpected fd %d", __func__, fd);
1077 if (event & EV_READ) {
1078 if ((n = imsgbuf_read(ibuf)) == -1)
1079 fatal("imsg_read error");
1080 if (n == 0) {
1081 /* Connection closed. */
1082 shut = 1;
1083 goto done;
1087 if (event & EV_WRITE) {
1088 err = gotd_imsg_flush(ibuf);
1089 if (err)
1090 fatalx("%s", err->msg);
1093 for (;;) {
1094 const struct got_error *err = NULL;
1095 struct gotd_client *client = NULL;
1096 uint32_t client_id = 0;
1097 int do_disconnect = 0;
1099 if ((n = imsg_get(ibuf, &imsg)) == -1)
1100 fatal("%s: imsg_get error", __func__);
1101 if (n == 0) /* No more messages. */
1102 break;
1104 switch (imsg.hdr.type) {
1105 case GOTD_IMSG_ERROR:
1106 do_disconnect = 1;
1107 err = gotd_imsg_recv_error(&client_id, &imsg);
1108 break;
1109 case GOTD_IMSG_CONNECT:
1110 err = recv_connect(&client_id, &imsg);
1111 break;
1112 default:
1113 log_debug("unexpected imsg %d", imsg.hdr.type);
1114 break;
1117 client = find_client(client_id);
1118 if (client == NULL) {
1119 log_warnx("%s: client not found", __func__);
1120 imsg_free(&imsg);
1121 continue;
1124 if (err)
1125 log_warnx("uid %d: %s", client->euid, err->msg);
1127 if (do_disconnect) {
1128 if (err)
1129 disconnect_on_error(client, err);
1130 else
1131 disconnect(client);
1134 imsg_free(&imsg);
1136 done:
1137 if (!shut) {
1138 gotd_imsg_event_add(iev);
1139 } else {
1140 /* This pipe is dead. Remove its event handler */
1141 event_del(&iev->ev);
1142 event_loopexit(NULL);
1146 static void
1147 gotd_dispatch_notifier(int fd, short event, void *arg)
1149 const struct got_error *err = NULL;
1150 struct gotd_imsgev *iev = arg;
1151 struct imsgbuf *ibuf = &iev->ibuf;
1152 struct gotd_child_proc *proc = gotd.notify_proc;
1153 ssize_t n;
1154 int shut = 0;
1155 struct imsg imsg;
1157 if (proc->iev.ibuf.fd != fd)
1158 fatalx("%s: unexpected fd %d", __func__, fd);
1160 if (event & EV_READ) {
1161 if ((n = imsgbuf_read(ibuf)) == -1)
1162 fatal("imsg_read error");
1163 if (n == 0) {
1164 /* Connection closed. */
1165 shut = 1;
1166 goto done;
1170 if (event & EV_WRITE) {
1171 err = gotd_imsg_flush(ibuf);
1172 if (err)
1173 fatalx("%s", err->msg);
1176 for (;;) {
1177 if ((n = imsg_get(ibuf, &imsg)) == -1)
1178 fatal("%s: imsg_get error", __func__);
1179 if (n == 0) /* No more messages. */
1180 break;
1182 switch (imsg.hdr.type) {
1183 default:
1184 log_debug("unexpected imsg %d", imsg.hdr.type);
1185 break;
1188 imsg_free(&imsg);
1190 done:
1191 if (!shut) {
1192 gotd_imsg_event_add(iev);
1193 } else {
1194 /* This pipe is dead. Remove its event handler */
1195 event_del(&iev->ev);
1198 * Do not exit all of gotd if the notification handler dies.
1199 * We can continue operating without notifications until an
1200 * operator intervenes.
1202 log_warnx("notify child process (pid %d) closed its imsg pipe "
1203 "unexpectedly", proc->pid);
1204 proc_done(proc);
1208 static void
1209 gotd_dispatch_auth_child(int fd, short event, void *arg)
1211 const struct got_error *err = NULL;
1212 struct gotd_imsgev *iev = arg;
1213 struct imsgbuf *ibuf = &iev->ibuf;
1214 struct gotd_client *client;
1215 struct gotd_repo *repo = NULL;
1216 ssize_t n;
1217 int shut = 0;
1218 struct imsg imsg;
1219 uint32_t client_id = 0;
1220 int do_disconnect = 0;
1221 size_t datalen;
1223 client = find_client_by_proc_fd(fd);
1224 if (client == NULL) {
1225 /* Can happen during process teardown. */
1226 warnx("cannot find client for fd %d", fd);
1227 shut = 1;
1228 goto done;
1231 if (client->auth == NULL)
1232 fatalx("cannot find auth child process for fd %d", fd);
1234 if (event & EV_READ) {
1235 if ((n = imsgbuf_read(ibuf)) == -1)
1236 fatal("imsg_read error");
1237 if (n == 0) {
1238 /* Connection closed. */
1239 shut = 1;
1240 goto done;
1244 if (event & EV_WRITE) {
1245 err = gotd_imsg_flush(ibuf);
1246 if (err)
1247 fatalx("%s", err->msg);
1248 goto done;
1251 if (client->auth->iev.ibuf.fd != fd)
1252 fatalx("%s: unexpected fd %d", __func__, fd);
1254 if ((n = imsg_get(ibuf, &imsg)) == -1)
1255 fatal("%s: imsg_get error", __func__);
1256 if (n == 0) /* No more messages. */
1257 return;
1259 evtimer_del(&client->tmo);
1261 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
1263 switch (imsg.hdr.type) {
1264 case GOTD_IMSG_ERROR:
1265 do_disconnect = 1;
1266 err = gotd_imsg_recv_error(&client_id, &imsg);
1267 break;
1268 case GOTD_IMSG_ACCESS_GRANTED:
1269 if (client->state != GOTD_CLIENT_STATE_NEW) {
1270 do_disconnect = 1;
1271 err = got_error(GOT_ERR_PRIVSEP_MSG);
1273 break;
1274 default:
1275 do_disconnect = 1;
1276 log_debug("unexpected imsg %d", imsg.hdr.type);
1277 break;
1280 if (!verify_imsg_src(client, client->auth, &imsg)) {
1281 do_disconnect = 1;
1282 log_debug("dropping imsg type %d from PID %d",
1283 imsg.hdr.type, client->auth->pid);
1286 if (do_disconnect) {
1287 if (err)
1288 disconnect_on_error(client, err);
1289 else
1290 disconnect(client);
1291 imsg_free(&imsg);
1292 return;
1295 client->state = GOTD_CLIENT_STATE_ACCESS_GRANTED;
1296 if (datalen > 0)
1297 client->username = strndup(imsg.data, datalen);
1298 imsg_free(&imsg);
1299 if (client->username == NULL &&
1300 asprintf(&client->username, "uid %d", client->euid) == -1) {
1301 err = got_error_from_errno("asprintf");
1302 goto done;
1305 repo = gotd_find_repo_by_name(client->auth->repo_name, &gotd.repos);
1306 if (repo == NULL) {
1307 err = got_error(GOT_ERR_NOT_GIT_REPO);
1308 goto done;
1310 kill_auth_proc(client);
1312 log_info("authenticated %s for repository %s",
1313 client->username, repo->name);
1315 err = start_session_child(client, repo, gotd.argv0,
1316 gotd.confpath, gotd.daemonize, gotd.verbosity);
1317 if (err)
1318 goto done;
1319 done:
1320 if (err)
1321 log_warnx("uid %d: %s", client->euid, err->msg);
1323 /* We might have killed the auth process by now. */
1324 if (client->auth != NULL) {
1325 if (!shut) {
1326 gotd_imsg_event_add(iev);
1327 } else {
1328 /* This pipe is dead. Remove its event handler */
1329 event_del(&iev->ev);
1334 static const struct got_error *
1335 connect_session(struct gotd_client *client)
1337 const struct got_error *err = NULL;
1338 struct gotd_imsg_connect iconnect;
1339 int s;
1340 struct ibuf *wbuf;
1342 memset(&iconnect, 0, sizeof(iconnect));
1344 s = dup(client->fd);
1345 if (s == -1)
1346 return got_error_from_errno("dup");
1348 iconnect.client_id = client->id;
1349 iconnect.euid = client->euid;
1350 iconnect.egid = client->egid;
1351 iconnect.username_len = strlen(client->username);
1353 wbuf = imsg_create(&client->session->iev.ibuf, GOTD_IMSG_CONNECT,
1354 PROC_GOTD, gotd.pid, sizeof(iconnect) + iconnect.username_len);
1355 if (wbuf == NULL) {
1356 err = got_error_from_errno("imsg compose CONNECT");
1357 close(s);
1358 return err;
1360 if (imsg_add(wbuf, &iconnect, sizeof(iconnect)) == -1) {
1361 close(s);
1362 return got_error_from_errno("imsg_add CONNECT");
1364 if (imsg_add(wbuf, client->username, iconnect.username_len) == -1) {
1365 close(s);
1366 return got_error_from_errno("imsg_add CONNECT");
1369 ibuf_fd_set(wbuf, s);
1370 imsg_close(&client->session->iev.ibuf, wbuf);
1371 gotd_imsg_event_add(&client->session->iev);
1374 * We are no longer interested in messages from this client.
1375 * Further client requests will be handled by the session process.
1377 imsgbuf_clear(&client->iev.ibuf);
1378 imsgbuf_clear(&client->iev.ibuf);
1379 event_del(&client->iev.ev);
1380 client->fd = -1; /* will be closed via copy in client->iev.ibuf.fd */
1382 return NULL;
1385 static void
1386 gotd_dispatch_client_session(int fd, short event, void *arg)
1388 const struct got_error *err = NULL;
1389 struct gotd_imsgev *iev = arg;
1390 struct imsgbuf *ibuf = &iev->ibuf;
1391 struct gotd_child_proc *proc = NULL;
1392 struct gotd_client *client = NULL;
1393 ssize_t n;
1394 int shut = 0;
1395 struct imsg imsg;
1397 client = find_client_by_proc_fd(fd);
1398 if (client == NULL) {
1399 /* Can happen during process teardown. */
1400 warnx("cannot find client for fd %d", fd);
1401 shut = 1;
1402 goto done;
1405 if (event & EV_READ) {
1406 if ((n = imsgbuf_read(ibuf)) == -1)
1407 fatal("imsg_read error");
1408 if (n == 0) {
1409 /* Connection closed. */
1410 shut = 1;
1411 goto done;
1415 if (event & EV_WRITE) {
1416 err = gotd_imsg_flush(ibuf);
1417 if (err)
1418 fatalx("%s", err->msg);
1421 proc = client->session;
1422 if (proc == NULL)
1423 fatalx("cannot find session child process for fd %d", fd);
1425 for (;;) {
1426 const struct got_error *err = NULL;
1427 uint32_t client_id = 0;
1428 int do_disconnect = 0, do_start_repo_child = 0;
1430 if ((n = imsg_get(ibuf, &imsg)) == -1)
1431 fatal("%s: imsg_get error", __func__);
1432 if (n == 0) /* No more messages. */
1433 break;
1435 switch (imsg.hdr.type) {
1436 case GOTD_IMSG_ERROR:
1437 do_disconnect = 1;
1438 err = gotd_imsg_recv_error(&client_id, &imsg);
1439 break;
1440 case GOTD_IMSG_CLIENT_SESSION_READY:
1441 if (client->state != GOTD_CLIENT_STATE_ACCESS_GRANTED) {
1442 err = got_error(GOT_ERR_PRIVSEP_MSG);
1443 break;
1445 do_start_repo_child = 1;
1446 break;
1447 case GOTD_IMSG_DISCONNECT:
1448 do_disconnect = 1;
1449 break;
1450 default:
1451 log_debug("unexpected imsg %d", imsg.hdr.type);
1452 break;
1455 if (!verify_imsg_src(client, proc, &imsg)) {
1456 log_debug("dropping imsg type %d from PID %d",
1457 imsg.hdr.type, proc->pid);
1458 imsg_free(&imsg);
1459 continue;
1461 if (err)
1462 log_warnx("uid %d: %s", client->euid, err->msg);
1464 if (do_start_repo_child) {
1465 struct gotd_repo *repo;
1466 const char *name = client->session->repo_name;
1468 repo = gotd_find_repo_by_name(name, &gotd.repos);
1469 if (repo != NULL) {
1470 enum gotd_procid proc_type;
1472 if (client->required_auth & GOTD_AUTH_WRITE)
1473 proc_type = PROC_REPO_WRITE;
1474 else
1475 proc_type = PROC_REPO_READ;
1477 err = start_repo_child(client, proc_type, repo,
1478 gotd.argv0, gotd.confpath, gotd.daemonize,
1479 gotd.verbosity);
1480 } else
1481 err = got_error(GOT_ERR_NOT_GIT_REPO);
1483 if (err) {
1484 log_warnx("uid %d: %s", client->euid, err->msg);
1485 do_disconnect = 1;
1489 if (do_disconnect) {
1490 if (err)
1491 disconnect_on_error(client, err);
1492 else
1493 disconnect(client);
1496 imsg_free(&imsg);
1498 done:
1499 if (!shut) {
1500 gotd_imsg_event_add(iev);
1501 } else {
1502 /* This pipe is dead. Remove its event handler */
1503 event_del(&iev->ev);
1504 disconnect(client);
1508 static const struct got_error *
1509 connect_notifier_and_session(struct gotd_client *client)
1511 const struct got_error *err = NULL;
1512 struct gotd_imsgev *session_iev = &client->session->iev;
1513 int pipe[2];
1514 int sock_flags = SOCK_STREAM|SOCK_NONBLOCK;
1516 if (gotd.notify_proc == NULL)
1517 return NULL;
1519 #ifdef SOCK_CLOEXEC
1520 sock_flags |= SOCK_CLOEXEC;
1521 #endif
1522 if (socketpair(AF_UNIX, sock_flags,
1523 PF_UNSPEC, pipe) == -1)
1524 return got_error_from_errno("socketpair");
1526 /* Pass notifier pipe to session . */
1527 if (gotd_imsg_compose_event(session_iev, GOTD_IMSG_CONNECT_NOTIFIER,
1528 PROC_GOTD, pipe[0], NULL, 0) == -1) {
1529 err = got_error_from_errno("imsg compose CONNECT_NOTIFIER");
1530 close(pipe[0]);
1531 close(pipe[1]);
1532 return err;
1535 /* Pass session pipe to notifier. */
1536 if (gotd_imsg_compose_event(&gotd.notify_proc->iev,
1537 GOTD_IMSG_CONNECT_SESSION, PROC_GOTD, pipe[1], NULL, 0) == -1) {
1538 err = got_error_from_errno("imsg compose CONNECT_SESSION");
1539 close(pipe[1]);
1540 return err;
1543 return NULL;
1546 static void
1547 gotd_dispatch_repo_child(int fd, short event, void *arg)
1549 const struct got_error *err = NULL;
1550 struct gotd_imsgev *iev = arg;
1551 struct imsgbuf *ibuf = &iev->ibuf;
1552 struct gotd_child_proc *proc = NULL;
1553 struct gotd_client *client;
1554 ssize_t n;
1555 int shut = 0;
1556 struct imsg imsg;
1558 client = find_client_by_proc_fd(fd);
1559 if (client == NULL) {
1560 /* Can happen during process teardown. */
1561 warnx("cannot find client for fd %d", fd);
1562 shut = 1;
1563 goto done;
1566 if (event & EV_READ) {
1567 if ((n = imsgbuf_read(ibuf)) == -1)
1568 fatal("imsg_read error");
1569 if (n == 0) {
1570 /* Connection closed. */
1571 shut = 1;
1572 goto done;
1576 if (event & EV_WRITE) {
1577 err = gotd_imsg_flush(ibuf);
1578 if (err)
1579 fatalx("%s", err->msg);
1582 proc = client->repo;
1583 if (proc == NULL)
1584 fatalx("cannot find child process for fd %d", fd);
1586 for (;;) {
1587 uint32_t client_id = 0;
1588 int do_disconnect = 0;
1590 if ((n = imsg_get(ibuf, &imsg)) == -1)
1591 fatal("%s: imsg_get error", __func__);
1592 if (n == 0) /* No more messages. */
1593 break;
1595 switch (imsg.hdr.type) {
1596 case GOTD_IMSG_ERROR:
1597 do_disconnect = 1;
1598 err = gotd_imsg_recv_error(&client_id, &imsg);
1599 break;
1600 case GOTD_IMSG_REPO_CHILD_READY:
1601 err = connect_session(client);
1602 if (err)
1603 break;
1604 if (client_is_writing(client)) {
1605 err = connect_notifier_and_session(client);
1606 if (err)
1607 break;
1609 err = connect_repo_child(client, proc);
1610 break;
1611 default:
1612 log_debug("unexpected imsg %d", imsg.hdr.type);
1613 break;
1616 if (!verify_imsg_src(client, proc, &imsg)) {
1617 log_debug("dropping imsg type %d from PID %d",
1618 imsg.hdr.type, proc->pid);
1619 imsg_free(&imsg);
1620 continue;
1622 if (err)
1623 log_warnx("uid %d: %s", client->euid, err->msg);
1625 if (do_disconnect) {
1626 if (err)
1627 disconnect_on_error(client, err);
1628 else
1629 disconnect(client);
1632 imsg_free(&imsg);
1634 done:
1635 if (!shut) {
1636 gotd_imsg_event_add(iev);
1637 } else {
1638 /* This pipe is dead. Remove its event handler */
1639 event_del(&iev->ev);
1640 disconnect(client);
1644 static pid_t
1645 start_child(enum gotd_procid proc_id, const char *repo_path,
1646 char *argv0, const char *confpath, int fd, int daemonize, int verbosity)
1648 const char *argv[11];
1649 int argc = 0;
1650 pid_t pid;
1652 switch (pid = fork()) {
1653 case -1:
1654 fatal("cannot fork");
1655 case 0:
1656 break;
1657 default:
1658 close(fd);
1659 return pid;
1662 if (fd != GOTD_FILENO_MSG_PIPE) {
1663 if (dup2(fd, GOTD_FILENO_MSG_PIPE) == -1)
1664 fatal("cannot setup imsg fd");
1665 } else if (fcntl(fd, F_SETFD, 0) == -1)
1666 fatal("cannot setup imsg fd");
1668 argv[argc++] = argv0;
1669 switch (proc_id) {
1670 case PROC_LISTEN:
1671 argv[argc++] = "-TL";
1672 break;
1673 case PROC_AUTH:
1674 argv[argc++] = "-TA";
1675 break;
1676 case PROC_SESSION_READ:
1677 argv[argc++] = "-Ts";
1678 break;
1679 case PROC_SESSION_WRITE:
1680 argv[argc++] = "-TS";
1681 break;
1682 case PROC_REPO_READ:
1683 argv[argc++] = "-TR";
1684 break;
1685 case PROC_REPO_WRITE:
1686 argv[argc++] = "-TW";
1687 break;
1688 case PROC_NOTIFY:
1689 argv[argc++] = "-TN";
1690 break;
1691 default:
1692 fatalx("invalid process id %d", proc_id);
1695 argv[argc++] = "-f";
1696 argv[argc++] = confpath;
1698 if (repo_path) {
1699 argv[argc++] = "-P";
1700 argv[argc++] = repo_path;
1703 if (!daemonize)
1704 argv[argc++] = "-d";
1705 if (verbosity > 0)
1706 argv[argc++] = "-v";
1707 if (verbosity > 1)
1708 argv[argc++] = "-v";
1709 argv[argc++] = NULL;
1711 execvp(argv0, (char * const *)argv);
1712 fatal("execvp");
1715 static void
1716 start_listener(char *argv0, const char *confpath, int daemonize, int verbosity)
1718 struct gotd_child_proc *proc;
1719 int sock_flags = SOCK_STREAM|SOCK_NONBLOCK;
1721 #ifdef SOCK_CLOEXEC
1722 sock_flags |= SOCK_CLOEXEC;
1723 #endif
1725 proc = calloc(1, sizeof(*proc));
1726 if (proc == NULL)
1727 fatal("calloc");
1729 TAILQ_INSERT_HEAD(&procs, proc, entry);
1731 /* proc->tmo is initialized in main() after event_init() */
1733 proc->type = PROC_LISTEN;
1735 if (socketpair(AF_UNIX, sock_flags,
1736 PF_UNSPEC, proc->pipe) == -1)
1737 fatal("socketpair");
1739 proc->pid = start_child(proc->type, NULL, argv0, confpath,
1740 proc->pipe[1], daemonize, verbosity);
1741 if (imsgbuf_init(&proc->iev.ibuf, proc->pipe[0]) == -1)
1742 fatal("imsgbuf_init");
1743 imsgbuf_allow_fdpass(&proc->iev.ibuf);
1744 proc->iev.handler = gotd_dispatch_listener;
1745 proc->iev.events = EV_READ;
1746 proc->iev.handler_arg = NULL;
1748 gotd.listen_proc = proc;
1751 static void
1752 start_notifier(char *argv0, const char *confpath, int daemonize, int verbosity)
1754 struct gotd_child_proc *proc;
1755 int sock_flags = SOCK_STREAM | SOCK_NONBLOCK;
1758 proc = calloc(1, sizeof(*proc));
1759 if (proc == NULL)
1760 fatal("calloc");
1762 TAILQ_INSERT_HEAD(&procs, proc, entry);
1764 /* proc->tmo is initialized in main() after event_init() */
1766 proc->type = PROC_NOTIFY;
1768 #ifdef SOCK_CLOEXEC
1769 sock_flags |= SOCK_CLOEXEC;
1770 #endif
1771 if (socketpair(AF_UNIX, sock_flags,
1772 PF_UNSPEC, proc->pipe) == -1)
1773 fatal("socketpair");
1775 proc->pid = start_child(proc->type, NULL, argv0, confpath,
1776 proc->pipe[1], daemonize, verbosity);
1777 if (imsgbuf_init(&proc->iev.ibuf, proc->pipe[0]) == -1)
1778 fatal("imsgbuf_init");
1779 imsgbuf_allow_fdpass(&proc->iev.ibuf);
1780 proc->iev.handler = gotd_dispatch_notifier;
1781 proc->iev.events = EV_READ;
1782 proc->iev.handler_arg = NULL;
1783 event_set(&proc->iev.ev, proc->iev.ibuf.fd, EV_READ,
1784 gotd_dispatch_notifier, &proc->iev);
1786 gotd.notify_proc = proc;
1789 static const struct got_error *
1790 start_session_child(struct gotd_client *client, struct gotd_repo *repo,
1791 char *argv0, const char *confpath, int daemonize, int verbosity)
1793 struct gotd_child_proc *proc;
1794 int sock_flags = SOCK_STREAM | SOCK_NONBLOCK;
1796 #ifdef SOCK_CLOEXEC
1797 sock_flags |= SOCK_CLOEXEC;
1798 #endif
1800 proc = calloc(1, sizeof(*proc));
1801 if (proc == NULL)
1802 return got_error_from_errno("calloc");
1804 TAILQ_INSERT_HEAD(&procs, proc, entry);
1805 evtimer_set(&proc->tmo, kill_proc_timeout, proc);
1807 if (client_is_reading(client))
1808 proc->type = PROC_SESSION_READ;
1809 else
1810 proc->type = PROC_SESSION_WRITE;
1811 if (strlcpy(proc->repo_name, repo->name,
1812 sizeof(proc->repo_name)) >= sizeof(proc->repo_name))
1813 fatalx("repository name too long: %s", repo->name);
1814 log_debug("starting client uid %d session for repository %s",
1815 client->euid, repo->name);
1816 if (strlcpy(proc->repo_path, repo->path, sizeof(proc->repo_path)) >=
1817 sizeof(proc->repo_path))
1818 fatalx("repository path too long: %s", repo->path);
1819 if (socketpair(AF_UNIX, sock_flags, PF_UNSPEC, proc->pipe) == -1)
1820 fatal("socketpair");
1821 proc->pid = start_child(proc->type, proc->repo_path, argv0,
1822 confpath, proc->pipe[1], daemonize, verbosity);
1823 if (imsgbuf_init(&proc->iev.ibuf, proc->pipe[0]) == -1)
1824 fatal("imsgbuf_init");
1825 imsgbuf_allow_fdpass(&proc->iev.ibuf);
1826 log_debug("proc %s %s is on fd %d",
1827 gotd_proc_names[proc->type], proc->repo_path,
1828 proc->pipe[0]);
1829 proc->iev.handler = gotd_dispatch_client_session;
1830 proc->iev.events = EV_READ;
1831 proc->iev.handler_arg = NULL;
1832 event_set(&proc->iev.ev, proc->iev.ibuf.fd, EV_READ,
1833 gotd_dispatch_client_session, &proc->iev);
1834 gotd_imsg_event_add(&proc->iev);
1836 client->session = proc;
1837 return NULL;
1840 static const struct got_error *
1841 start_repo_child(struct gotd_client *client, enum gotd_procid proc_type,
1842 struct gotd_repo *repo, char *argv0, const char *confpath,
1843 int daemonize, int verbosity)
1845 struct gotd_child_proc *proc;
1846 int sock_flags = SOCK_STREAM|SOCK_NONBLOCK;
1848 #ifdef SOCK_CLOEXEC
1849 sock_flags |= SOCK_CLOEXEC;
1850 #endif
1852 if (proc_type != PROC_REPO_READ && proc_type != PROC_REPO_WRITE)
1853 return got_error_msg(GOT_ERR_NOT_IMPL, "bad process type");
1855 proc = calloc(1, sizeof(*proc));
1856 if (proc == NULL)
1857 return got_error_from_errno("calloc");
1859 TAILQ_INSERT_HEAD(&procs, proc, entry);
1860 evtimer_set(&proc->tmo, kill_proc_timeout, proc);
1862 proc->type = proc_type;
1863 if (strlcpy(proc->repo_name, repo->name,
1864 sizeof(proc->repo_name)) >= sizeof(proc->repo_name))
1865 fatalx("repository name too long: %s", repo->name);
1866 log_debug("starting %s for repository %s",
1867 proc->type == PROC_REPO_READ ? "reader" : "writer", repo->name);
1869 if (strlcpy(proc->repo_path, repo->path, sizeof(proc->repo_path)) >=
1870 sizeof(proc->repo_path))
1871 fatalx("repository path too long: %s", repo->path);
1872 if (realpath(repo->path, proc->repo_path) == NULL)
1873 fatal("%s", repo->path);
1874 if (socketpair(AF_UNIX, sock_flags,
1875 PF_UNSPEC, proc->pipe) == -1)
1876 fatal("socketpair");
1877 proc->pid = start_child(proc->type, proc->repo_path, argv0,
1878 confpath, proc->pipe[1], daemonize, verbosity);
1879 if (imsgbuf_init(&proc->iev.ibuf, proc->pipe[0]) == -1)
1880 fatal("imsgbuf_init");
1881 imsgbuf_allow_fdpass(&proc->iev.ibuf);
1882 log_debug("proc %s %s is on fd %d",
1883 gotd_proc_names[proc->type], proc->repo_path,
1884 proc->pipe[0]);
1885 proc->iev.handler = gotd_dispatch_repo_child;
1886 proc->iev.events = EV_READ;
1887 proc->iev.handler_arg = NULL;
1888 event_set(&proc->iev.ev, proc->iev.ibuf.fd, EV_READ,
1889 gotd_dispatch_repo_child, &proc->iev);
1890 gotd_imsg_event_add(&proc->iev);
1892 client->repo = proc;
1893 return NULL;
1896 static const struct got_error *
1897 start_auth_child(struct gotd_client *client, int required_auth,
1898 struct gotd_repo *repo, char *argv0, const char *confpath,
1899 int daemonize, int verbosity)
1901 const struct got_error *err = NULL;
1902 struct gotd_child_proc *proc;
1903 struct gotd_imsg_auth iauth;
1904 int fd;
1905 int sock_flags = SOCK_STREAM|SOCK_NONBLOCK;
1907 #ifdef SOCK_CLOEXEC
1908 sock_flags |= SOCK_CLOEXEC;
1909 #endif
1911 memset(&iauth, 0, sizeof(iauth));
1913 fd = dup(client->fd);
1914 if (fd == -1)
1915 return got_error_from_errno("dup");
1917 proc = calloc(1, sizeof(*proc));
1918 if (proc == NULL) {
1919 err = got_error_from_errno("calloc");
1920 close(fd);
1921 return err;
1924 TAILQ_INSERT_HEAD(&procs, proc, entry);
1925 evtimer_set(&proc->tmo, kill_proc_timeout, proc);
1927 proc->type = PROC_AUTH;
1928 if (strlcpy(proc->repo_name, repo->name,
1929 sizeof(proc->repo_name)) >= sizeof(proc->repo_name))
1930 fatalx("repository name too long: %s", repo->name);
1931 log_debug("starting auth for uid %d repository %s",
1932 client->euid, repo->name);
1933 if (strlcpy(proc->repo_path, repo->path, sizeof(proc->repo_path)) >=
1934 sizeof(proc->repo_path))
1935 fatalx("repository path too long: %s", repo->path);
1936 if (realpath(repo->path, proc->repo_path) == NULL)
1937 fatal("%s", repo->path);
1938 if (socketpair(AF_UNIX, sock_flags,
1939 PF_UNSPEC, proc->pipe) == -1)
1940 fatal("socketpair");
1941 proc->pid = start_child(proc->type, proc->repo_path, argv0,
1942 confpath, proc->pipe[1], daemonize, verbosity);
1943 if (imsgbuf_init(&proc->iev.ibuf, proc->pipe[0]) == -1)
1944 fatal("imsgbuf_init");
1945 imsgbuf_allow_fdpass(&proc->iev.ibuf);
1946 log_debug("proc %s %s is on fd %d",
1947 gotd_proc_names[proc->type], proc->repo_path,
1948 proc->pipe[0]);
1949 proc->iev.handler = gotd_dispatch_auth_child;
1950 proc->iev.events = EV_READ;
1951 proc->iev.handler_arg = NULL;
1952 event_set(&proc->iev.ev, proc->iev.ibuf.fd, EV_READ,
1953 gotd_dispatch_auth_child, &proc->iev);
1954 gotd_imsg_event_add(&proc->iev);
1956 iauth.euid = client->euid;
1957 iauth.egid = client->egid;
1958 iauth.required_auth = required_auth;
1959 iauth.client_id = client->id;
1960 if (gotd_imsg_compose_event(&proc->iev, GOTD_IMSG_AUTHENTICATE,
1961 PROC_GOTD, fd, &iauth, sizeof(iauth)) == -1) {
1962 log_warn("imsg compose AUTHENTICATE");
1963 close(fd);
1964 /* Let the auth_timeout handler tidy up. */
1967 client->auth = proc;
1968 client->required_auth = required_auth;
1969 return NULL;
1972 static void
1973 apply_unveil_repo_readonly(const char *repo_path, int need_tmpdir)
1975 if (need_tmpdir) {
1976 if (unveil(GOT_TMPDIR_STR, "rwc") == -1)
1977 fatal("unveil %s", GOT_TMPDIR_STR);
1980 if (unveil(repo_path, "r") == -1)
1981 fatal("unveil %s", repo_path);
1983 if (unveil(NULL, NULL) == -1)
1984 fatal("unveil");
1987 static void
1988 apply_unveil_repo_readwrite(const char *repo_path)
1990 if (unveil(repo_path, "rwc") == -1)
1991 fatal("unveil %s", repo_path);
1993 if (unveil(GOT_TMPDIR_STR, "rwc") == -1)
1994 fatal("unveil %s", GOT_TMPDIR_STR);
1996 if (unveil(NULL, NULL) == -1)
1997 fatal("unveil");
2000 static void
2001 apply_unveil_none(void)
2003 if (unveil("/", "") == -1)
2004 fatal("unveil");
2006 if (unveil(NULL, NULL) == -1)
2007 fatal("unveil");
2010 static void
2011 apply_unveil_selfexec(void)
2013 if (unveil(gotd.argv0, "x") == -1)
2014 fatal("unveil %s", gotd.argv0);
2016 if (unveil(NULL, NULL) == -1)
2017 fatal("unveil");
2020 static void
2021 set_max_datasize(void)
2023 struct rlimit rl;
2025 if (getrlimit(RLIMIT_DATA, &rl) != 0)
2026 return;
2028 rl.rlim_cur = rl.rlim_max;
2029 setrlimit(RLIMIT_DATA, &rl);
2032 static void
2033 unveil_notification_helpers(void)
2035 const char *helpers[] = {
2036 GOTD_PATH_PROG_NOTIFY_EMAIL,
2037 GOTD_PATH_PROG_NOTIFY_HTTP,
2039 size_t i;
2041 for (i = 0; i < nitems(helpers); i++) {
2042 if (unveil(helpers[i], "x") == 0)
2043 continue;
2044 fatal("unveil %s", helpers[i]);
2047 if (unveil(NULL, NULL) == -1)
2048 fatal("unveil");
2051 static void
2052 check_file_secrecy(int fd, const char *fname)
2054 struct stat st;
2056 if (fstat(fd, &st))
2057 fatal("cannot stat %s", fname);
2059 if (st.st_uid != 0)
2060 fatalx("secrets file %s must be owned by root", fname);
2062 if (st.st_gid != 0)
2063 fatalx("secrets file %s must be owned by group wheel/root",
2064 fname);
2066 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO))
2067 fatalx("secrets file %s must not be group writable or world "
2068 "readable/writable", fname);
2072 main(int argc, char **argv)
2074 const struct got_error *error = NULL;
2075 struct gotd_secrets *secrets = NULL;
2076 int ch, fd = -1, daemonize = 1, verbosity = 0, noaction = 0;
2077 const char *confpath = GOTD_CONF_PATH;
2078 char *secretspath = NULL;
2079 char *argv0 = argv[0];
2080 char title[2048];
2081 struct passwd *pw = NULL;
2082 uid_t uid;
2083 char *repo_path = NULL;
2084 enum gotd_procid proc_id = PROC_GOTD;
2085 struct event evsigint, evsigterm, evsighup, evsigusr1, evsigchld;
2086 int *pack_fds = NULL, *temp_fds = NULL;
2087 struct gotd_repo *repo = NULL;
2088 char *default_sender = NULL;
2089 char hostname[_POSIX_HOST_NAME_MAX + 1];
2090 FILE *fp;
2091 FILE *diff_f1 = NULL, *diff_f2 = NULL;
2092 int diff_fd1 = -1, diff_fd2 = -1;
2093 const char *errstr;
2095 TAILQ_INIT(&procs);
2097 log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */
2099 while ((ch = getopt(argc, argv, "df:nP:s:T:v")) != -1) {
2100 switch (ch) {
2101 case 'd':
2102 daemonize = 0;
2103 break;
2104 case 'f':
2105 confpath = optarg;
2106 break;
2107 case 'n':
2108 noaction = 1;
2109 break;
2110 case 'P':
2111 repo_path = realpath(optarg, NULL);
2112 if (repo_path == NULL)
2113 fatal("realpath '%s'", optarg);
2114 break;
2115 case 's':
2116 secretspath = realpath(optarg, NULL);
2117 if (secretspath == NULL)
2118 fatal("realpath '%s'", optarg);
2119 break;
2120 case 'T':
2121 switch (*optarg) {
2122 case 'A':
2123 proc_id = PROC_AUTH;
2124 break;
2125 case 'L':
2126 proc_id = PROC_LISTEN;
2127 break;
2128 case 'N':
2129 proc_id = PROC_NOTIFY;
2130 break;
2131 case 'R':
2132 proc_id = PROC_REPO_READ;
2133 break;
2134 case 's':
2135 proc_id = PROC_SESSION_READ;
2136 break;
2137 case 'S':
2138 proc_id = PROC_SESSION_WRITE;
2139 break;
2140 case 'W':
2141 proc_id = PROC_REPO_WRITE;
2142 break;
2143 default:
2144 errx(1, "unknown proc type %s", optarg);
2146 break;
2147 case 'v':
2148 if (verbosity < 3)
2149 verbosity++;
2150 break;
2151 default:
2152 usage();
2156 argc -= optind;
2157 argv += optind;
2159 if (argc != 0)
2160 usage();
2162 if (geteuid() && (proc_id == PROC_GOTD || proc_id == PROC_LISTEN))
2163 fatalx("need root privileges");
2165 if (proc_id == PROC_GOTD) {
2166 const char *p = secretspath ? secretspath : GOTD_SECRETS_PATH;
2168 fp = fopen(p, "r");
2169 if (fp == NULL && (secretspath != NULL || errno != ENOENT))
2170 fatal("can't open secrets file %s", p);
2172 if (fp != NULL) {
2173 check_file_secrecy(fileno(fp), p);
2174 error = gotd_secrets_parse(p, fp, &secrets);
2175 fclose(fp);
2176 if (error)
2177 fatalx("failed to parse secrets file %s: %s",
2178 p, error->msg);
2182 if (parse_config(confpath, proc_id, secrets, &gotd) != 0)
2183 return 1;
2185 pw = getpwnam(gotd.user_name);
2186 if (pw == NULL) {
2187 uid = strtonum(gotd.user_name, 0, UID_MAX - 1, &errstr);
2188 if (errstr == NULL)
2189 pw = getpwuid(uid);
2191 if (pw == NULL)
2192 fatalx("user %s not found", gotd.user_name);
2194 if (pw->pw_uid == 0)
2195 fatalx("cannot run %s as the superuser", getprogname());
2198 * SHA2 repositories cannot be used with gotd until Git protocol v2
2199 * support is added. Reject them at startup for now.
2201 TAILQ_FOREACH(repo, &gotd.repos, entry) {
2202 struct got_repository *r;
2204 error = got_repo_open(&r, repo->path, NULL, NULL);
2205 if (error) {
2206 if (error->code == GOT_ERR_ERRNO && errno == ENOENT)
2207 continue;
2208 fatalx("%s: %s", repo->path, error->msg);
2211 if (got_repo_get_object_format(r) != GOT_HASH_SHA1) {
2212 error = got_error_msg(GOT_ERR_NOT_IMPL,
2213 "sha256 object IDs unsupported in network "
2214 "protocol");
2215 fatalx("%s: %s", repo->path, error->msg);
2218 got_repo_close(r);
2221 if (noaction) {
2222 fprintf(stderr, "configuration OK\n");
2223 return 0;
2226 gotd.argv0 = argv0;
2227 gotd.daemonize = daemonize;
2228 gotd.verbosity = verbosity;
2229 gotd.confpath = confpath;
2231 /* Require an absolute path in argv[0] for reliable re-exec. */
2232 if (!got_path_is_absolute(argv0))
2233 fatalx("bad path \"%s\": must be an absolute path", argv0);
2235 log_init(daemonize ? 0 : 1, LOG_DAEMON);
2236 log_setverbose(verbosity);
2238 if (proc_id == PROC_GOTD) {
2239 snprintf(title, sizeof(title), "%s", gotd_proc_names[proc_id]);
2240 arc4random_buf(&clients_hash_key, sizeof(clients_hash_key));
2241 if (daemonize && daemon(1, 0) == -1)
2242 fatal("daemon");
2243 gotd.pid = getpid();
2244 start_listener(argv0, confpath, daemonize, verbosity);
2245 start_notifier(argv0, confpath, daemonize, verbosity);
2246 } else if (proc_id == PROC_LISTEN) {
2247 snprintf(title, sizeof(title), "%s", gotd_proc_names[proc_id]);
2248 if (verbosity) {
2249 log_info("socket: %s", gotd.unix_socket_path);
2250 log_info("user: %s", pw->pw_name);
2253 fd = unix_socket_listen(gotd.unix_socket_path, pw->pw_uid,
2254 pw->pw_gid);
2255 if (fd == -1) {
2256 fatal("cannot listen on unix socket %s",
2257 gotd.unix_socket_path);
2259 } else if (proc_id == PROC_AUTH) {
2260 snprintf(title, sizeof(title), "%s %s",
2261 gotd_proc_names[proc_id], repo_path);
2262 } else if (proc_id == PROC_REPO_READ || proc_id == PROC_REPO_WRITE ||
2263 proc_id == PROC_SESSION_READ || proc_id == PROC_SESSION_WRITE) {
2264 error = got_repo_pack_fds_open(&pack_fds);
2265 if (error != NULL)
2266 fatalx("cannot open pack tempfiles: %s", error->msg);
2267 error = got_repo_temp_fds_open(&temp_fds);
2268 if (error != NULL)
2269 fatalx("cannot open pack tempfiles: %s", error->msg);
2270 if (repo_path == NULL)
2271 fatalx("repository path not specified");
2272 snprintf(title, sizeof(title), "%s %s",
2273 gotd_proc_names[proc_id], repo_path);
2274 } else if (proc_id == PROC_NOTIFY) {
2275 snprintf(title, sizeof(title), "%s", gotd_proc_names[proc_id]);
2276 if (gethostname(hostname, sizeof(hostname)) == -1)
2277 fatal("gethostname");
2278 if (asprintf(&default_sender, "%s@%s",
2279 pw->pw_name, hostname) == -1)
2280 fatal("asprintf");
2281 } else
2282 fatal("invalid process id %d", proc_id);
2284 setproctitle("%s", title);
2285 log_procinit(title);
2287 if (proc_id != PROC_GOTD && proc_id != PROC_LISTEN &&
2288 proc_id != PROC_REPO_READ && proc_id != PROC_REPO_WRITE) {
2289 /* Drop root privileges. */
2290 if (setgid(pw->pw_gid) == -1)
2291 fatal("setgid %d failed", pw->pw_gid);
2292 if (setuid(pw->pw_uid) == -1)
2293 fatal("setuid %d failed", pw->pw_uid);
2296 event_init();
2298 switch (proc_id) {
2299 case PROC_GOTD:
2300 #ifndef PROFILE
2301 /* "exec" promise will be limited to argv[0] via unveil(2). */
2302 if (pledge("stdio proc exec sendfd recvfd unveil", NULL) == -1)
2303 err(1, "pledge");
2304 #endif
2305 break;
2306 case PROC_LISTEN:
2307 #ifndef PROFILE
2308 if (pledge("stdio sendfd unix unveil", NULL) == -1)
2309 err(1, "pledge");
2310 #endif
2312 * Ensure that AF_UNIX bind(2) cannot be used with any other
2313 * sockets by revoking all filesystem access via unveil(2).
2315 apply_unveil_none();
2317 enter_chroot(GOTD_EMPTY_PATH);
2318 drop_privs(pw);
2320 listen_main(title, fd, gotd.connection_limits,
2321 gotd.nconnection_limits);
2322 /* NOTREACHED */
2323 break;
2324 case PROC_AUTH:
2325 #ifndef PROFILE
2326 if (pledge("stdio getpw recvfd unix unveil", NULL) == -1)
2327 err(1, "pledge");
2328 #endif
2330 * We need the "unix" pledge promise for getpeername(2) only.
2331 * Ensure that AF_UNIX bind(2) cannot be used by revoking all
2332 * filesystem access via unveil(2). Access to password database
2333 * files will still work since "getpw" bypasses unveil(2).
2335 apply_unveil_none();
2337 drop_privs(pw);
2339 auth_main(title, &gotd.repos, repo_path);
2340 /* NOTREACHED */
2341 break;
2342 case PROC_SESSION_READ:
2343 case PROC_SESSION_WRITE:
2344 #ifndef PROFILE
2346 * The "recvfd" promise is only needed during setup and
2347 * will be removed in a later pledge(2) call.
2349 if (pledge("stdio rpath wpath cpath recvfd sendfd fattr flock "
2350 "unveil", NULL) == -1)
2351 err(1, "pledge");
2352 #endif
2353 if (proc_id == PROC_SESSION_READ)
2354 apply_unveil_repo_readonly(repo_path, 1);
2355 else {
2356 apply_unveil_repo_readwrite(repo_path);
2357 repo = gotd_find_repo_by_path(repo_path, &gotd);
2358 if (repo == NULL)
2359 fatalx("no repository for path %s", repo_path);
2362 drop_privs(pw);
2364 if (proc_id == PROC_SESSION_READ)
2365 session_read_main(title, repo_path, pack_fds, temp_fds,
2366 &gotd.request_timeout, repo);
2367 else
2368 session_write_main(title, repo_path, pack_fds, temp_fds,
2369 &gotd.request_timeout, repo);
2370 /* NOTREACHED */
2371 break;
2372 case PROC_REPO_READ:
2373 set_max_datasize();
2374 #ifndef PROFILE
2375 if (pledge("stdio rpath recvfd unveil", NULL) == -1)
2376 err(1, "pledge");
2377 #endif
2378 apply_unveil_repo_readonly(repo_path, 0);
2380 if (enter_chroot(repo_path)) {
2381 log_info("change repo path %s", repo_path);
2382 free(repo_path);
2383 repo_path = strdup("/");
2384 if (repo_path == NULL)
2385 fatal("strdup");
2386 log_info("repo path is now %s", repo_path);
2388 drop_privs(pw);
2390 repo_read_main(title, repo_path, pack_fds, temp_fds);
2391 /* NOTREACHED */
2392 exit(0);
2393 case PROC_REPO_WRITE:
2394 set_max_datasize();
2396 diff_f1 = got_opentemp();
2397 if (diff_f1 == NULL)
2398 fatal("got_opentemp");
2399 diff_f2 = got_opentemp();
2400 if (diff_f2 == NULL)
2401 fatal("got_opentemp");
2402 diff_fd1 = got_opentempfd();
2403 if (diff_fd1 == -1)
2404 fatal("got_opentempfd");
2405 diff_fd2 = got_opentempfd();
2406 if (diff_fd2 == -1)
2407 fatal("got_opentempfd");
2408 #ifndef PROFILE
2409 if (pledge("stdio rpath recvfd unveil", NULL) == -1)
2410 err(1, "pledge");
2411 #endif
2412 apply_unveil_repo_readonly(repo_path, 0);
2413 repo = gotd_find_repo_by_path(repo_path, &gotd);
2414 if (repo == NULL)
2415 fatalx("no repository for path %s", repo_path);
2417 if (enter_chroot(repo_path)) {
2418 free(repo_path);
2419 repo_path = strdup("/");
2420 if (repo_path == NULL)
2421 fatal("strdup");
2423 drop_privs(pw);
2425 repo_write_main(title, repo_path, pack_fds, temp_fds,
2426 diff_f1, diff_f2, diff_fd1, diff_fd2,
2427 &repo->protected_tag_namespaces,
2428 &repo->protected_branch_namespaces,
2429 &repo->protected_branches);
2430 /* NOTREACHED */
2431 exit(0);
2432 case PROC_NOTIFY:
2433 #ifndef PROFILE
2434 if (pledge("stdio proc exec recvfd unveil", NULL) == -1)
2435 err(1, "pledge");
2436 #endif
2438 * Limit "exec" promise to notification helpers via unveil(2).
2440 unveil_notification_helpers();
2442 drop_privs(pw);
2444 notify_main(title, &gotd.repos, default_sender);
2445 /* NOTREACHED */
2446 exit(0);
2447 default:
2448 fatal("invalid process id %d", proc_id);
2451 if (proc_id != PROC_GOTD)
2452 fatal("invalid process id %d", proc_id);
2454 evtimer_set(&gotd.listen_proc->tmo, kill_proc_timeout,
2455 gotd.listen_proc);
2456 if (gotd.notify_proc) {
2457 evtimer_set(&gotd.notify_proc->tmo, kill_proc_timeout,
2458 gotd.notify_proc);
2461 apply_unveil_selfexec();
2463 signal_set(&evsigint, SIGINT, gotd_sighdlr, NULL);
2464 signal_set(&evsigterm, SIGTERM, gotd_sighdlr, NULL);
2465 signal_set(&evsighup, SIGHUP, gotd_sighdlr, NULL);
2466 signal_set(&evsigusr1, SIGUSR1, gotd_sighdlr, NULL);
2467 signal_set(&evsigchld, SIGCHLD, gotd_sighdlr, NULL);
2468 signal(SIGPIPE, SIG_IGN);
2470 signal_add(&evsigint, NULL);
2471 signal_add(&evsigterm, NULL);
2472 signal_add(&evsighup, NULL);
2473 signal_add(&evsigusr1, NULL);
2474 signal_add(&evsigchld, NULL);
2476 gotd_imsg_event_add(&gotd.listen_proc->iev);
2477 if (gotd.notify_proc) {
2478 struct imsgbuf *imsgbuf = &gotd.notify_proc->iev.ibuf;
2479 struct gotd_secret *s;
2480 size_t i, n = 0;
2482 gotd_imsg_event_add(&gotd.notify_proc->iev);
2484 if (gotd.secrets)
2485 n = gotd.secrets->len;
2487 if (imsg_compose(imsgbuf, GOTD_IMSG_SECRETS, 0, 0, -1,
2488 &n, sizeof(n)) == -1)
2489 fatal("imsg_compose GOTD_IMSG_SECRETS");
2490 error = gotd_imsg_flush(imsgbuf);
2491 if (error)
2492 fatalx("%s", error->msg);
2494 for (i = 0; i < n; ++i) {
2495 struct iovec iov[5];
2497 s = &gotd.secrets->secrets[i];
2499 iov[0].iov_base = &s->type;
2500 iov[0].iov_len = sizeof(s->type);
2502 iov[1].iov_base = s->label;
2503 iov[1].iov_len = strlen(s->label) + 1;
2505 iov[2].iov_base = s->user;
2506 iov[2].iov_len = s->user ? strlen(s->user) + 1 : 0 ;
2508 iov[3].iov_base = s->pass;
2509 iov[3].iov_len = s->pass ? strlen(s->pass) + 1 : 0 ;
2511 iov[4].iov_base = s->hmac;
2512 iov[4].iov_len = s->hmac ? strlen(s->hmac) + 1 : 0 ;
2514 if (imsg_composev(imsgbuf, GOTD_IMSG_SECRET,
2515 0, 0, -1, iov, 5) == -1)
2516 fatal("imsg_composev GOTD_IMSG_SECRET");
2517 error = gotd_imsg_flush(imsgbuf);
2518 if (error)
2519 fatalx("%s", error->msg);
2522 gotd_secrets_free(gotd.secrets);
2523 gotd.secrets = NULL;
2526 event_dispatch();
2528 free(repo_path);
2529 free(secretspath);
2530 free(default_sender);
2531 gotd_shutdown();
2533 return 0;