1*12690b15Sbluhm /* $OpenBSD: control.c,v 1.3 2024/03/22 19:14:28 bluhm Exp $ */
23b188dabSeric
33b188dabSeric /*
43b188dabSeric * Copyright (c) 2017 Eric Faurot <eric@openbsd.org>
53b188dabSeric *
63b188dabSeric * Permission to use, copy, modify, and distribute this software for any
73b188dabSeric * purpose with or without fee is hereby granted, provided that the above
83b188dabSeric * copyright notice and this permission notice appear in all copies.
93b188dabSeric *
103b188dabSeric * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113b188dabSeric * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123b188dabSeric * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133b188dabSeric * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143b188dabSeric * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153b188dabSeric * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163b188dabSeric * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173b188dabSeric */
183b188dabSeric
193b188dabSeric #include <sys/types.h>
203b188dabSeric #include <sys/queue.h>
213b188dabSeric #include <sys/stat.h>
223b188dabSeric #include <sys/socket.h>
233b188dabSeric #include <sys/un.h>
243b188dabSeric
253b188dabSeric #include <errno.h>
263b188dabSeric #include <event.h>
273b188dabSeric #include <imsg.h>
28*12690b15Sbluhm #include <paths.h>
293b188dabSeric #include <pwd.h>
303b188dabSeric #include <signal.h>
313b188dabSeric #include <stdlib.h>
323b188dabSeric #include <string.h>
333b188dabSeric #include <syslog.h>
343b188dabSeric #include <unistd.h>
353b188dabSeric
363b188dabSeric #include "lpd.h"
373b188dabSeric
383b188dabSeric #include "log.h"
393b188dabSeric #include "proc.h"
403b188dabSeric
413b188dabSeric #define CONTROL_BACKLOG 5
423b188dabSeric
433b188dabSeric static void control_init(const char *);
443b188dabSeric static void control_listen(void);
453b188dabSeric static void control_pause(void);
463b188dabSeric static void control_resume(void);
473b188dabSeric static void control_accept(int, short, void *);
483b188dabSeric static void control_close(struct imsgproc *);
493b188dabSeric static void control_dispatch_priv(struct imsgproc *, struct imsg *, void *);
503b188dabSeric static void control_dispatch_client(struct imsgproc *, struct imsg *, void *);
513b188dabSeric
523b188dabSeric static struct {
533b188dabSeric struct event evt;
543b188dabSeric int fd;
553b188dabSeric int pause;
563b188dabSeric } ctl;
573b188dabSeric
583b188dabSeric void
control(int debug,int verbose)593b188dabSeric control(int debug, int verbose)
603b188dabSeric {
613b188dabSeric struct passwd *pw;
623b188dabSeric
633b188dabSeric /* Early initialisation. */
643b188dabSeric log_init(debug, LOG_DAEMON);
653b188dabSeric log_setverbose(verbose);
663b188dabSeric log_procinit("control");
673b188dabSeric setproctitle("control");
683b188dabSeric
693b188dabSeric control_init(LPD_SOCKET);
703b188dabSeric
713a50f0a9Sjmc /* Drop privileges. */
723b188dabSeric if ((pw = getpwnam(LPD_USER)) == NULL)
733b188dabSeric fatalx("unknown user " LPD_USER);
743b188dabSeric
75*12690b15Sbluhm if (chroot(_PATH_VAREMPTY) == -1)
76*12690b15Sbluhm fatal("%s: chroot", __func__);
77*12690b15Sbluhm if (chdir("/") == -1)
78*12690b15Sbluhm fatal("%s: chdir", __func__);
79*12690b15Sbluhm
803b188dabSeric if (setgroups(1, &pw->pw_gid) ||
813b188dabSeric setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
823b188dabSeric setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
833b188dabSeric fatal("cannot drop privileges");
843b188dabSeric
853b188dabSeric if (pledge("stdio unix recvfd sendfd", NULL) == -1)
863b188dabSeric fatal("%s: pledge", __func__);
873b188dabSeric
883b188dabSeric event_init();
893b188dabSeric
903b188dabSeric signal(SIGPIPE, SIG_IGN);
913b188dabSeric
923b188dabSeric /* Setup imsg socket with parent. */
933b188dabSeric p_priv = proc_attach(PROC_PRIV, 3);
943b188dabSeric if (p_priv == NULL)
953b188dabSeric fatal("%s: proc_attach", __func__);
963b188dabSeric proc_setcallback(p_priv, control_dispatch_priv, NULL);
973b188dabSeric proc_enable(p_priv);
983b188dabSeric
993b188dabSeric event_dispatch();
1003b188dabSeric
1013b188dabSeric exit(0);
1023b188dabSeric }
1033b188dabSeric
1043b188dabSeric static void
control_init(const char * path)1053b188dabSeric control_init(const char *path)
1063b188dabSeric {
1073b188dabSeric struct sockaddr_un sun;
1083b188dabSeric mode_t old_umask;
1093b188dabSeric int fd;
1103b188dabSeric
1113b188dabSeric fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
1123b188dabSeric if (fd == -1)
1133b188dabSeric fatal("%s: socket", __func__);
1143b188dabSeric
1153b188dabSeric memset(&sun, 0, sizeof(sun));
1163b188dabSeric sun.sun_family = AF_UNIX;
1173b188dabSeric strlcpy(sun.sun_path, LPD_SOCKET, sizeof(sun.sun_path));
1183b188dabSeric
1193b188dabSeric if ((unlink(path) == -1) && (errno != ENOENT))
1203b188dabSeric fatal("%s: unlink: %s", __func__, path);
1213b188dabSeric
1223b188dabSeric old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
1233b188dabSeric if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
1243b188dabSeric fatal("%s: bind: %s", __func__, path);
1253b188dabSeric umask(old_umask);
1263b188dabSeric
1273b188dabSeric if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1)
1283b188dabSeric fatal("%s: chmod: %s", __func__, path);
1293b188dabSeric
1303b188dabSeric ctl.fd = fd;
1313b188dabSeric }
1323b188dabSeric
1333b188dabSeric static void
control_listen(void)1343b188dabSeric control_listen(void)
1353b188dabSeric {
1363b188dabSeric if (listen(ctl.fd, CONTROL_BACKLOG) == -1)
1373b188dabSeric fatal("%s: listen", __func__);
1383b188dabSeric
1393b188dabSeric ctl.pause = 0;
1403b188dabSeric control_resume();
1413b188dabSeric }
1423b188dabSeric
1433b188dabSeric static void
control_pause(void)1443b188dabSeric control_pause(void)
1453b188dabSeric {
1463b188dabSeric struct timeval tv;
1473b188dabSeric
1483b188dabSeric event_del(&ctl.evt);
1493b188dabSeric
1503b188dabSeric tv.tv_sec = 1;
1513b188dabSeric tv.tv_usec = 0;
1523b188dabSeric
1533b188dabSeric evtimer_set(&ctl.evt, control_accept, NULL);
1543b188dabSeric evtimer_add(&ctl.evt, &tv);
1553b188dabSeric ctl.pause = 1;
1563b188dabSeric }
1573b188dabSeric
1583b188dabSeric static void
control_resume(void)1593b188dabSeric control_resume(void)
1603b188dabSeric {
1613b188dabSeric if (ctl.pause) {
1623b188dabSeric evtimer_del(&ctl.evt);
1633b188dabSeric ctl.pause = 0;
1643b188dabSeric }
1653b188dabSeric event_set(&ctl.evt, ctl.fd, EV_READ | EV_PERSIST, control_accept, NULL);
1663b188dabSeric event_add(&ctl.evt, NULL);
1673b188dabSeric }
1683b188dabSeric
1693b188dabSeric static void
control_accept(int fd,short event,void * arg)1703b188dabSeric control_accept(int fd, short event, void *arg)
1713b188dabSeric {
1723b188dabSeric struct imsgproc *proc;
1733b188dabSeric int sock;
1743b188dabSeric
1753b188dabSeric if (ctl.pause) {
1763b188dabSeric ctl.pause = 0;
1773b188dabSeric control_resume();
1783b188dabSeric return;
1793b188dabSeric }
1803b188dabSeric
1813b188dabSeric sock = accept4(ctl.fd, NULL, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK);
1823b188dabSeric if (sock == -1) {
1833b188dabSeric if (errno == ENFILE || errno == EMFILE)
1843b188dabSeric control_pause();
1853b188dabSeric else if (errno != EWOULDBLOCK && errno != EINTR &&
1863b188dabSeric errno != ECONNABORTED)
1873b188dabSeric log_warn("%s: accept4", __func__);
1883b188dabSeric return;
1893b188dabSeric }
1903b188dabSeric
1913b188dabSeric proc = proc_attach(PROC_CLIENT, sock);
1923b188dabSeric if (proc == NULL) {
1933b188dabSeric log_warn("%s: proc_attach", __func__);
1943b188dabSeric close(sock);
1953b188dabSeric return;
1963b188dabSeric }
1973b188dabSeric proc_setcallback(proc, control_dispatch_client, NULL);
1983b188dabSeric proc_enable(proc);
1993b188dabSeric }
2003b188dabSeric
2013b188dabSeric static void
control_close(struct imsgproc * proc)2023b188dabSeric control_close(struct imsgproc *proc)
2033b188dabSeric {
2043b188dabSeric proc_free(proc);
2053b188dabSeric
2063b188dabSeric if (ctl.pause)
2073b188dabSeric control_resume();
2083b188dabSeric }
2093b188dabSeric
2103b188dabSeric static void
control_dispatch_priv(struct imsgproc * proc,struct imsg * imsg,void * arg)2113b188dabSeric control_dispatch_priv(struct imsgproc *proc, struct imsg *imsg, void *arg)
2123b188dabSeric {
2133b188dabSeric if (imsg == NULL) {
2143b188dabSeric log_debug("%s: imsg connection lost", __func__);
2153b188dabSeric event_loopexit(NULL);
2163b188dabSeric return;
2173b188dabSeric }
2183b188dabSeric
2193b188dabSeric if (log_getverbose() > LOGLEVEL_IMSG)
2203b188dabSeric log_imsg(proc, imsg);
2213b188dabSeric
2223b188dabSeric switch (imsg->hdr.type) {
2233b188dabSeric case IMSG_CONF_START:
2243b188dabSeric m_end(proc);
2253b188dabSeric break;
2263b188dabSeric
2273b188dabSeric case IMSG_CONF_END:
2283b188dabSeric m_end(proc);
2293b188dabSeric control_listen();
2303b188dabSeric break;
2313b188dabSeric
2323b188dabSeric default:
2333b188dabSeric fatalx("%s: unexpected imsg %s", __func__,
2343b188dabSeric log_fmt_imsgtype(imsg->hdr.type));
2353b188dabSeric }
2363b188dabSeric }
2373b188dabSeric
2383b188dabSeric static void
control_dispatch_client(struct imsgproc * proc,struct imsg * imsg,void * arg)2393b188dabSeric control_dispatch_client(struct imsgproc *proc, struct imsg *imsg, void *arg)
2403b188dabSeric {
2413b188dabSeric if (imsg == NULL) {
2423b188dabSeric control_close(proc);
2433b188dabSeric return;
2443b188dabSeric }
2453b188dabSeric
2463b188dabSeric if (log_getverbose() > LOGLEVEL_IMSG)
2473b188dabSeric log_imsg(proc, imsg);
2483b188dabSeric
2493b188dabSeric switch (imsg->hdr.type) {
2503b188dabSeric default:
2513b188dabSeric log_debug("%s: error handling imsg %d", __func__,
2523b188dabSeric imsg->hdr.type);
2533b188dabSeric }
2543b188dabSeric }
255