xref: /openbsd-src/usr.sbin/lpd/control.c (revision 12690b15432155c8cdd2d0cd268bc11c914fd725)
1 /*	$OpenBSD: control.c,v 1.3 2024/03/22 19:14:28 bluhm Exp $	*/
2 
3 /*
4  * Copyright (c) 2017 Eric Faurot <eric@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 
25 #include <errno.h>
26 #include <event.h>
27 #include <imsg.h>
28 #include <paths.h>
29 #include <pwd.h>
30 #include <signal.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <unistd.h>
35 
36 #include "lpd.h"
37 
38 #include "log.h"
39 #include "proc.h"
40 
41 #define	CONTROL_BACKLOG	5
42 
43 static void control_init(const char *);
44 static void control_listen(void);
45 static void control_pause(void);
46 static void control_resume(void);
47 static void control_accept(int, short, void *);
48 static void control_close(struct imsgproc *);
49 static void control_dispatch_priv(struct imsgproc *, struct imsg *, void *);
50 static void control_dispatch_client(struct imsgproc *, struct imsg *, void *);
51 
52 static struct {
53 	struct event	evt;
54 	int		fd;
55 	int		pause;
56 } ctl;
57 
58 void
control(int debug,int verbose)59 control(int debug, int verbose)
60 {
61 	struct passwd *pw;
62 
63 	/* Early initialisation. */
64 	log_init(debug, LOG_DAEMON);
65 	log_setverbose(verbose);
66 	log_procinit("control");
67 	setproctitle("control");
68 
69 	control_init(LPD_SOCKET);
70 
71 	/* Drop privileges. */
72 	if ((pw = getpwnam(LPD_USER)) == NULL)
73 		fatalx("unknown user " LPD_USER);
74 
75 	if (chroot(_PATH_VAREMPTY) == -1)
76 		fatal("%s: chroot", __func__);
77 	if (chdir("/") == -1)
78 		fatal("%s: chdir", __func__);
79 
80 	if (setgroups(1, &pw->pw_gid) ||
81 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
82 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
83 		fatal("cannot drop privileges");
84 
85 	if (pledge("stdio unix recvfd sendfd", NULL) == -1)
86 		fatal("%s: pledge", __func__);
87 
88 	event_init();
89 
90 	signal(SIGPIPE, SIG_IGN);
91 
92 	/* Setup imsg socket with parent. */
93 	p_priv = proc_attach(PROC_PRIV, 3);
94 	if (p_priv == NULL)
95 		fatal("%s: proc_attach", __func__);
96 	proc_setcallback(p_priv, control_dispatch_priv, NULL);
97 	proc_enable(p_priv);
98 
99 	event_dispatch();
100 
101 	exit(0);
102 }
103 
104 static void
control_init(const char * path)105 control_init(const char *path)
106 {
107 	struct sockaddr_un sun;
108 	mode_t old_umask;
109 	int fd;
110 
111 	fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
112 	if (fd == -1)
113 		fatal("%s: socket", __func__);
114 
115 	memset(&sun, 0, sizeof(sun));
116 	sun.sun_family = AF_UNIX;
117 	strlcpy(sun.sun_path, LPD_SOCKET, sizeof(sun.sun_path));
118 
119 	if ((unlink(path) == -1) && (errno != ENOENT))
120 		fatal("%s: unlink: %s", __func__, path);
121 
122 	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
123 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
124 		fatal("%s: bind: %s", __func__, path);
125 	umask(old_umask);
126 
127 	if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1)
128 		fatal("%s: chmod: %s", __func__, path);
129 
130 	ctl.fd = fd;
131 }
132 
133 static void
control_listen(void)134 control_listen(void)
135 {
136 	if (listen(ctl.fd, CONTROL_BACKLOG) == -1)
137 		fatal("%s: listen", __func__);
138 
139 	ctl.pause = 0;
140 	control_resume();
141 }
142 
143 static void
control_pause(void)144 control_pause(void)
145 {
146 	struct timeval tv;
147 
148 	event_del(&ctl.evt);
149 
150 	tv.tv_sec = 1;
151 	tv.tv_usec = 0;
152 
153 	evtimer_set(&ctl.evt, control_accept, NULL);
154 	evtimer_add(&ctl.evt, &tv);
155 	ctl.pause = 1;
156 }
157 
158 static void
control_resume(void)159 control_resume(void)
160 {
161 	if (ctl.pause) {
162 		evtimer_del(&ctl.evt);
163 		ctl.pause = 0;
164 	}
165 	event_set(&ctl.evt, ctl.fd, EV_READ | EV_PERSIST, control_accept, NULL);
166 	event_add(&ctl.evt, NULL);
167 }
168 
169 static void
control_accept(int fd,short event,void * arg)170 control_accept(int fd, short event, void *arg)
171 {
172 	struct imsgproc *proc;
173 	int sock;
174 
175 	if (ctl.pause) {
176 		ctl.pause = 0;
177 		control_resume();
178 		return;
179 	}
180 
181 	sock = accept4(ctl.fd, NULL, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK);
182 	if (sock == -1) {
183 		if (errno == ENFILE || errno == EMFILE)
184 			control_pause();
185 		else if (errno != EWOULDBLOCK && errno != EINTR &&
186 		    errno != ECONNABORTED)
187 			log_warn("%s: accept4", __func__);
188 		return;
189 	}
190 
191 	proc = proc_attach(PROC_CLIENT, sock);
192 	if (proc == NULL) {
193 		log_warn("%s: proc_attach", __func__);
194 		close(sock);
195 		return;
196 	}
197 	proc_setcallback(proc, control_dispatch_client, NULL);
198 	proc_enable(proc);
199 }
200 
201 static void
control_close(struct imsgproc * proc)202 control_close(struct imsgproc *proc)
203 {
204 	proc_free(proc);
205 
206 	if (ctl.pause)
207 		control_resume();
208 }
209 
210 static void
control_dispatch_priv(struct imsgproc * proc,struct imsg * imsg,void * arg)211 control_dispatch_priv(struct imsgproc *proc, struct imsg *imsg, void *arg)
212 {
213 	if (imsg == NULL) {
214 		log_debug("%s: imsg connection lost", __func__);
215 		event_loopexit(NULL);
216 		return;
217 	}
218 
219 	if (log_getverbose() > LOGLEVEL_IMSG)
220 		log_imsg(proc, imsg);
221 
222 	switch (imsg->hdr.type) {
223 	case IMSG_CONF_START:
224 		m_end(proc);
225 		break;
226 
227 	case IMSG_CONF_END:
228 		m_end(proc);
229 		control_listen();
230 		break;
231 
232 	default:
233 		fatalx("%s: unexpected imsg %s", __func__,
234 		    log_fmt_imsgtype(imsg->hdr.type));
235 	}
236 }
237 
238 static void
control_dispatch_client(struct imsgproc * proc,struct imsg * imsg,void * arg)239 control_dispatch_client(struct imsgproc *proc, struct imsg *imsg, void *arg)
240 {
241 	if (imsg == NULL) {
242 		control_close(proc);
243 		return;
244 	}
245 
246 	if (log_getverbose() > LOGLEVEL_IMSG)
247 		log_imsg(proc, imsg);
248 
249 	switch (imsg->hdr.type) {
250 	default:
251 		log_debug("%s: error handling imsg %d", __func__,
252 		    imsg->hdr.type);
253 	}
254 }
255