1 /*	$OpenBSD: control.c,v 1.14 2021/04/20 21:11:56 dv Exp $	*/
2 
3 /*
4  * Copyright (c) 2003, 2004 Henning Brauer <henning@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/queue.h>
20 #include <sys/stat.h>
21 #include <sys/socket.h>
22 #include <sys/time.h>
23 #include <sys/un.h>
24 #include <sys/param.h>
25 
26 #include <errno.h>
27 #include <event.h>
28 #include <fcntl.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <imsg.h>
33 
34 #include "httpd.h"
35 
36 #define	CONTROL_BACKLOG	5
37 
38 struct ctl_connlist ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns);
39 
40 void		 control_accept(int, short, void *);
41 void		 control_close(int, struct control_sock *);
42 
43 int
control_init(struct privsep * ps,struct control_sock * cs)44 control_init(struct privsep *ps, struct control_sock *cs)
45 {
46 	struct httpd		*env = ps->ps_env;
47 	struct sockaddr_un	 sun;
48 	int			 fd;
49 	mode_t			 old_umask, mode;
50 
51 	if (cs->cs_name == NULL)
52 		return (0);
53 
54 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) {
55 		log_warn("%s: socket", __func__);
56 		return (-1);
57 	}
58 
59 	sun.sun_family = AF_UNIX;
60 	if (strlcpy(sun.sun_path, cs->cs_name,
61 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
62 		log_warn("%s: %s name too long", __func__, cs->cs_name);
63 		close(fd);
64 		return (-1);
65 	}
66 
67 	if (unlink(cs->cs_name) == -1)
68 		if (errno != ENOENT) {
69 			log_warn("%s: unlink %s", __func__, cs->cs_name);
70 			close(fd);
71 			return (-1);
72 		}
73 
74 	if (cs->cs_restricted) {
75 		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
76 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
77 	} else {
78 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
79 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
80 	}
81 
82 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
83 		log_warn("%s: bind: %s", __func__, cs->cs_name);
84 		close(fd);
85 		(void)umask(old_umask);
86 		return (-1);
87 	}
88 	(void)umask(old_umask);
89 
90 	if (chmod(cs->cs_name, mode) == -1) {
91 		log_warn("%s: chmod", __func__);
92 		close(fd);
93 		(void)unlink(cs->cs_name);
94 		return (-1);
95 	}
96 
97 	cs->cs_fd = fd;
98 	cs->cs_env = env;
99 
100 	return (0);
101 }
102 
103 int
control_listen(struct control_sock * cs)104 control_listen(struct control_sock *cs)
105 {
106 	if (cs->cs_name == NULL)
107 		return (0);
108 
109 	if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
110 		log_warn("%s: listen", __func__);
111 		return (-1);
112 	}
113 
114 	event_set(&cs->cs_ev, cs->cs_fd, EV_READ,
115 	    control_accept, cs);
116 	event_add(&cs->cs_ev, NULL);
117 	evtimer_set(&cs->cs_evt, control_accept, cs);
118 
119 	return (0);
120 }
121 
122 void
control_cleanup(struct control_sock * cs)123 control_cleanup(struct control_sock *cs)
124 {
125 	if (cs->cs_name == NULL)
126 		return;
127 	event_del(&cs->cs_ev);
128 	event_del(&cs->cs_evt);
129 }
130 
131 /* ARGSUSED */
132 void
control_accept(int listenfd,short event,void * arg)133 control_accept(int listenfd, short event, void *arg)
134 {
135 	int			 connfd;
136 	socklen_t		 len;
137 	struct sockaddr_un	 sun;
138 	struct ctl_conn		*c;
139 	struct control_sock	*cs = arg;
140 
141 	event_add(&cs->cs_ev, NULL);
142 	if ((event & EV_TIMEOUT))
143 		return;
144 
145 	len = sizeof(sun);
146 	if ((connfd = accept4(listenfd,
147 	    (struct sockaddr *)&sun, &len, SOCK_NONBLOCK)) == -1) {
148 		/*
149 		 * Pause accept if we are out of file descriptors, or
150 		 * libevent will haunt us here too.
151 		 */
152 		if (errno == ENFILE || errno == EMFILE) {
153 			struct timeval evtpause = { 1, 0 };
154 
155 			event_del(&cs->cs_ev);
156 			evtimer_add(&cs->cs_evt, &evtpause);
157 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
158 		    errno != ECONNABORTED)
159 			log_warn("%s: accept", __func__);
160 		return;
161 	}
162 
163 	if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
164 		close(connfd);
165 		log_warn("%s: calloc", __func__);
166 		return;
167 	}
168 
169 	imsg_init(&c->iev.ibuf, connfd);
170 	c->iev.handler = control_dispatch_imsg;
171 	c->iev.events = EV_READ;
172 	c->iev.data = cs;	/* proc.c cheats (reuses the handler) */
173 	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
174 	    c->iev.handler, cs);
175 	event_add(&c->iev.ev, NULL);
176 
177 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
178 }
179 
180 struct ctl_conn *
control_connbyfd(int fd)181 control_connbyfd(int fd)
182 {
183 	struct ctl_conn	*c;
184 
185 	TAILQ_FOREACH(c, &ctl_conns, entry) {
186 		if (c->iev.ibuf.fd == fd)
187 			break;
188 	}
189 
190 	return (c);
191 }
192 
193 void
control_close(int fd,struct control_sock * cs)194 control_close(int fd, struct control_sock *cs)
195 {
196 	struct ctl_conn	*c;
197 
198 	if ((c = control_connbyfd(fd)) == NULL) {
199 		log_warn("%s: fd %d not found", __func__, fd);
200 		return;
201 	}
202 
203 	msgbuf_clear(&c->iev.ibuf.w);
204 	TAILQ_REMOVE(&ctl_conns, c, entry);
205 
206 	event_del(&c->iev.ev);
207 	close(c->iev.ibuf.fd);
208 
209 	/* Some file descriptors are available again. */
210 	if (evtimer_pending(&cs->cs_evt, NULL)) {
211 		evtimer_del(&cs->cs_evt);
212 		event_add(&cs->cs_ev, NULL);
213 	}
214 
215 	free(c);
216 }
217 
218 /* ARGSUSED */
219 void
control_dispatch_imsg(int fd,short event,void * arg)220 control_dispatch_imsg(int fd, short event, void *arg)
221 {
222 	struct control_sock	*cs = arg;
223 	struct ctl_conn		*c;
224 	struct imsg		 imsg;
225 	int			 n;
226 	int			 verbose;
227 	struct httpd		*env = cs->cs_env;
228 
229 	if ((c = control_connbyfd(fd)) == NULL) {
230 		log_warn("%s: fd %d not found", __func__, fd);
231 		return;
232 	}
233 
234 	if (event & EV_READ) {
235 		if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) ||
236 		    n == 0) {
237 			control_close(fd, cs);
238 			return;
239 		}
240 	}
241 
242 	if (event & EV_WRITE) {
243 		if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
244 			control_close(fd, cs);
245 			return;
246 		}
247 	}
248 
249 	for (;;) {
250 		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
251 			control_close(fd, cs);
252 			return;
253 		}
254 
255 		if (n == 0)
256 			break;
257 
258 		if (c->waiting) {
259 			log_debug("%s: unexpected imsg %d",
260 			    __func__, imsg.hdr.type);
261 			imsg_free(&imsg);
262 			control_close(fd, cs);
263 			return;
264 		}
265 
266 		switch (imsg.hdr.type) {
267 		case IMSG_CTL_SHUTDOWN:
268 		case IMSG_CTL_RELOAD:
269 		case IMSG_CTL_REOPEN:
270 			proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1);
271 			break;
272 		case IMSG_CTL_NOTIFY:
273 			if (c->flags & CTL_CONN_NOTIFY) {
274 				log_debug("%s: "
275 				    "client requested notify more than once",
276 				    __func__);
277 				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
278 				    0, env->sc_ps->ps_instance + 1, -1,
279 				    NULL, 0);
280 				break;
281 			}
282 			c->flags |= CTL_CONN_NOTIFY;
283 			break;
284 		case IMSG_CTL_VERBOSE:
285 			IMSG_SIZE_CHECK(&imsg, &verbose);
286 
287 			memcpy(&verbose, imsg.data, sizeof(verbose));
288 
289 			proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, -1);
290 			proc_forward_imsg(env->sc_ps, &imsg, PROC_SERVER, -1);
291 
292 			memcpy(imsg.data, &verbose, sizeof(verbose));
293 			control_imsg_forward(env->sc_ps, &imsg);
294 			log_setverbose(verbose);
295 			break;
296 		default:
297 			log_debug("%s: error handling imsg %d",
298 			    __func__, imsg.hdr.type);
299 			break;
300 		}
301 		imsg_free(&imsg);
302 	}
303 
304 	imsg_event_add(&c->iev);
305 }
306 
307 void
control_imsg_forward(struct privsep * ps,struct imsg * imsg)308 control_imsg_forward(struct privsep *ps, struct imsg *imsg)
309 {
310 	struct ctl_conn *c;
311 
312 	TAILQ_FOREACH(c, &ctl_conns, entry)
313 		if (c->flags & CTL_CONN_NOTIFY)
314 			imsg_compose_event(&c->iev, imsg->hdr.type,
315 			    0, ps->ps_instance + 1, -1, imsg->data,
316 			    imsg->hdr.len - IMSG_HEADER_SIZE);
317 }
318