xref: /openbsd/usr.sbin/lpd/lpd.c (revision 3a50f0a9)
1 /*	$OpenBSD: lpd.c,v 1.3 2022/12/28 21:30:17 jmc 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/stat.h>
21 #include <sys/un.h>
22 #include <sys/wait.h>
23 
24 #include <errno.h>
25 #include <getopt.h>
26 #include <pwd.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <signal.h>
30 #include <string.h>
31 #include <syslog.h>
32 #include <unistd.h>
33 
34 #include "lpd.h"
35 
36 #include "log.h"
37 #include "proc.h"
38 
39 struct lpd_conf *env;
40 struct imsgproc *p_control;
41 struct imsgproc *p_engine;
42 struct imsgproc *p_frontend;
43 struct imsgproc *p_priv;
44 
45 static void priv_dispatch_control(struct imsgproc *, struct imsg *, void *);
46 static void priv_dispatch_engine(struct imsgproc *, struct imsg *, void *);
47 static void priv_dispatch_frontend(struct imsgproc *, struct imsg *, void *);
48 static void priv_dispatch_printer(struct imsgproc *, struct imsg *, void *);
49 static void priv_open_listener(struct listener *);
50 static void priv_send_config(void);
51 static void priv_sighandler(int, short, void *);
52 static void priv_shutdown(void);
53 static void priv_run_printer(const char *);
54 
55 static char **saved_argv;
56 static int saved_argc;
57 
58 static void
usage(void)59 usage(void)
60 {
61 	extern char *__progname;
62 
63 	fprintf(stderr, "usage: %s [-dnv] [-D macro=value]  [-f file]\n",
64 	    __progname);
65 	exit(1);
66 }
67 
68 int
main(int argc,char ** argv)69 main(int argc, char **argv)
70 {
71 	struct listener *l;
72 	struct event evt_sigchld, evt_sigint, evt_sigterm, evt_sighup;
73 	const char *conffile = LPD_CONFIG, *reexec = NULL;
74 	int sp[2], ch, debug = 0, nflag = 0, verbose = 1;
75 
76 	saved_argv = argv;
77 	saved_argc = argc;
78 
79 	log_init(1, LOG_LPR);
80 	log_setverbose(0);
81 
82 	while ((ch = getopt(argc, argv, "D:df:nvX:")) != -1) {
83 		switch (ch) {
84 		case 'D':
85 			if (cmdline_symset(optarg) < 0)
86 				log_warnx("could not parse macro definition %s",
87 				    optarg);
88 			break;
89 		case 'd':
90 			debug = 1;
91 			break;
92 		case 'f':
93 			conffile = optarg;
94 			break;
95 		case 'n':
96 			nflag = 1;
97 			break;
98 		case 'v':
99 			verbose++;
100 			break;
101 		case 'X':
102 			reexec = optarg;
103 			break;
104 		default:
105 			usage();
106 		}
107 	}
108 
109 	argv += optind;
110 	argc -= optind;
111 
112 	if (argc || *argv)
113 		usage();
114 
115 	if (reexec) {
116 		if (!strcmp(reexec, "control"))
117 			control(debug, verbose);
118 		if (!strcmp(reexec, "engine"))
119 			engine(debug, verbose);
120 		if (!strcmp(reexec, "frontend"))
121 			frontend(debug, verbose);
122 		if (!strncmp(reexec, "printer:", 8))
123 			printer(debug, verbose, strchr(reexec, ':') + 1);
124 		fatalx("unknown process %s", reexec);
125 	}
126 
127 	/* Parse config file. */
128 	env = parse_config(conffile, verbose);
129 	if (env == NULL)
130 		exit(1);
131 
132 	if (nflag) {
133 		fprintf(stderr, "configuration OK\n");
134 		exit(0);
135 	}
136 
137 	/* Check for root privileges. */
138 	if (geteuid())
139 		fatalx("need root privileges");
140 
141 	/* Check for assigned daemon user. */
142 	if (getpwnam(LPD_USER) == NULL)
143 		fatalx("unknown user %s", LPD_USER);
144 
145 	log_init(debug, LOG_LPR);
146 	log_setverbose(verbose);
147 	log_procinit("priv");
148 	setproctitle("priv");
149 
150 	if (!debug)
151 		if (daemon(1, 0) == -1)
152 			fatal("daemon");
153 
154         log_info("startup");
155 
156 	TAILQ_FOREACH(l, &env->listeners, entry)
157 		priv_open_listener(l);
158 
159 	event_init();
160 
161 	signal_set(&evt_sigint, SIGINT, priv_sighandler, NULL);
162 	signal_add(&evt_sigint, NULL);
163 	signal_set(&evt_sigterm, SIGTERM, priv_sighandler, NULL);
164 	signal_add(&evt_sigterm, NULL);
165 	signal_set(&evt_sigchld, SIGCHLD, priv_sighandler, NULL);
166 	signal_add(&evt_sigchld, NULL);
167 	signal_set(&evt_sighup, SIGHUP, priv_sighandler, NULL);
168 	signal_add(&evt_sighup, NULL);
169 	signal(SIGPIPE, SIG_IGN);
170 
171 	/* Fork and exec unprivileged processes. */
172 	argv = calloc(saved_argc + 3, sizeof(*argv));
173 	if (argv == NULL)
174 		fatal("calloc");
175 	for (argc = 0; argc < saved_argc; argc++)
176 		argv[argc] = saved_argv[argc];
177 	argv[argc++] = "-X";
178 	argv[argc++] = "";
179 	argv[argc++] = NULL;
180 
181 	argv[argc - 2] = "control";
182 	p_control = proc_exec(PROC_CONTROL, argv);
183 	if (p_control == NULL)
184 		fatalx("cannot exec control process");
185 	proc_setcallback(p_control, priv_dispatch_control, NULL);
186 	proc_enable(p_control);
187 
188 	argv[argc - 2] = "engine";
189 	p_engine = proc_exec(PROC_ENGINE, argv);
190 	if (p_engine == NULL)
191 		fatalx("cannot exec engine process");
192 	proc_setcallback(p_engine, priv_dispatch_engine, NULL);
193 	proc_enable(p_engine);
194 
195 	argv[argc - 2] = "frontend";
196 	p_frontend = proc_exec(PROC_FRONTEND, argv);
197 	if (p_frontend == NULL)
198 		fatalx("cannot exec frontend process");
199 	proc_setcallback(p_frontend, priv_dispatch_frontend, NULL);
200 	proc_enable(p_frontend);
201 
202 	/* Connect processes. */
203 	if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, PF_UNSPEC, sp) == -1)
204 		fatal("socketpair");
205 	m_compose(p_engine, IMSG_SOCK_FRONTEND, 0, 0, sp[1], NULL, 0);
206 	m_compose(p_frontend, IMSG_SOCK_ENGINE, 0, 0, sp[0], NULL, 0);
207 
208 	priv_send_config();
209 
210 	if (pledge("stdio sendfd proc exec", NULL) == -1)
211 		fatal("pledge");
212 
213 	event_dispatch();
214 
215 	priv_shutdown();
216 
217 	return (0);
218 }
219 
220 static void
priv_sighandler(int sig,short ev,void * arg)221 priv_sighandler(int sig, short ev, void *arg)
222 {
223 	pid_t pid;
224 	int status;
225 
226 	switch (sig) {
227 	case SIGTERM:
228 	case SIGINT:
229 		event_loopbreak();
230 		break;
231 	case SIGCHLD:
232 		do {
233 			pid = waitpid(-1, &status, WNOHANG);
234 			if (pid <= 0)
235 				continue;
236 			if (WIFSIGNALED(status))
237 				log_warnx("process %d terminated by signal %d",
238 				    (int)pid, WTERMSIG(status));
239 			else if (WIFEXITED(status) && WEXITSTATUS(status))
240 				log_warnx("process %d exited with status %d",
241 				    (int)pid, WEXITSTATUS(status));
242 			else if (WIFEXITED(status))
243 				log_debug("process %d exited normally",
244 				    (int)pid);
245 			else
246 				/* WIFSTOPPED or WIFCONTINUED */
247 				continue;
248 		} while (pid > 0 || (pid == -1 && errno == EINTR));
249 		break;
250 	default:
251 		fatalx("signal %d", sig);
252 	}
253 }
254 
255 static void
priv_shutdown(void)256 priv_shutdown(void)
257 {
258 	pid_t pid;
259 
260 	proc_free(p_control);
261 	proc_free(p_engine);
262 	proc_free(p_frontend);
263 
264 	do {
265 		pid = waitpid(WAIT_MYPGRP, NULL, 0);
266 	} while (pid != -1 || (pid == -1 && errno == EINTR));
267 
268 	log_info("exiting");
269 
270 	exit(0);
271 }
272 
273 static void
priv_open_listener(struct listener * l)274 priv_open_listener(struct listener *l)
275 {
276 	struct sockaddr_un *su;
277 	struct sockaddr *sa;
278 	const char *path;
279 	mode_t old_umask;
280 	int opt, sock, r;
281 
282 	sa = (struct sockaddr *)&l->ss;
283 
284 	sock = socket(sa->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
285 	if (sock == -1) {
286 		if (errno == EAFNOSUPPORT) {
287 			log_warn("%s: socket", __func__);
288 			return;
289 		}
290 		fatal("%s: socket", __func__);
291 	}
292 
293 	switch (sa->sa_family) {
294 	case AF_LOCAL:
295 		su = (struct sockaddr_un *)sa;
296 		path = su->sun_path;
297 		if (connect(sock, sa, sa->sa_len) == 0)
298 			fatalx("%s already in use", path);
299 
300 		if (unlink(path) == -1)
301 			if (errno != ENOENT)
302 				fatal("unlink: %s", path);
303 
304 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
305 		r = bind(sock, sa, sizeof(*su));
306 		(void)umask(old_umask);
307 
308 		if (r == -1)
309 			fatal("bind: %s", path);
310 		break;
311 
312 	case AF_INET:
313 	case AF_INET6:
314 		opt = 1;
315 		if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt,
316 		    sizeof(opt)) == -1)
317 			fatal("setsockopt: %s", log_fmt_sockaddr(sa));
318 
319 		if (bind(sock, sa, sa->sa_len) == -1)
320 			fatal("bind: %s", log_fmt_sockaddr(sa));
321 		break;
322 
323 	default:
324 		fatalx("bad address family %d", sa->sa_family);
325 	}
326 
327 	l->sock = sock;
328 }
329 
330 static void
priv_send_config(void)331 priv_send_config(void)
332 {
333 	struct listener *l;
334 
335 	m_compose(p_control, IMSG_CONF_START, 0, 0, -1, NULL, 0);
336 	m_compose(p_control, IMSG_CONF_END, 0, 0, -1, NULL, 0);
337 
338 	m_compose(p_engine, IMSG_CONF_START, 0, 0, -1, NULL, 0);
339 	m_compose(p_engine, IMSG_CONF_END, 0, 0, -1, NULL, 0);
340 
341 	m_compose(p_frontend, IMSG_CONF_START, 0, 0, -1, NULL, 0);
342 	TAILQ_FOREACH(l, &env->listeners, entry) {
343 		m_create(p_frontend, IMSG_CONF_LISTENER, 0, 0, l->sock);
344 		m_add_int(p_frontend, l->proto);
345 		m_add_sockaddr(p_frontend, (struct sockaddr *)(&l->ss));
346 		m_close(p_frontend);
347 	}
348 	m_compose(p_frontend, IMSG_CONF_END, 0, 0, -1, NULL, 0);
349 }
350 
351 static void
priv_dispatch_control(struct imsgproc * proc,struct imsg * imsg,void * arg)352 priv_dispatch_control(struct imsgproc *proc, struct imsg *imsg, void *arg)
353 {
354 	if (imsg == NULL)
355 		fatalx("%s: imsg connection lost", __func__);
356 
357 	if (log_getverbose() > LOGLEVEL_IMSG)
358 		log_imsg(proc, imsg);
359 
360 	switch (imsg->hdr.type) {
361 	default:
362 		fatalx("%s: unexpected imsg %s", __func__,
363 		    log_fmt_imsgtype(imsg->hdr.type));
364 	}
365 }
366 
367 static void
priv_dispatch_engine(struct imsgproc * proc,struct imsg * imsg,void * arg)368 priv_dispatch_engine(struct imsgproc *proc, struct imsg *imsg, void *arg)
369 {
370 	const char *prn;
371 
372 	if (imsg == NULL)
373 		fatalx("%s: imsg connection lost", __func__);
374 
375 	if (log_getverbose() > LOGLEVEL_IMSG)
376 		log_imsg(proc, imsg);
377 
378 	switch (imsg->hdr.type) {
379 	case IMSG_LPR_PRINTJOB:
380 		m_get_string(proc, &prn);
381 		m_end(proc);
382 		priv_run_printer(prn);
383 		break;
384 	default:
385 		fatalx("%s: unexpected imsg %s", __func__,
386 		    log_fmt_imsgtype(imsg->hdr.type));
387 	}
388 }
389 
390 static void
priv_dispatch_frontend(struct imsgproc * proc,struct imsg * imsg,void * arg)391 priv_dispatch_frontend(struct imsgproc *proc, struct imsg *imsg, void *arg)
392 {
393 	if (imsg == NULL)
394 		fatalx("%s: imsg connection lost", __func__);
395 
396 	if (log_getverbose() > LOGLEVEL_IMSG)
397 		log_imsg(proc, imsg);
398 
399 	switch (imsg->hdr.type) {
400 	default:
401 		fatalx("%s: unexpected imsg %s", __func__,
402 		    log_fmt_imsgtype(imsg->hdr.type));
403 	}
404 }
405 
406 static void
priv_dispatch_printer(struct imsgproc * proc,struct imsg * imsg,void * arg)407 priv_dispatch_printer(struct imsgproc *proc, struct imsg *imsg, void *arg)
408 {
409 	if (imsg == NULL) {
410 		log_debug("printer process ended, pid=%d, printer=%s",
411 		     proc_getpid(proc), proc_gettitle(proc));
412 		proc_free(proc);
413 		return;
414 	}
415 
416 	if (log_getverbose() > LOGLEVEL_IMSG)
417 		log_imsg(proc, imsg);
418 
419 	switch (imsg->hdr.type) {
420 	default:
421 		fatalx("%s: unexpected imsg %s", __func__,
422 		    log_fmt_imsgtype(imsg->hdr.type));
423 	}
424 }
425 
426 static void
priv_run_printer(const char * prn)427 priv_run_printer(const char *prn)
428 {
429 	struct imsgproc *p;
430 	char **argv, *buf;
431 	int argc;
432 
433 	if (asprintf(&buf, "printer:%s", prn) == -1) {
434 		log_warn("%s: asprintf", __func__);
435 		return;
436 	}
437 
438 	argv = calloc(saved_argc + 4, sizeof(*argv));
439 	if (argv == NULL) {
440 		log_warn("%s: calloc", __func__);
441 		free(buf);
442 		return;
443 	}
444 	for (argc = 0; argc < saved_argc; argc++)
445 		argv[argc] = saved_argv[argc];
446 	argv[argc++] = "-X";
447 	argv[argc++] = buf;
448 	argv[argc++] = NULL;
449 
450 	p = proc_exec(PROC_PRINTER, argv);
451 	if (p == NULL)
452 		log_warnx("%s: cannot exec printer process", __func__);
453 	else {
454 		proc_settitle(p, prn);
455 		proc_setcallback(p, priv_dispatch_printer, p);
456 		proc_enable(p);
457 	}
458 
459 	free(argv);
460 	free(buf);
461 }
462