Home | History | Annotate | Download | only in dhcpcd-6.8.2
      1 /*
      2  * dhcpcd - DHCP client daemon
      3  * Copyright (c) 2006-2015 Roy Marples <roy (at) 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
     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
     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
     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
    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
    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
    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 #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
    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
    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
    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
    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
    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
    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
    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
    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