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