/* $OpenBSD: control.c,v 1.3 2024/03/22 19:14:28 bluhm Exp $ */ /* * Copyright (c) 2017 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lpd.h" #include "log.h" #include "proc.h" #define CONTROL_BACKLOG 5 static void control_init(const char *); static void control_listen(void); static void control_pause(void); static void control_resume(void); static void control_accept(int, short, void *); static void control_close(struct imsgproc *); static void control_dispatch_priv(struct imsgproc *, struct imsg *, void *); static void control_dispatch_client(struct imsgproc *, struct imsg *, void *); static struct { struct event evt; int fd; int pause; } ctl; void control(int debug, int verbose) { struct passwd *pw; /* Early initialisation. */ log_init(debug, LOG_DAEMON); log_setverbose(verbose); log_procinit("control"); setproctitle("control"); control_init(LPD_SOCKET); /* Drop privileges. */ if ((pw = getpwnam(LPD_USER)) == NULL) fatalx("unknown user " LPD_USER); if (chroot(_PATH_VAREMPTY) == -1) fatal("%s: chroot", __func__); if (chdir("/") == -1) fatal("%s: chdir", __func__); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("cannot drop privileges"); if (pledge("stdio unix recvfd sendfd", NULL) == -1) fatal("%s: pledge", __func__); event_init(); signal(SIGPIPE, SIG_IGN); /* Setup imsg socket with parent. */ p_priv = proc_attach(PROC_PRIV, 3); if (p_priv == NULL) fatal("%s: proc_attach", __func__); proc_setcallback(p_priv, control_dispatch_priv, NULL); proc_enable(p_priv); event_dispatch(); exit(0); } static void control_init(const char *path) { struct sockaddr_un sun; mode_t old_umask; int fd; fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); if (fd == -1) fatal("%s: socket", __func__); memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; strlcpy(sun.sun_path, LPD_SOCKET, sizeof(sun.sun_path)); if ((unlink(path) == -1) && (errno != ENOENT)) fatal("%s: unlink: %s", __func__, path); old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) fatal("%s: bind: %s", __func__, path); umask(old_umask); if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) fatal("%s: chmod: %s", __func__, path); ctl.fd = fd; } static void control_listen(void) { if (listen(ctl.fd, CONTROL_BACKLOG) == -1) fatal("%s: listen", __func__); ctl.pause = 0; control_resume(); } static void control_pause(void) { struct timeval tv; event_del(&ctl.evt); tv.tv_sec = 1; tv.tv_usec = 0; evtimer_set(&ctl.evt, control_accept, NULL); evtimer_add(&ctl.evt, &tv); ctl.pause = 1; } static void control_resume(void) { if (ctl.pause) { evtimer_del(&ctl.evt); ctl.pause = 0; } event_set(&ctl.evt, ctl.fd, EV_READ | EV_PERSIST, control_accept, NULL); event_add(&ctl.evt, NULL); } static void control_accept(int fd, short event, void *arg) { struct imsgproc *proc; int sock; if (ctl.pause) { ctl.pause = 0; control_resume(); return; } sock = accept4(ctl.fd, NULL, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK); if (sock == -1) { if (errno == ENFILE || errno == EMFILE) control_pause(); else if (errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED) log_warn("%s: accept4", __func__); return; } proc = proc_attach(PROC_CLIENT, sock); if (proc == NULL) { log_warn("%s: proc_attach", __func__); close(sock); return; } proc_setcallback(proc, control_dispatch_client, NULL); proc_enable(proc); } static void control_close(struct imsgproc *proc) { proc_free(proc); if (ctl.pause) control_resume(); } static void control_dispatch_priv(struct imsgproc *proc, struct imsg *imsg, void *arg) { if (imsg == NULL) { log_debug("%s: imsg connection lost", __func__); event_loopexit(NULL); return; } if (log_getverbose() > LOGLEVEL_IMSG) log_imsg(proc, imsg); switch (imsg->hdr.type) { case IMSG_CONF_START: m_end(proc); break; case IMSG_CONF_END: m_end(proc); control_listen(); break; default: fatalx("%s: unexpected imsg %s", __func__, log_fmt_imsgtype(imsg->hdr.type)); } } static void control_dispatch_client(struct imsgproc *proc, struct imsg *imsg, void *arg) { if (imsg == NULL) { control_close(proc); return; } if (log_getverbose() > LOGLEVEL_IMSG) log_imsg(proc, imsg); switch (imsg->hdr.type) { default: log_debug("%s: error handling imsg %d", __func__, imsg->hdr.type); } }