1*af27b3ccSclaudio /* $OpenBSD: frontend.c,v 1.4 2024/11/21 13:34:51 claudio 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/tree.h> 203b188dabSeric 213b188dabSeric #include <errno.h> 223b188dabSeric #include <paths.h> 233b188dabSeric #include <pwd.h> 243b188dabSeric #include <signal.h> 253b188dabSeric #include <stdlib.h> 263b188dabSeric #include <syslog.h> 273b188dabSeric #include <unistd.h> 283b188dabSeric 293b188dabSeric #include "lpd.h" 303b188dabSeric 313b188dabSeric #include "log.h" 323b188dabSeric #include "proc.h" 333b188dabSeric 343b188dabSeric static void frontend_shutdown(void); 353b188dabSeric static void frontend_listen(struct listener *); 363b188dabSeric static void frontend_pause(struct listener *); 373b188dabSeric static void frontend_resume(struct listener *); 383b188dabSeric static void frontend_accept(int, short, void *); 393b188dabSeric static void frontend_dispatch_priv(struct imsgproc *, struct imsg *, void *); 403b188dabSeric static void frontend_dispatch_engine(struct imsgproc *, struct imsg *, void *); 413b188dabSeric 423b188dabSeric struct conn { 433b188dabSeric SPLAY_ENTRY(conn) entry; 443b188dabSeric struct listener *listener; 453b188dabSeric uint32_t id; 463b188dabSeric }; 473b188dabSeric 483b188dabSeric static int conn_cmp(struct conn *, struct conn *); 493b188dabSeric 503b188dabSeric SPLAY_HEAD(conntree, conn); 513b188dabSeric SPLAY_PROTOTYPE(conntree, conn, entry, conn_cmp); 523b188dabSeric 533b188dabSeric static struct conntree conns; 543b188dabSeric static struct lpd_conf *tmpconf; 553b188dabSeric 563b188dabSeric static int 573b188dabSeric conn_cmp(struct conn *a, struct conn *b) 583b188dabSeric { 593b188dabSeric if (a->id < b->id) 603b188dabSeric return (-1); 613b188dabSeric if (a->id > b->id) 623b188dabSeric return (1); 633b188dabSeric return (0); 643b188dabSeric } 653b188dabSeric 663b188dabSeric SPLAY_GENERATE(conntree, conn, entry, conn_cmp); 673b188dabSeric 683b188dabSeric void 693b188dabSeric frontend(int debug, int verbose) 703b188dabSeric { 713b188dabSeric struct passwd *pw; 723b188dabSeric 733b188dabSeric /* Early initialisation. */ 743b188dabSeric log_init(debug, LOG_LPR); 753b188dabSeric log_setverbose(verbose); 763b188dabSeric log_procinit("frontend"); 773b188dabSeric setproctitle("frontend"); 783b188dabSeric 793b188dabSeric SPLAY_INIT(&conns); 803b188dabSeric lpr_init(); 813b188dabSeric 823a50f0a9Sjmc /* Drop privileges. */ 833b188dabSeric if ((pw = getpwnam(LPD_USER)) == NULL) 843b188dabSeric fatal("%s: getpwnam: %s", __func__, LPD_USER); 853b188dabSeric 863b188dabSeric if (chroot(_PATH_VAREMPTY) == -1) 873b188dabSeric fatal("%s: chroot", __func__); 883b188dabSeric if (chdir("/") == -1) 893b188dabSeric fatal("%s: chdir", __func__); 903b188dabSeric 913b188dabSeric if (setgroups(1, &pw->pw_gid) || 923b188dabSeric setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 933b188dabSeric setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 943b188dabSeric fatal("%s: cannot drop privileges", __func__); 953b188dabSeric 963b188dabSeric if (pledge("stdio unix inet recvfd sendfd", NULL) == -1) 973b188dabSeric fatal("%s: pledge", __func__); 983b188dabSeric 993b188dabSeric event_init(); 1003b188dabSeric 1013b188dabSeric signal(SIGPIPE, SIG_IGN); 1023b188dabSeric 1033b188dabSeric /* Setup parent imsg socket. */ 1043b188dabSeric p_priv = proc_attach(PROC_PRIV, 3); 1053b188dabSeric if (p_priv == NULL) 1063b188dabSeric fatal("%s: proc_attach", __func__); 1073b188dabSeric proc_setcallback(p_priv, frontend_dispatch_priv, NULL); 1083b188dabSeric proc_enable(p_priv); 1093b188dabSeric 1103b188dabSeric event_dispatch(); 1113b188dabSeric 1123b188dabSeric frontend_shutdown(); 1133b188dabSeric } 1143b188dabSeric 1153b188dabSeric void 1163b188dabSeric frontend_conn_closed(uint32_t connid) 1173b188dabSeric { 1183b188dabSeric struct listener *l; 1193b188dabSeric struct conn key, *conn; 1203b188dabSeric 1213b188dabSeric key.id = connid; 1223b188dabSeric conn = SPLAY_FIND(conntree, &conns, &key); 1233b188dabSeric if (conn == NULL) 1243b188dabSeric fatalx("%s: %08x unknown connid", __func__, connid); 1253b188dabSeric 1263b188dabSeric l = conn->listener; 1273b188dabSeric 1283b188dabSeric if (log_getverbose() > LOGLEVEL_CONN) 1293b188dabSeric log_debug("%08x close %s", conn->id, 1303b188dabSeric log_fmt_proto(l->proto)); 1313b188dabSeric 1323b188dabSeric SPLAY_REMOVE(conntree, &conns, conn); 1333b188dabSeric free(conn); 1343b188dabSeric 1353b188dabSeric if (l->pause) 1363b188dabSeric frontend_resume(l); 1373b188dabSeric } 1383b188dabSeric 1393b188dabSeric static void 1403b188dabSeric frontend_shutdown() 1413b188dabSeric { 1423b188dabSeric struct listener *l; 1433b188dabSeric 1443b188dabSeric TAILQ_FOREACH(l, &env->listeners, entry) 1453b188dabSeric close(l->sock); 1463b188dabSeric 1473b188dabSeric log_debug("exiting"); 1483b188dabSeric 1493b188dabSeric exit(0); 1503b188dabSeric } 1513b188dabSeric 1523b188dabSeric static void 1533b188dabSeric frontend_listen(struct listener *l) 1543b188dabSeric { 1553b188dabSeric if (log_getverbose() > LOGLEVEL_CONN) 1563b188dabSeric log_debug("listen %s %s", log_fmt_proto(l->proto), 1573b188dabSeric log_fmt_sockaddr((struct sockaddr*)&l->ss)); 1583b188dabSeric 1593b188dabSeric if (listen(l->sock, 5) == -1) 1603b188dabSeric fatal("%s: listen", __func__); 1613b188dabSeric 1623b188dabSeric frontend_resume(l); 1633b188dabSeric } 1643b188dabSeric 1653b188dabSeric static void 1663b188dabSeric frontend_pause(struct listener *l) 1673b188dabSeric { 1683b188dabSeric struct timeval tv; 1693b188dabSeric 1703b188dabSeric event_del(&l->ev); 1713b188dabSeric 1723b188dabSeric tv.tv_sec = 2; 1733b188dabSeric tv.tv_usec = 0; 1743b188dabSeric 1753b188dabSeric evtimer_set(&l->ev, frontend_accept, l); 1763b188dabSeric evtimer_add(&l->ev, &tv); 1773b188dabSeric l->pause = 1; 1783b188dabSeric } 1793b188dabSeric 1803b188dabSeric static void 1813b188dabSeric frontend_resume(struct listener *l) 1823b188dabSeric { 1833b188dabSeric if (l->pause) { 1843b188dabSeric evtimer_del(&l->ev); 1853b188dabSeric l->pause = 0; 1863b188dabSeric } 1873b188dabSeric event_set(&l->ev, l->sock, EV_READ | EV_PERSIST, frontend_accept, l); 1883b188dabSeric event_add(&l->ev, NULL); 1893b188dabSeric } 1903b188dabSeric 1913b188dabSeric static void 1923b188dabSeric frontend_accept(int sock, short ev, void *arg) 1933b188dabSeric { 1943b188dabSeric struct listener *l = arg; 1953b188dabSeric struct sockaddr_storage ss; 1963b188dabSeric struct sockaddr *sa; 1973b188dabSeric struct conn *conn; 1983b188dabSeric socklen_t len; 1993b188dabSeric 2003b188dabSeric if (l->pause) { 2013b188dabSeric l->pause = 0; 2023b188dabSeric frontend_resume(l); 2033b188dabSeric return; 2043b188dabSeric } 2053b188dabSeric 2063b188dabSeric conn = calloc(1, sizeof(*conn)); 2073b188dabSeric if (conn == NULL) 2083b188dabSeric log_warn("%s: calloc", __func__); 2093b188dabSeric 2103b188dabSeric sa = (struct sockaddr *)&ss; 2113b188dabSeric len = sizeof(ss); 2123b188dabSeric sock = accept4(sock, sa, &len, SOCK_NONBLOCK); 2133b188dabSeric if (sock == -1) { 2143b188dabSeric if (errno == ENFILE || errno == EMFILE) 2153b188dabSeric frontend_pause(l); 2163b188dabSeric else if (errno != EWOULDBLOCK && errno != EINTR && 2173b188dabSeric errno != ECONNABORTED) 2183b188dabSeric log_warn("%s: accept4", __func__); 2193b188dabSeric free(conn); 2203b188dabSeric return; 2213b188dabSeric } 2223b188dabSeric 2233b188dabSeric if (conn == NULL) { 2243b188dabSeric close(sock); 2253b188dabSeric return; 2263b188dabSeric } 2273b188dabSeric 2283b188dabSeric while (conn->id == 0 || SPLAY_FIND(conntree, &conns, conn)) 2293b188dabSeric conn->id = arc4random(); 2303b188dabSeric SPLAY_INSERT(conntree, &conns, conn); 2313b188dabSeric conn->listener = l; 2323b188dabSeric 2333b188dabSeric if (log_getverbose() > LOGLEVEL_CONN) 2343b188dabSeric log_debug("%08x accept %s %s", conn->id, 2353b188dabSeric log_fmt_proto(conn->listener->proto), 2363b188dabSeric log_fmt_sockaddr((struct sockaddr*)&ss)); 2373b188dabSeric 2383b188dabSeric switch (l->proto) { 2393b188dabSeric case PROTO_LPR: 2403b188dabSeric lpr_conn(conn->id, l, sock, sa); 2413b188dabSeric break; 2423b188dabSeric default: 2433b188dabSeric fatalx("%s: unexpected protocol %d", __func__, l->proto); 2443b188dabSeric } 2453b188dabSeric } 2463b188dabSeric 2473b188dabSeric static void 2483b188dabSeric frontend_dispatch_priv(struct imsgproc *proc, struct imsg *imsg, void *arg) 2493b188dabSeric { 2503b188dabSeric struct listener *l; 251*af27b3ccSclaudio int fd; 2523b188dabSeric 2533b188dabSeric if (imsg == NULL) { 2543b188dabSeric log_debug("%s: imsg connection lost", __func__); 2553b188dabSeric event_loopexit(NULL); 2563b188dabSeric return; 2573b188dabSeric } 2583b188dabSeric 2593b188dabSeric if (log_getverbose() > LOGLEVEL_IMSG) 2603b188dabSeric log_imsg(proc, imsg); 2613b188dabSeric 2623b188dabSeric switch (imsg->hdr.type) { 2633b188dabSeric case IMSG_SOCK_ENGINE: 264*af27b3ccSclaudio if ((fd = imsg_get_fd(imsg)) == -1) 2653b188dabSeric fatalx("%s: engine socket not received", __func__); 2663b188dabSeric m_end(proc); 267*af27b3ccSclaudio p_engine = proc_attach(PROC_ENGINE, fd); 2683b188dabSeric proc_setcallback(p_engine, frontend_dispatch_engine, NULL); 2693b188dabSeric proc_enable(p_engine); 2703b188dabSeric break; 2713b188dabSeric 2723b188dabSeric case IMSG_CONF_START: 2733b188dabSeric m_end(proc); 2743b188dabSeric if ((tmpconf = calloc(1, sizeof(*tmpconf))) == NULL) 2753b188dabSeric fatal("%s: calloc", __func__); 2763b188dabSeric TAILQ_INIT(&tmpconf->listeners); 2773b188dabSeric break; 2783b188dabSeric 2793b188dabSeric case IMSG_CONF_LISTENER: 280*af27b3ccSclaudio if ((fd = imsg_get_fd(imsg)) == -1) 2813b188dabSeric fatalx("%s: listener socket not received", __func__); 2823b188dabSeric if ((l = calloc(1, sizeof(*l))) == NULL) 2833b188dabSeric fatal("%s: calloc", __func__); 2843b188dabSeric m_get_int(proc, &l->proto); 2853b188dabSeric m_get_sockaddr(proc, (struct sockaddr *)&l->ss); 2863b188dabSeric m_end(proc); 287*af27b3ccSclaudio l->sock = fd; 2883b188dabSeric TAILQ_INSERT_TAIL(&tmpconf->listeners, l, entry); 2893b188dabSeric break; 2903b188dabSeric 2913b188dabSeric case IMSG_CONF_END: 2923b188dabSeric m_end(proc); 2933b188dabSeric TAILQ_FOREACH(l, &tmpconf->listeners, entry) 2943b188dabSeric frontend_listen(l); 2953b188dabSeric env = tmpconf; 2963b188dabSeric break; 2973b188dabSeric 2983b188dabSeric default: 2993b188dabSeric fatalx("%s: unexpected imsg %s", __func__, 3003b188dabSeric log_fmt_imsgtype(imsg->hdr.type)); 3013b188dabSeric } 3023b188dabSeric } 3033b188dabSeric 3043b188dabSeric static void 3053b188dabSeric frontend_dispatch_engine(struct imsgproc *proc, struct imsg *imsg, void *arg) 3063b188dabSeric { 3073b188dabSeric if (imsg == NULL) { 3083b188dabSeric log_debug("%s: imsg connection lost", __func__); 3093b188dabSeric event_loopexit(NULL); 3103b188dabSeric return; 3113b188dabSeric } 3123b188dabSeric 3133b188dabSeric if (log_getverbose() > LOGLEVEL_IMSG) 3143b188dabSeric log_imsg(proc, imsg); 3153b188dabSeric 3163b188dabSeric switch (imsg->hdr.type) { 317db7b06acSeric case IMSG_GETADDRINFO: 318db7b06acSeric case IMSG_GETADDRINFO_END: 319db7b06acSeric case IMSG_GETNAMEINFO: 3203b188dabSeric resolver_dispatch_result(proc, imsg); 3213b188dabSeric break; 3223b188dabSeric 3233b188dabSeric case IMSG_LPR_ALLOWEDHOST: 3243b188dabSeric case IMSG_LPR_DISPLAYQ: 3253b188dabSeric case IMSG_LPR_RECVJOB: 3263b188dabSeric case IMSG_LPR_RECVJOB_CF: 3273b188dabSeric case IMSG_LPR_RECVJOB_DF: 3283b188dabSeric case IMSG_LPR_RMJOB: 3293b188dabSeric lpr_dispatch_engine(proc, imsg); 3303b188dabSeric break; 3313b188dabSeric 3323b188dabSeric default: 3333b188dabSeric fatalx("%s: unexpected imsg %s", __func__, 3343b188dabSeric log_fmt_imsgtype(imsg->hdr.type)); 3353b188dabSeric } 3363b188dabSeric } 337