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