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/stat.h> 29 #include <sys/uio.h> 30 #include <sys/wait.h> 31 32 #include <netinet/in.h> 33 #include <arpa/inet.h> 34 35 #include <ctype.h> 36 #include <errno.h> 37 #include <signal.h> 38 /* We can't include spawn.h here because it may not exist. 39 * config.h will pull it in, or our compat one. */ 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 44 #include "config.h" 45 #include "common.h" 46 #include "dhcp.h" 47 #include "dhcp6.h" 48 #include "if.h" 49 #include "if-options.h" 50 #include "ipv6nd.h" 51 #include "script.h" 52 53 #ifdef HAVE_SPAWN_H 54 #include <spawn.h> 55 #else 56 #include "compat/posix_spawn.h" 57 #endif 58 59 /* Allow the OS to define another script env var name */ 60 #ifndef RC_SVCNAME 61 #define RC_SVCNAME "RC_SVCNAME" 62 #endif 63 64 #define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" 65 66 static const char * const if_params[] = { 67 "interface", 68 "reason", 69 "pid", 70 "ifcarrier", 71 "ifmetric", 72 "ifwireless", 73 "ifflags", 74 "ssid", 75 "profile", 76 "interface_order", 77 NULL 78 }; 79 80 void 81 if_printoptions(void) 82 { 83 const char * const *p; 84 85 for (p = if_params; *p; p++) 86 printf(" - %s\n", *p); 87 } 88 89 static int 90 exec_script(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env) 91 { 92 pid_t pid; 93 posix_spawnattr_t attr; 94 int i; 95 #ifdef USE_SIGNALS 96 short flags; 97 sigset_t defsigs; 98 #else 99 UNUSED(ctx); 100 #endif 101 102 /* posix_spawn is a safe way of executing another image 103 * and changing signals back to how they should be. */ 104 if (posix_spawnattr_init(&attr) == -1) 105 return -1; 106 #ifdef USE_SIGNALS 107 flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF; 108 posix_spawnattr_setflags(&attr, flags); 109 sigemptyset(&defsigs); 110 for (i = 0; dhcpcd_handlesigs[i]; i++) 111 sigaddset(&defsigs, dhcpcd_handlesigs[i]); 112 posix_spawnattr_setsigdefault(&attr, &defsigs); 113 posix_spawnattr_setsigmask(&attr, &ctx->sigset); 114 #endif 115 errno = 0; 116 i = posix_spawn(&pid, argv[0], NULL, &attr, argv, env); 117 if (i) { 118 errno = i; 119 return -1; 120 } 121 return pid; 122 } 123 124 #ifdef INET 125 static char * 126 make_var(struct dhcpcd_ctx *ctx, const char *prefix, const char *var) 127 { 128 size_t len; 129 char *v; 130 131 len = strlen(prefix) + strlen(var) + 2; 132 v = malloc(len); 133 if (v == NULL) { 134 logger(ctx, LOG_ERR, "%s: %m", __func__); 135 return NULL; 136 } 137 snprintf(v, len, "%s_%s", prefix, var); 138 return v; 139 } 140 141 142 static int 143 append_config(struct dhcpcd_ctx *ctx, char ***env, size_t *len, 144 const char *prefix, const char *const *config) 145 { 146 size_t i, j, e1; 147 char **ne, *eq, **nep, *p; 148 int ret; 149 150 if (config == NULL) 151 return 0; 152 153 ne = *env; 154 ret = 0; 155 for (i = 0; config[i] != NULL; i++) { 156 eq = strchr(config[i], '='); 157 e1 = (size_t)(eq - config[i] + 1); 158 for (j = 0; j < *len; j++) { 159 if (strncmp(ne[j] + strlen(prefix) + 1, 160 config[i], e1) == 0) 161 { 162 p = make_var(ctx, prefix, config[i]); 163 if (p == NULL) { 164 ret = -1; 165 break; 166 } 167 free(ne[j]); 168 ne[j] = p; 169 break; 170 } 171 } 172 if (j == *len) { 173 j++; 174 p = make_var(ctx, prefix, config[i]); 175 if (p == NULL) { 176 ret = -1; 177 break; 178 } 179 nep = realloc(ne, sizeof(char *) * (j + 1)); 180 if (nep == NULL) { 181 logger(ctx, LOG_ERR, "%s: %m", __func__); 182 free(p); 183 ret = -1; 184 break; 185 } 186 ne = nep; 187 ne[j - 1] = p; 188 *len = j; 189 } 190 } 191 *env = ne; 192 return ret; 193 } 194 #endif 195 196 static ssize_t 197 arraytostr(const char *const *argv, char **s) 198 { 199 const char *const *ap; 200 char *p; 201 size_t len, l; 202 203 if (*argv == NULL) 204 return 0; 205 len = 0; 206 ap = argv; 207 while (*ap) 208 len += strlen(*ap++) + 1; 209 *s = p = malloc(len); 210 if (p == NULL) 211 return -1; 212 ap = argv; 213 while (*ap) { 214 l = strlen(*ap) + 1; 215 memcpy(p, *ap, l); 216 p += l; 217 ap++; 218 } 219 return (ssize_t)len; 220 } 221 222 static ssize_t 223 make_env(const struct interface *ifp, const char *reason, char ***argv) 224 { 225 char **env, **nenv, *p; 226 size_t e, elen, l; 227 #if defined(INET) || defined(INET6) 228 ssize_t n; 229 #endif 230 const struct if_options *ifo = ifp->options; 231 const struct interface *ifp2; 232 #ifdef INET 233 int dhcp; 234 const struct dhcp_state *state; 235 #endif 236 #ifdef INET6 237 const struct dhcp6_state *d6_state; 238 int dhcp6, ra; 239 #endif 240 241 #ifdef INET 242 dhcp = 0; 243 state = D_STATE(ifp); 244 #endif 245 #ifdef INET6 246 dhcp6 = ra = 0; 247 d6_state = D6_CSTATE(ifp); 248 #endif 249 if (strcmp(reason, "TEST") == 0) { 250 if (1 == 2) {} 251 #ifdef INET6 252 else if (d6_state && d6_state->new) 253 dhcp6 = 1; 254 else if (ipv6nd_hasra(ifp)) 255 ra = 1; 256 #endif 257 #ifdef INET 258 else 259 dhcp = 1; 260 #endif 261 } 262 #ifdef INET6 263 else if (reason[strlen(reason) - 1] == '6') 264 dhcp6 = 1; 265 else if (strcmp(reason, "ROUTERADVERT") == 0) 266 ra = 1; 267 #endif 268 else if (strcmp(reason, "PREINIT") == 0 || 269 strcmp(reason, "CARRIER") == 0 || 270 strcmp(reason, "NOCARRIER") == 0 || 271 strcmp(reason, "UNKNOWN") == 0 || 272 strcmp(reason, "DEPARTED") == 0 || 273 strcmp(reason, "STOPPED") == 0) 274 { 275 /* This space left intentionally blank */ 276 } 277 #ifdef INET 278 else 279 dhcp = 1; 280 #endif 281 282 /* When dumping the lease, we only want to report interface and 283 reason - the other interface variables are meaningless */ 284 if (ifp->ctx->options & DHCPCD_DUMPLEASE) 285 elen = 2; 286 else 287 elen = 13; 288 289 #define EMALLOC(i, l) if ((env[(i)] = malloc((l))) == NULL) goto eexit; 290 /* Make our env + space for profile, wireless and debug */ 291 env = calloc(1, sizeof(char *) * (elen + 3 + 1)); 292 if (env == NULL) 293 goto eexit; 294 e = strlen("interface") + strlen(ifp->name) + 2; 295 EMALLOC(0, e); 296 snprintf(env[0], e, "interface=%s", ifp->name); 297 e = strlen("reason") + strlen(reason) + 2; 298 EMALLOC(1, e); 299 snprintf(env[1], e, "reason=%s", reason); 300 if (ifp->ctx->options & DHCPCD_DUMPLEASE) 301 goto dumplease; 302 e = 20; 303 EMALLOC(2, e); 304 snprintf(env[2], e, "pid=%d", getpid()); 305 EMALLOC(3, e); 306 snprintf(env[3], e, "ifcarrier=%s", 307 ifp->carrier == LINK_UNKNOWN ? "unknown" : 308 ifp->carrier == LINK_UP ? "up" : "down"); 309 EMALLOC(4, e); 310 snprintf(env[4], e, "ifmetric=%d", ifp->metric); 311 EMALLOC(5, e); 312 snprintf(env[5], e, "ifwireless=%d", ifp->wireless); 313 EMALLOC(6, e); 314 snprintf(env[6], e, "ifflags=%u", ifp->flags); 315 EMALLOC(7, e); 316 snprintf(env[7], e, "ifmtu=%d", if_getmtu(ifp->name)); 317 l = e = strlen("interface_order="); 318 TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 319 if (!(ifp2->options->options & DHCPCD_PFXDLGONLY)) 320 e += strlen(ifp2->name) + 1; 321 } 322 EMALLOC(8, e); 323 p = env[8]; 324 strlcpy(p, "interface_order=", e); 325 e -= l; 326 p += l; 327 TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 328 if (!(ifp2->options->options & DHCPCD_PFXDLGONLY)) { 329 l = strlcpy(p, ifp2->name, e); 330 p += l; 331 e -= l; 332 *p++ = ' '; 333 e--; 334 } 335 } 336 *--p = '\0'; 337 if (strcmp(reason, "STOPPED") == 0) { 338 env[9] = strdup("if_up=false"); 339 if (ifo->options & DHCPCD_RELEASE) 340 env[10] = strdup("if_down=true"); 341 else 342 env[10] = strdup("if_down=false"); 343 } else if (strcmp(reason, "TEST") == 0 || 344 strcmp(reason, "PREINIT") == 0 || 345 strcmp(reason, "CARRIER") == 0 || 346 strcmp(reason, "UNKNOWN") == 0) 347 { 348 env[9] = strdup("if_up=false"); 349 env[10] = strdup("if_down=false"); 350 } else if (1 == 2 /* appease ifdefs */ 351 #ifdef INET 352 || (dhcp && state && state->new) 353 #endif 354 #ifdef INET6 355 || (dhcp6 && d6_state && d6_state->new) 356 || (ra && ipv6nd_hasra(ifp)) 357 #endif 358 ) 359 { 360 env[9] = strdup("if_up=true"); 361 env[10] = strdup("if_down=false"); 362 } else { 363 env[9] = strdup("if_up=false"); 364 env[10] = strdup("if_down=true"); 365 } 366 if (env[9] == NULL || env[10] == NULL) 367 goto eexit; 368 if (dhcpcd_oneup(ifp->ctx)) 369 env[11] = strdup("if_oneup=true"); 370 else 371 env[11] = strdup("if_oneup=false"); 372 if (env[11] == NULL) 373 goto eexit; 374 if (dhcpcd_ipwaited(ifp->ctx)) 375 env[12] = strdup("if_ipwaited=true"); 376 else 377 env[12] = strdup("if_ipwaited=false"); 378 if (env[12] == NULL) 379 goto eexit; 380 if (ifo->options & DHCPCD_DEBUG) { 381 e = strlen("syslog_debug=true") + 1; 382 EMALLOC(elen, e); 383 snprintf(env[elen++], e, "syslog_debug=true"); 384 } 385 if (*ifp->profile) { 386 e = strlen("profile=") + strlen(ifp->profile) + 1; 387 EMALLOC(elen, e); 388 snprintf(env[elen++], e, "profile=%s", ifp->profile); 389 } 390 if (ifp->wireless) { 391 static const char *pfx = "ifssid="; 392 size_t pfx_len; 393 ssize_t psl; 394 395 pfx_len = strlen(pfx); 396 psl = print_string(NULL, 0, ESCSTRING, 397 (const uint8_t *)ifp->ssid, ifp->ssid_len); 398 if (psl != -1) { 399 EMALLOC(elen, pfx_len + (size_t)psl + 1); 400 memcpy(env[elen], pfx, pfx_len); 401 print_string(env[elen] + pfx_len, (size_t)psl + 1, 402 ESCSTRING, 403 (const uint8_t *)ifp->ssid, ifp->ssid_len); 404 elen++; 405 } 406 } 407 #ifdef INET 408 if (dhcp && state && state->old) { 409 n = dhcp_env(NULL, NULL, state->old, ifp); 410 if (n == -1) 411 goto eexit; 412 if (n > 0) { 413 nenv = realloc(env, sizeof(char *) * 414 (elen + (size_t)n + 1)); 415 if (nenv == NULL) 416 goto eexit; 417 env = nenv; 418 n = dhcp_env(env + elen, "old", state->old, ifp); 419 if (n == -1) 420 goto eexit; 421 elen += (size_t)n; 422 } 423 if (append_config(ifp->ctx, &env, &elen, "old", 424 (const char *const *)ifo->config) == -1) 425 goto eexit; 426 } 427 #endif 428 #ifdef INET6 429 if (dhcp6 && d6_state && ifo->options & DHCPCD_PFXDLGONLY) { 430 nenv = realloc(env, sizeof(char *) * (elen + 2)); 431 if (nenv == NULL) 432 goto eexit; 433 env = nenv; 434 env[elen] = strdup("ifclass=pd"); 435 if (env[elen] == NULL) 436 goto eexit; 437 elen++; 438 } 439 if (dhcp6 && d6_state && d6_state->old) { 440 n = dhcp6_env(NULL, NULL, ifp, 441 d6_state->old, d6_state->old_len); 442 if (n > 0) { 443 nenv = realloc(env, sizeof(char *) * 444 (elen + (size_t)n + 1)); 445 if (nenv == NULL) 446 goto eexit; 447 env = nenv; 448 n = dhcp6_env(env + elen, "old", ifp, 449 d6_state->old, d6_state->old_len); 450 if (n == -1) 451 goto eexit; 452 elen += (size_t)n; 453 } 454 } 455 #endif 456 457 dumplease: 458 #ifdef INET 459 if (dhcp && state && state->new) { 460 n = dhcp_env(NULL, NULL, state->new, ifp); 461 if (n > 0) { 462 nenv = realloc(env, sizeof(char *) * 463 (elen + (size_t)n + 1)); 464 if (nenv == NULL) 465 goto eexit; 466 env = nenv; 467 n = dhcp_env(env + elen, "new", 468 state->new, ifp); 469 if (n == -1) 470 goto eexit; 471 elen += (size_t)n; 472 } 473 if (append_config(ifp->ctx, &env, &elen, "new", 474 (const char *const *)ifo->config) == -1) 475 goto eexit; 476 } 477 #endif 478 #ifdef INET6 479 if (dhcp6 && D6_STATE_RUNNING(ifp)) { 480 n = dhcp6_env(NULL, NULL, ifp, 481 d6_state->new, d6_state->new_len); 482 if (n > 0) { 483 nenv = realloc(env, sizeof(char *) * 484 (elen + (size_t)n + 1)); 485 if (nenv == NULL) 486 goto eexit; 487 env = nenv; 488 n = dhcp6_env(env + elen, "new", ifp, 489 d6_state->new, d6_state->new_len); 490 if (n == -1) 491 goto eexit; 492 elen += (size_t)n; 493 } 494 } 495 if (ra) { 496 n = ipv6nd_env(NULL, NULL, ifp); 497 if (n > 0) { 498 nenv = realloc(env, sizeof(char *) * 499 (elen + (size_t)n + 1)); 500 if (nenv == NULL) 501 goto eexit; 502 env = nenv; 503 n = ipv6nd_env(env + elen, NULL, ifp); 504 if (n == -1) 505 goto eexit; 506 elen += (size_t)n; 507 } 508 } 509 #endif 510 511 /* Add our base environment */ 512 if (ifo->environ) { 513 e = 0; 514 while (ifo->environ[e++]) 515 ; 516 nenv = realloc(env, sizeof(char *) * (elen + e + 1)); 517 if (nenv == NULL) 518 goto eexit; 519 env = nenv; 520 e = 0; 521 while (ifo->environ[e]) { 522 env[elen + e] = strdup(ifo->environ[e]); 523 if (env[elen + e] == NULL) 524 goto eexit; 525 e++; 526 } 527 elen += e; 528 } 529 env[elen] = NULL; 530 531 *argv = env; 532 return (ssize_t)elen; 533 534 eexit: 535 logger(ifp->ctx, LOG_ERR, "%s: %m", __func__); 536 if (env) { 537 nenv = env; 538 while (*nenv) 539 free(*nenv++); 540 free(env); 541 } 542 return -1; 543 } 544 545 static int 546 send_interface1(struct fd_list *fd, const struct interface *iface, 547 const char *reason) 548 { 549 char **env, **ep, *s; 550 size_t elen; 551 int retval; 552 553 if (make_env(iface, reason, &env) == -1) 554 return -1; 555 s = NULL; 556 elen = (size_t)arraytostr((const char *const *)env, &s); 557 if ((ssize_t)elen == -1) { 558 free(s); 559 return -1; 560 } 561 retval = control_queue(fd, s, elen, 1); 562 ep = env; 563 while (*ep) 564 free(*ep++); 565 free(env); 566 return retval; 567 } 568 569 int 570 send_interface(struct fd_list *fd, const struct interface *ifp) 571 { 572 const char *reason; 573 int retval = 0; 574 #ifdef INET 575 const struct dhcp_state *d; 576 #endif 577 #ifdef INET6 578 const struct dhcp6_state *d6; 579 #endif 580 581 switch (ifp->carrier) { 582 case LINK_UP: 583 reason = "CARRIER"; 584 break; 585 case LINK_DOWN: 586 reason = "NOCARRIER"; 587 break; 588 default: 589 reason = "UNKNOWN"; 590 break; 591 } 592 if (send_interface1(fd, ifp, reason) == -1) 593 retval = -1; 594 #ifdef INET 595 if (D_STATE_RUNNING(ifp)) { 596 d = D_CSTATE(ifp); 597 if (send_interface1(fd, ifp, d->reason) == -1) 598 retval = -1; 599 } 600 #endif 601 602 #ifdef INET6 603 if (RS_STATE_RUNNING(ifp)) { 604 if (send_interface1(fd, ifp, "ROUTERADVERT") == -1) 605 retval = -1; 606 } 607 if (D6_STATE_RUNNING(ifp)) { 608 d6 = D6_CSTATE(ifp); 609 if (send_interface1(fd, ifp, d6->reason) == -1) 610 retval = -1; 611 } 612 #endif 613 614 return retval; 615 } 616 617 int 618 script_runreason(const struct interface *ifp, const char *reason) 619 { 620 char *argv[2]; 621 char **env = NULL, **ep; 622 char *svcname, *path, *bigenv; 623 size_t e, elen = 0; 624 pid_t pid; 625 int status = 0; 626 struct fd_list *fd; 627 628 if (ifp->options->script && 629 (ifp->options->script[0] == '\0' || 630 strcmp(ifp->options->script, "/dev/null") == 0)) 631 return 0; 632 633 argv[0] = ifp->options->script ? ifp->options->script : UNCONST(SCRIPT); 634 argv[1] = NULL; 635 logger(ifp->ctx, LOG_DEBUG, "%s: executing `%s' %s", 636 ifp->name, argv[0], reason); 637 638 /* Make our env */ 639 elen = (size_t)make_env(ifp, reason, &env); 640 if (elen == (size_t)-1) { 641 logger(ifp->ctx, LOG_ERR, "%s: make_env: %m", ifp->name); 642 return -1; 643 } 644 /* Resize for PATH and RC_SVCNAME */ 645 svcname = getenv(RC_SVCNAME); 646 ep = realloc(env, sizeof(char *) * (elen + 2 + (svcname ? 1 : 0))); 647 if (ep == NULL) { 648 elen = 0; 649 goto out; 650 } 651 env = ep; 652 /* Add path to it */ 653 path = getenv("PATH"); 654 if (path) { 655 e = strlen("PATH") + strlen(path) + 2; 656 env[elen] = malloc(e); 657 if (env[elen] == NULL) { 658 elen = 0; 659 goto out; 660 } 661 snprintf(env[elen], e, "PATH=%s", path); 662 } else { 663 env[elen] = strdup(DEFAULT_PATH); 664 if (env[elen] == NULL) { 665 elen = 0; 666 goto out; 667 } 668 } 669 if (svcname) { 670 e = strlen(RC_SVCNAME) + strlen(svcname) + 2; 671 env[++elen] = malloc(e); 672 if (env[elen] == NULL) { 673 elen = 0; 674 goto out; 675 } 676 snprintf(env[elen], e, "%s=%s", RC_SVCNAME, svcname); 677 } 678 env[++elen] = NULL; 679 680 pid = exec_script(ifp->ctx, argv, env); 681 if (pid == -1) 682 logger(ifp->ctx, LOG_ERR, "%s: %s: %m", __func__, argv[0]); 683 else if (pid != 0) { 684 /* Wait for the script to finish */ 685 while (waitpid(pid, &status, 0) == -1) { 686 if (errno != EINTR) { 687 logger(ifp->ctx, LOG_ERR, "waitpid: %m"); 688 status = 0; 689 break; 690 } 691 } 692 if (WIFEXITED(status)) { 693 if (WEXITSTATUS(status)) 694 logger(ifp->ctx, LOG_ERR, 695 "%s: %s: WEXITSTATUS %d", 696 __func__, argv[0], WEXITSTATUS(status)); 697 } else if (WIFSIGNALED(status)) 698 logger(ifp->ctx, LOG_ERR, "%s: %s: %s", 699 __func__, argv[0], strsignal(WTERMSIG(status))); 700 } 701 702 /* Send to our listeners */ 703 bigenv = NULL; 704 status = 0; 705 TAILQ_FOREACH(fd, &ifp->ctx->control_fds, next) { 706 if (!(fd->flags & FD_LISTEN)) 707 continue; 708 if (bigenv == NULL) { 709 elen = (size_t)arraytostr((const char *const *)env, 710 &bigenv); 711 if ((ssize_t)elen == -1) { 712 logger(ifp->ctx, LOG_ERR, "%s: arraytostr: %m", 713 ifp->name); 714 break; 715 } 716 } 717 if (control_queue(fd, bigenv, elen, 1) == -1) 718 logger(ifp->ctx, LOG_ERR, 719 "%s: control_queue: %m", __func__); 720 else 721 status = 1; 722 } 723 if (!status) 724 free(bigenv); 725 726 out: 727 /* Cleanup */ 728 ep = env; 729 while (*ep) 730 free(*ep++); 731 free(env); 732 if (elen == 0) 733 return -1; 734 return WEXITSTATUS(status); 735 } 736