xref: /openbsd-src/usr.sbin/lpd/control.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: control.c,v 1.1.1.1 2018/04/27 16:14:35 eric 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 <pwd.h>
29 #include <signal.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <unistd.h>
34 
35 #include "lpd.h"
36 
37 #include "log.h"
38 #include "proc.h"
39 
40 #define	CONTROL_BACKLOG	5
41 
42 static void control_init(const char *);
43 static void control_listen(void);
44 static void control_pause(void);
45 static void control_resume(void);
46 static void control_accept(int, short, void *);
47 static void control_close(struct imsgproc *);
48 static void control_dispatch_priv(struct imsgproc *, struct imsg *, void *);
49 static void control_dispatch_client(struct imsgproc *, struct imsg *, void *);
50 
51 static struct {
52 	struct event	evt;
53 	int		fd;
54 	int		pause;
55 } ctl;
56 
57 void
58 control(int debug, int verbose)
59 {
60 	struct passwd *pw;
61 
62 	/* Early initialisation. */
63 	log_init(debug, LOG_DAEMON);
64 	log_setverbose(verbose);
65 	log_procinit("control");
66 	setproctitle("control");
67 
68 	control_init(LPD_SOCKET);
69 
70 	/* Drop priviledges. */
71 	if ((pw = getpwnam(LPD_USER)) == NULL)
72 		fatalx("unknown user " LPD_USER);
73 
74 	if (setgroups(1, &pw->pw_gid) ||
75 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
76 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
77 		fatal("cannot drop privileges");
78 
79 	if (chroot(pw->pw_dir) == 1)
80 		fatal("%s: chroot", __func__);
81 
82 	if (pledge("stdio unix recvfd sendfd", NULL) == -1)
83 		fatal("%s: pledge", __func__);
84 
85 	event_init();
86 
87 	signal(SIGPIPE, SIG_IGN);
88 
89 	/* Setup imsg socket with parent. */
90 	p_priv = proc_attach(PROC_PRIV, 3);
91 	if (p_priv == NULL)
92 		fatal("%s: proc_attach", __func__);
93 	proc_setcallback(p_priv, control_dispatch_priv, NULL);
94 	proc_enable(p_priv);
95 
96 	event_dispatch();
97 
98 	exit(0);
99 }
100 
101 static void
102 control_init(const char *path)
103 {
104 	struct sockaddr_un sun;
105 	mode_t old_umask;
106 	int fd;
107 
108 	fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
109 	if (fd == -1)
110 		fatal("%s: socket", __func__);
111 
112 	memset(&sun, 0, sizeof(sun));
113 	sun.sun_family = AF_UNIX;
114 	strlcpy(sun.sun_path, LPD_SOCKET, sizeof(sun.sun_path));
115 
116 	if ((unlink(path) == -1) && (errno != ENOENT))
117 		fatal("%s: unlink: %s", __func__, path);
118 
119 	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
120 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
121 		fatal("%s: bind: %s", __func__, path);
122 	umask(old_umask);
123 
124 	if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1)
125 		fatal("%s: chmod: %s", __func__, path);
126 
127 	ctl.fd = fd;
128 }
129 
130 static void
131 control_listen(void)
132 {
133 	if (listen(ctl.fd, CONTROL_BACKLOG) == -1)
134 		fatal("%s: listen", __func__);
135 
136 	ctl.pause = 0;
137 	control_resume();
138 }
139 
140 static void
141 control_pause(void)
142 {
143 	struct timeval tv;
144 
145 	event_del(&ctl.evt);
146 
147 	tv.tv_sec = 1;
148 	tv.tv_usec = 0;
149 
150 	evtimer_set(&ctl.evt, control_accept, NULL);
151 	evtimer_add(&ctl.evt, &tv);
152 	ctl.pause = 1;
153 }
154 
155 static void
156 control_resume(void)
157 {
158 	if (ctl.pause) {
159 		evtimer_del(&ctl.evt);
160 		ctl.pause = 0;
161 	}
162 	event_set(&ctl.evt, ctl.fd, EV_READ | EV_PERSIST, control_accept, NULL);
163 	event_add(&ctl.evt, NULL);
164 }
165 
166 static void
167 control_accept(int fd, short event, void *arg)
168 {
169 	struct imsgproc *proc;
170 	int sock;
171 
172 	if (ctl.pause) {
173 		ctl.pause = 0;
174 		control_resume();
175 		return;
176 	}
177 
178 	sock = accept4(ctl.fd, NULL, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK);
179 	if (sock == -1) {
180 		if (errno == ENFILE || errno == EMFILE)
181 			control_pause();
182 		else if (errno != EWOULDBLOCK && errno != EINTR &&
183 		    errno != ECONNABORTED)
184 			log_warn("%s: accept4", __func__);
185 		return;
186 	}
187 
188 	proc = proc_attach(PROC_CLIENT, sock);
189 	if (proc == NULL) {
190 		log_warn("%s: proc_attach", __func__);
191 		close(sock);
192 		return;
193 	}
194 	proc_setcallback(proc, control_dispatch_client, NULL);
195 	proc_enable(proc);
196 }
197 
198 static void
199 control_close(struct imsgproc *proc)
200 {
201 	proc_free(proc);
202 
203 	if (ctl.pause)
204 		control_resume();
205 }
206 
207 static void
208 control_dispatch_priv(struct imsgproc *proc, struct imsg *imsg, void *arg)
209 {
210 	if (imsg == NULL) {
211 		log_debug("%s: imsg connection lost", __func__);
212 		event_loopexit(NULL);
213 		return;
214 	}
215 
216 	if (log_getverbose() > LOGLEVEL_IMSG)
217 		log_imsg(proc, imsg);
218 
219 	switch (imsg->hdr.type) {
220 	case IMSG_CONF_START:
221 		m_end(proc);
222 		break;
223 
224 	case IMSG_CONF_END:
225 		m_end(proc);
226 		control_listen();
227 		break;
228 
229 	default:
230 		fatalx("%s: unexpected imsg %s", __func__,
231 		    log_fmt_imsgtype(imsg->hdr.type));
232 	}
233 }
234 
235 static void
236 control_dispatch_client(struct imsgproc *proc, struct imsg *imsg, void *arg)
237 {
238 	if (imsg == NULL) {
239 		control_close(proc);
240 		return;
241 	}
242 
243 	if (log_getverbose() > LOGLEVEL_IMSG)
244 		log_imsg(proc, imsg);
245 
246 	switch (imsg->hdr.type) {
247 	default:
248 		log_debug("%s: error handling imsg %d", __func__,
249 		    imsg->hdr.type);
250 	}
251 }
252