xref: /openbsd-src/usr.sbin/lpd/frontend.c (revision af27b3cce1070d60f0e791a83d790160efdc2be8)
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