xref: /minix/external/bsd/dhcpcd/dist/control.c (revision fb9c64b2)
1 #include <sys/cdefs.h>
2  __RCSID("$NetBSD: control.c,v 1.10 2015/08/21 10:39:00 roy Exp $");
3 
4 /*
5  * dhcpcd - DHCP client daemon
6  * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
7  * All rights reserved
8 
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 #include <sys/uio.h>
34 #include <sys/un.h>
35 
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 #include <unistd.h>
43 
44 #include "config.h"
45 #include "common.h"
46 #include "dhcpcd.h"
47 #include "control.h"
48 #include "eloop.h"
49 #include "if.h"
50 
51 #ifndef SUN_LEN
52 #define SUN_LEN(su) \
53             (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
54 #endif
55 
56 static void
57 control_queue_purge(struct dhcpcd_ctx *ctx, char *data)
58 {
59 	int found;
60 	struct fd_list *fp;
61 	struct fd_data *fpd;
62 
63 	/* If no other fd queue has the same data, free it */
64 	found = 0;
65 	TAILQ_FOREACH(fp, &ctx->control_fds, next) {
66 		TAILQ_FOREACH(fpd, &fp->queue, next) {
67 			if (fpd->data == data) {
68 				found = 1;
69 				break;
70 			}
71 		}
72 	}
73 	if (!found)
74 		free(data);
75 }
76 
77 static void
78 control_queue_free(struct fd_list *fd)
79 {
80 	struct fd_data *fdp;
81 
82 	while ((fdp = TAILQ_FIRST(&fd->queue))) {
83 		TAILQ_REMOVE(&fd->queue, fdp, next);
84 		if (fdp->freeit)
85 			control_queue_purge(fd->ctx, fdp->data);
86 		free(fdp);
87 	}
88 	while ((fdp = TAILQ_FIRST(&fd->free_queue))) {
89 		TAILQ_REMOVE(&fd->free_queue, fdp, next);
90 		free(fdp);
91 	}
92 }
93 
94 static void
95 control_delete(struct fd_list *fd)
96 {
97 
98 	TAILQ_REMOVE(&fd->ctx->control_fds, fd, next);
99 	eloop_event_delete(fd->ctx->eloop, fd->fd);
100 	close(fd->fd);
101 	control_queue_free(fd);
102 	free(fd);
103 }
104 
105 static void
106 control_handle_data(void *arg)
107 {
108 	struct fd_list *fd = arg;
109 	char buffer[1024], *e, *p, *argvp[255], **ap, *a;
110 	ssize_t bytes;
111 	size_t len;
112 	int argc;
113 
114 	bytes = read(fd->fd, buffer, sizeof(buffer) - 1);
115 	if (bytes == -1 || bytes == 0) {
116 		/* Control was closed or there was an error.
117 		 * Remove it from our list. */
118 		control_delete(fd);
119 		return;
120 	}
121 	buffer[bytes] = '\0';
122 	p = buffer;
123 	e = buffer + bytes;
124 
125 	/* Each command is \n terminated
126 	 * Each argument is NULL separated */
127 	while (p < e) {
128 		argc = 0;
129 		ap = argvp;
130 		while (p < e) {
131 			argc++;
132 			if ((size_t)argc >= sizeof(argvp) / sizeof(argvp[0])) {
133 				errno = ENOBUFS;
134 				return;
135 			}
136 			a = *ap++ = p;
137 			len = strlen(p);
138 			p += len + 1;
139 			if (len && a[len - 1] == '\n') {
140 				a[len - 1] = '\0';
141 				break;
142 			}
143 		}
144 		*ap = NULL;
145 		if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) {
146 			logger(fd->ctx, LOG_ERR,
147 			    "%s: dhcpcd_handleargs: %m", __func__);
148 			if (errno != EINTR && errno != EAGAIN) {
149 				control_delete(fd);
150 				return;
151 			}
152 		}
153 	}
154 }
155 
156 static void
157 control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags)
158 {
159 	struct sockaddr_un run;
160 	socklen_t len;
161 	struct fd_list *l;
162 	int fd, flags;
163 
164 	len = sizeof(run);
165 	if ((fd = accept(lfd, (struct sockaddr *)&run, &len)) == -1)
166 		return;
167 	if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
168 	    fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
169 	{
170 		close(fd);
171 	        return;
172 	}
173 	if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
174 	    fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
175 	{
176 		close(fd);
177 	        return;
178 	}
179 	l = malloc(sizeof(*l));
180 	if (l) {
181 		l->ctx = ctx;
182 		l->fd = fd;
183 		l->flags = fd_flags;
184 		TAILQ_INIT(&l->queue);
185 		TAILQ_INIT(&l->free_queue);
186 		TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
187 		eloop_event_add(ctx->eloop, l->fd,
188 		    control_handle_data, l, NULL, NULL);
189 	} else
190 		close(fd);
191 }
192 
193 static void
194 control_handle(void *arg)
195 {
196 	struct dhcpcd_ctx *ctx = arg;
197 
198 	control_handle1(ctx, ctx->control_fd, 0);
199 }
200 
201 static void
202 control_handle_unpriv(void *arg)
203 {
204 	struct dhcpcd_ctx *ctx = arg;
205 
206 	control_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV);
207 }
208 
209 static int
210 make_sock(struct sockaddr_un *sa, const char *ifname, int unpriv)
211 {
212 	int fd;
213 
214 	if ((fd = xsocket(AF_UNIX, SOCK_STREAM, 0, O_NONBLOCK|O_CLOEXEC)) == -1)
215 		return -1;
216 	memset(sa, 0, sizeof(*sa));
217 	sa->sun_family = AF_UNIX;
218 	if (unpriv)
219 		strlcpy(sa->sun_path, UNPRIVSOCKET, sizeof(sa->sun_path));
220 	else {
221 		snprintf(sa->sun_path, sizeof(sa->sun_path), CONTROLSOCKET,
222 		    ifname ? "-" : "", ifname ? ifname : "");
223 	}
224 	return fd;
225 }
226 
227 #define S_PRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
228 #define S_UNPRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
229 
230 static int
231 control_start1(struct dhcpcd_ctx *ctx, const char *ifname, mode_t fmode)
232 {
233 	struct sockaddr_un sa;
234 	int fd;
235 	socklen_t len;
236 
237 	if ((fd = make_sock(&sa, ifname, (fmode & S_UNPRIV) == S_UNPRIV)) == -1)
238 		return -1;
239 	len = (socklen_t)SUN_LEN(&sa);
240 	unlink(sa.sun_path);
241 	if (bind(fd, (struct sockaddr *)&sa, len) == -1 ||
242 	    chmod(sa.sun_path, fmode) == -1 ||
243 	    (ctx->control_group &&
244 	    chown(sa.sun_path, geteuid(), ctx->control_group) == -1) ||
245 	    listen(fd, sizeof(ctx->control_fds)) == -1)
246 	{
247 		close(fd);
248 		unlink(sa.sun_path);
249 		return -1;
250 	}
251 
252 	if ((fmode & S_UNPRIV) != S_UNPRIV)
253 		strlcpy(ctx->control_sock, sa.sun_path,
254 		    sizeof(ctx->control_sock));
255 	return fd;
256 }
257 
258 int
259 control_start(struct dhcpcd_ctx *ctx, const char *ifname)
260 {
261 	int fd;
262 
263 	if ((fd = control_start1(ctx, ifname, S_PRIV)) == -1)
264 		return -1;
265 
266 	ctx->control_fd = fd;
267 	eloop_event_add(ctx->eloop, fd, control_handle, ctx, NULL, NULL);
268 
269 	if (ifname == NULL && (fd = control_start1(ctx, NULL, S_UNPRIV)) != -1){
270 		/* We must be in master mode, so create an unpriviledged socket
271 		 * to allow normal users to learn the status of dhcpcd. */
272 		ctx->control_unpriv_fd = fd;
273 		eloop_event_add(ctx->eloop, fd, control_handle_unpriv,
274 		    ctx, NULL, NULL);
275 	}
276 	return ctx->control_fd;
277 }
278 
279 int
280 control_stop(struct dhcpcd_ctx *ctx)
281 {
282 	int retval = 0;
283 	struct fd_list *l;
284 
285 	if (ctx->options & DHCPCD_FORKED)
286 		goto freeit;
287 
288 	if (ctx->control_fd == -1)
289 		return 0;
290 	eloop_event_delete(ctx->eloop, ctx->control_fd);
291 	close(ctx->control_fd);
292 	ctx->control_fd = -1;
293 	if (unlink(ctx->control_sock) == -1)
294 		retval = -1;
295 
296 	if (ctx->control_unpriv_fd != -1) {
297 		eloop_event_delete(ctx->eloop, ctx->control_unpriv_fd);
298 		close(ctx->control_unpriv_fd);
299 		ctx->control_unpriv_fd = -1;
300 		if (unlink(UNPRIVSOCKET) == -1)
301 			retval = -1;
302 	}
303 
304 freeit:
305 	while ((l = TAILQ_FIRST(&ctx->control_fds))) {
306 		TAILQ_REMOVE(&ctx->control_fds, l, next);
307 		eloop_event_delete(ctx->eloop, l->fd);
308 		close(l->fd);
309 		control_queue_free(l);
310 		free(l);
311 	}
312 
313 	return retval;
314 }
315 
316 int
317 control_open(struct dhcpcd_ctx *ctx, const char *ifname)
318 {
319 	struct sockaddr_un sa;
320 	socklen_t len;
321 
322 	if ((ctx->control_fd = make_sock(&sa, ifname, 0)) == -1)
323 		return -1;
324 	len = (socklen_t)SUN_LEN(&sa);
325 	if (connect(ctx->control_fd, (struct sockaddr *)&sa, len) == -1) {
326 		close(ctx->control_fd);
327 		ctx->control_fd = -1;
328 		return -1;
329 	}
330 	return 0;
331 }
332 
333 ssize_t
334 control_send(struct dhcpcd_ctx *ctx, int argc, char * const *argv)
335 {
336 	char buffer[1024];
337 	int i;
338 	size_t len, l;
339 
340 	if (argc > 255) {
341 		errno = ENOBUFS;
342 		return -1;
343 	}
344 	len = 0;
345 	for (i = 0; i < argc; i++) {
346 		l = strlen(argv[i]) + 1;
347 		if (len + l > sizeof(buffer)) {
348 			errno = ENOBUFS;
349 			return -1;
350 		}
351 		memcpy(buffer + len, argv[i], l);
352 		len += l;
353 	}
354 	return write(ctx->control_fd, buffer, len);
355 }
356 
357 static void
358 control_writeone(void *arg)
359 {
360 	struct fd_list *fd;
361 	struct iovec iov[2];
362 	struct fd_data *data;
363 
364 	fd = arg;
365 	data = TAILQ_FIRST(&fd->queue);
366 	iov[0].iov_base = &data->data_len;
367 	iov[0].iov_len = sizeof(size_t);
368 	iov[1].iov_base = data->data;
369 	iov[1].iov_len = data->data_len;
370 	if (writev(fd->fd, iov, 2) == -1) {
371 		logger(fd->ctx, LOG_ERR,
372 		    "%s: writev fd %d: %m", __func__, fd->fd);
373 		if (errno != EINTR && errno != EAGAIN)
374 			control_delete(fd);
375 		return;
376 	}
377 
378 	TAILQ_REMOVE(&fd->queue, data, next);
379 	if (data->freeit)
380 		control_queue_purge(fd->ctx, data->data);
381 	data->data = NULL; /* safety */
382 	data->data_len = 0;
383 	TAILQ_INSERT_TAIL(&fd->free_queue, data, next);
384 
385 	if (TAILQ_FIRST(&fd->queue) == NULL)
386 		eloop_event_remove_writecb(fd->ctx->eloop, fd->fd);
387 }
388 
389 int
390 control_queue(struct fd_list *fd, char *data, size_t data_len, uint8_t fit)
391 {
392 	struct fd_data *d;
393 	size_t n;
394 
395 	d = TAILQ_FIRST(&fd->free_queue);
396 	if (d) {
397 		TAILQ_REMOVE(&fd->free_queue, d, next);
398 	} else {
399 		n = 0;
400 		TAILQ_FOREACH(d, &fd->queue, next) {
401 			if (++n == CONTROL_QUEUE_MAX) {
402 				errno = ENOBUFS;
403 				return -1;
404 			}
405 		}
406 		d = malloc(sizeof(*d));
407 		if (d == NULL)
408 			return -1;
409 	}
410 	d->data = data;
411 	d->data_len = data_len;
412 	d->freeit = fit;
413 	TAILQ_INSERT_TAIL(&fd->queue, d, next);
414 	eloop_event_add(fd->ctx->eloop, fd->fd,
415 	    NULL, NULL, control_writeone, fd);
416 	return 0;
417 }
418 
419 void
420 control_close(struct dhcpcd_ctx *ctx)
421 {
422 
423 	if (ctx->control_fd != -1) {
424 		close(ctx->control_fd);
425 		ctx->control_fd = -1;
426 	}
427 }
428