• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * dhcpcd - DHCP client daemon
3  * Copyright (c) 2006-2015 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 
47 #ifndef SUN_LEN
48 #define SUN_LEN(su) \
49             (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
50 #endif
51 
52 static void
control_queue_purge(struct dhcpcd_ctx * ctx,char * data)53 control_queue_purge(struct dhcpcd_ctx *ctx, char *data)
54 {
55 	int found;
56 	struct fd_list *fp;
57 	struct fd_data *fpd;
58 
59 	/* If no other fd queue has the same data, free it */
60 	found = 0;
61 	TAILQ_FOREACH(fp, &ctx->control_fds, next) {
62 		TAILQ_FOREACH(fpd, &fp->queue, next) {
63 			if (fpd->data == data) {
64 				found = 1;
65 				break;
66 			}
67 		}
68 	}
69 	if (!found)
70 		free(data);
71 }
72 
73 static void
control_queue_free(struct fd_list * fd)74 control_queue_free(struct fd_list *fd)
75 {
76 	struct fd_data *fdp;
77 
78 	while ((fdp = TAILQ_FIRST(&fd->queue))) {
79 		TAILQ_REMOVE(&fd->queue, fdp, next);
80 		if (fdp->freeit)
81 			control_queue_purge(fd->ctx, fdp->data);
82 		free(fdp);
83 	}
84 	while ((fdp = TAILQ_FIRST(&fd->free_queue))) {
85 		TAILQ_REMOVE(&fd->free_queue, fdp, next);
86 		free(fdp);
87 	}
88 }
89 
90 static void
control_delete(struct fd_list * fd)91 control_delete(struct fd_list *fd)
92 {
93 
94 	TAILQ_REMOVE(&fd->ctx->control_fds, fd, next);
95 	eloop_event_delete(fd->ctx->eloop, fd->fd, 0);
96 	close(fd->fd);
97 	control_queue_free(fd);
98 	free(fd);
99 }
100 
101 static void
control_handle_data(void * arg)102 control_handle_data(void *arg)
103 {
104 	struct fd_list *fd = arg;
105 	char buffer[1024], *e, *p, *argvp[255], **ap, *a;
106 	ssize_t bytes;
107 	size_t len;
108 	int argc;
109 
110 	bytes = read(fd->fd, buffer, sizeof(buffer) - 1);
111 	if (bytes == -1 || bytes == 0) {
112 		/* Control was closed or there was an error.
113 		 * Remove it from our list. */
114 		control_delete(fd);
115 		return;
116 	}
117 	buffer[bytes] = '\0';
118 	p = buffer;
119 	e = buffer + bytes;
120 
121 	/* Each command is \n terminated
122 	 * Each argument is NULL separated */
123 	while (p < e) {
124 		argc = 0;
125 		ap = argvp;
126 		while (p < e) {
127 			argc++;
128 			if ((size_t)argc >= sizeof(argvp) / sizeof(argvp[0])) {
129 				errno = ENOBUFS;
130 				return;
131 			}
132 			a = *ap++ = p;
133 			len = strlen(p);
134 			p += len + 1;
135 			if (len && a[len - 1] == '\n') {
136 				a[len - 1] = '\0';
137 				break;
138 			}
139 		}
140 		*ap = NULL;
141 		if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) {
142 			logger(fd->ctx, LOG_ERR,
143 			    "%s: dhcpcd_handleargs: %m", __func__);
144 			if (errno != EINTR && errno != EAGAIN) {
145 				control_delete(fd);
146 				return;
147 			}
148 		}
149 	}
150 }
151 
152 static void
control_handle1(struct dhcpcd_ctx * ctx,int lfd,unsigned int fd_flags)153 control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags)
154 {
155 	struct sockaddr_un run;
156 	socklen_t len;
157 	struct fd_list *l;
158 	int fd, flags;
159 
160 	len = sizeof(run);
161 	if ((fd = accept(lfd, (struct sockaddr *)&run, &len)) == -1)
162 		return;
163 	if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
164 	    fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
165 	{
166 		close(fd);
167 	        return;
168 	}
169 	if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
170 	    fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
171 	{
172 		close(fd);
173 	        return;
174 	}
175 	l = malloc(sizeof(*l));
176 	if (l) {
177 		l->ctx = ctx;
178 		l->fd = fd;
179 		l->flags = fd_flags;
180 		TAILQ_INIT(&l->queue);
181 		TAILQ_INIT(&l->free_queue);
182 		TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
183 		eloop_event_add(ctx->eloop, l->fd,
184 		    control_handle_data, l, NULL, NULL);
185 	} else
186 		close(fd);
187 }
188 
189 static void
control_handle(void * arg)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
control_handle_unpriv(void * arg)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
make_sock(struct sockaddr_un * sa,const char * ifname,int unpriv)206 make_sock(struct sockaddr_un *sa, const char *ifname, int unpriv)
207 {
208 	int fd;
209 
210 #ifdef SOCK_CLOEXEC
211 	if ((fd = socket(AF_UNIX,
212 	    SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1)
213 		return -1;
214 #else
215 	int flags;
216 
217 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
218 		return -1;
219 	if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
220 	    fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
221 	{
222 		close(fd);
223 	        return -1;
224 	}
225 	if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
226 	    fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
227 	{
228 		close(fd);
229 	        return -1;
230 	}
231 #endif
232 	memset(sa, 0, sizeof(*sa));
233 	sa->sun_family = AF_UNIX;
234 	if (unpriv)
235 		strlcpy(sa->sun_path, UNPRIVSOCKET, sizeof(sa->sun_path));
236 	else {
237 		snprintf(sa->sun_path, sizeof(sa->sun_path), CONTROLSOCKET,
238 		    ifname ? "-" : "", ifname ? ifname : "");
239 	}
240 	return fd;
241 }
242 
243 #define S_PRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
244 #define S_UNPRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
245 
246 static int
control_start1(struct dhcpcd_ctx * ctx,const char * ifname,mode_t fmode)247 control_start1(struct dhcpcd_ctx *ctx, const char *ifname, mode_t fmode)
248 {
249 	struct sockaddr_un sa;
250 	int fd;
251 	socklen_t len;
252 
253 	if ((fd = make_sock(&sa, ifname, (fmode & S_UNPRIV) == S_UNPRIV)) == -1)
254 		return -1;
255 	len = (socklen_t)SUN_LEN(&sa);
256 	unlink(sa.sun_path);
257 	if (bind(fd, (struct sockaddr *)&sa, len) == -1 ||
258 	    chmod(sa.sun_path, fmode) == -1 ||
259 	    (ctx->control_group &&
260 	    chown(sa.sun_path, geteuid(), ctx->control_group) == -1) ||
261 	    listen(fd, sizeof(ctx->control_fds)) == -1)
262 	{
263 		close(fd);
264 		unlink(sa.sun_path);
265 		return -1;
266 	}
267 
268 	if ((fmode & S_UNPRIV) != S_UNPRIV)
269 		strlcpy(ctx->control_sock, sa.sun_path,
270 		    sizeof(ctx->control_sock));
271 	return fd;
272 }
273 
274 int
control_start(struct dhcpcd_ctx * ctx,const char * ifname)275 control_start(struct dhcpcd_ctx *ctx, const char *ifname)
276 {
277 	int fd;
278 
279 	if ((fd = control_start1(ctx, ifname, S_PRIV)) == -1)
280 		return -1;
281 
282 	ctx->control_fd = fd;
283 	eloop_event_add(ctx->eloop, fd, control_handle, ctx, NULL, NULL);
284 
285 	if (ifname == NULL && (fd = control_start1(ctx, NULL, S_UNPRIV)) != -1){
286 		/* We must be in master mode, so create an unpriviledged socket
287 		 * to allow normal users to learn the status of dhcpcd. */
288 		ctx->control_unpriv_fd = fd;
289 		eloop_event_add(ctx->eloop, fd, control_handle_unpriv,
290 		    ctx, NULL, NULL);
291 	}
292 	return ctx->control_fd;
293 }
294 
295 int
control_stop(struct dhcpcd_ctx * ctx)296 control_stop(struct dhcpcd_ctx *ctx)
297 {
298 	int retval = 0;
299 	struct fd_list *l;
300 
301 	if (ctx->options & DHCPCD_FORKED)
302 		goto freeit;
303 
304 	if (ctx->control_fd == -1)
305 		return 0;
306 	eloop_event_delete(ctx->eloop, ctx->control_fd, 0);
307 	close(ctx->control_fd);
308 	ctx->control_fd = -1;
309 	if (unlink(ctx->control_sock) == -1)
310 		retval = -1;
311 
312 	if (ctx->control_unpriv_fd != -1) {
313 		eloop_event_delete(ctx->eloop, ctx->control_unpriv_fd, 0);
314 		close(ctx->control_unpriv_fd);
315 		ctx->control_unpriv_fd = -1;
316 		if (unlink(UNPRIVSOCKET) == -1)
317 			retval = -1;
318 	}
319 
320 freeit:
321 	while ((l = TAILQ_FIRST(&ctx->control_fds))) {
322 		TAILQ_REMOVE(&ctx->control_fds, l, next);
323 		eloop_event_delete(ctx->eloop, l->fd, 0);
324 		close(l->fd);
325 		control_queue_free(l);
326 		free(l);
327 	}
328 
329 	return retval;
330 }
331 
332 int
control_open(struct dhcpcd_ctx * ctx,const char * ifname)333 control_open(struct dhcpcd_ctx *ctx, const char *ifname)
334 {
335 	struct sockaddr_un sa;
336 	socklen_t len;
337 
338 	if ((ctx->control_fd = make_sock(&sa, ifname, 0)) == -1)
339 		return -1;
340 	len = (socklen_t)SUN_LEN(&sa);
341 	if (connect(ctx->control_fd, (struct sockaddr *)&sa, len) == -1) {
342 		close(ctx->control_fd);
343 		ctx->control_fd = -1;
344 		return -1;
345 	}
346 	return 0;
347 }
348 
349 ssize_t
control_send(struct dhcpcd_ctx * ctx,int argc,char * const * argv)350 control_send(struct dhcpcd_ctx *ctx, int argc, char * const *argv)
351 {
352 	char buffer[1024];
353 	int i;
354 	size_t len, l;
355 
356 	if (argc > 255) {
357 		errno = ENOBUFS;
358 		return -1;
359 	}
360 	len = 0;
361 	for (i = 0; i < argc; i++) {
362 		l = strlen(argv[i]) + 1;
363 		if (len + l > sizeof(buffer)) {
364 			errno = ENOBUFS;
365 			return -1;
366 		}
367 		memcpy(buffer + len, argv[i], l);
368 		len += l;
369 	}
370 	return write(ctx->control_fd, buffer, len);
371 }
372 
373 static void
control_writeone(void * arg)374 control_writeone(void *arg)
375 {
376 	struct fd_list *fd;
377 	struct iovec iov[2];
378 	struct fd_data *data;
379 
380 	fd = arg;
381 	data = TAILQ_FIRST(&fd->queue);
382 	iov[0].iov_base = &data->data_len;
383 	iov[0].iov_len = sizeof(size_t);
384 	iov[1].iov_base = data->data;
385 	iov[1].iov_len = data->data_len;
386 	if (writev(fd->fd, iov, 2) == -1) {
387 		logger(fd->ctx, LOG_ERR,
388 		    "%s: writev fd %d: %m", __func__, fd->fd);
389 		if (errno != EINTR && errno != EAGAIN)
390 			control_delete(fd);
391 		return;
392 	}
393 
394 	TAILQ_REMOVE(&fd->queue, data, next);
395 	if (data->freeit)
396 		control_queue_purge(fd->ctx, data->data);
397 	data->data = NULL; /* safety */
398 	data->data_len = 0;
399 	TAILQ_INSERT_TAIL(&fd->free_queue, data, next);
400 
401 	if (TAILQ_FIRST(&fd->queue) == NULL)
402 		eloop_event_delete(fd->ctx->eloop, fd->fd, 1);
403 }
404 
405 int
control_queue(struct fd_list * fd,char * data,size_t data_len,uint8_t fit)406 control_queue(struct fd_list *fd, char *data, size_t data_len, uint8_t fit)
407 {
408 	struct fd_data *d;
409 	size_t n;
410 
411 	d = TAILQ_FIRST(&fd->free_queue);
412 	if (d) {
413 		TAILQ_REMOVE(&fd->free_queue, d, next);
414 	} else {
415 		n = 0;
416 		TAILQ_FOREACH(d, &fd->queue, next) {
417 			if (++n == CONTROL_QUEUE_MAX) {
418 				errno = ENOBUFS;
419 				return -1;
420 			}
421 		}
422 		d = malloc(sizeof(*d));
423 		if (d == NULL)
424 			return -1;
425 	}
426 	d->data = data;
427 	d->data_len = data_len;
428 	d->freeit = fit;
429 	TAILQ_INSERT_TAIL(&fd->queue, d, next);
430 	eloop_event_add(fd->ctx->eloop, fd->fd,
431 	    NULL, NULL, control_writeone, fd);
432 	return 0;
433 }
434 
435 void
control_close(struct dhcpcd_ctx * ctx)436 control_close(struct dhcpcd_ctx *ctx)
437 {
438 
439 	if (ctx->control_fd != -1) {
440 		close(ctx->control_fd);
441 		ctx->control_fd = -1;
442 	}
443 }
444