1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * dhcpcd - DHCP client daemon 4 * Copyright (c) 2006-2019 Roy Marples <roy@marples.name> 5 * All rights reserved 6 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/stat.h> 30 #include <sys/uio.h> 31 #include <sys/wait.h> 32 33 #include <netinet/in.h> 34 #include <arpa/inet.h> 35 36 #include <assert.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <signal.h> 40 #include <spawn.h> 41 #include <stdarg.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include "config.h" 47 #include "common.h" 48 #include "dhcp.h" 49 #include "dhcp6.h" 50 #include "if.h" 51 #include "if-options.h" 52 #include "ipv4ll.h" 53 #include "ipv6nd.h" 54 #include "logerr.h" 55 #include "script.h" 56 57 /* Allow the OS to define another script env var name */ 58 #ifndef RC_SVCNAME 59 #define RC_SVCNAME "RC_SVCNAME" 60 #endif 61 62 #define DEFAULT_PATH "/usr/bin:/usr/sbin:/bin:/sbin" 63 64 static const char * const if_params[] = { 65 "interface", 66 "protocol", 67 "reason", 68 "pid", 69 "ifcarrier", 70 "ifmetric", 71 "ifwireless", 72 "ifflags", 73 "ssid", 74 "profile", 75 "interface_order", 76 NULL 77 }; 78 79 void 80 if_printoptions(void) 81 { 82 const char * const *p; 83 84 for (p = if_params; *p; p++) 85 printf(" - %s\n", *p); 86 } 87 88 static int 89 exec_script(const struct dhcpcd_ctx *ctx, char *const *argv, char *const *env) 90 { 91 pid_t pid; 92 posix_spawnattr_t attr; 93 int r; 94 #ifdef USE_SIGNALS 95 size_t i; 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; i < dhcpcd_signals_len; i++) 111 sigaddset(&defsigs, dhcpcd_signals[i]); 112 posix_spawnattr_setsigdefault(&attr, &defsigs); 113 posix_spawnattr_setsigmask(&attr, &ctx->sigset); 114 #endif 115 errno = 0; 116 r = posix_spawn(&pid, argv[0], NULL, &attr, argv, env); 117 posix_spawnattr_destroy(&attr); 118 if (r) { 119 errno = r; 120 return -1; 121 } 122 return pid; 123 } 124 125 #ifdef INET 126 static int 127 append_config(FILE *fp, const char *prefix, const char *const *config) 128 { 129 size_t i; 130 131 if (config == NULL) 132 return 0; 133 134 /* Do we need to replace existing config rather than append? */ 135 for (i = 0; config[i] != NULL; i++) { 136 if (efprintf(fp, "%s_%s", prefix, config[i]) == -1) 137 return -1; 138 } 139 return 1; 140 } 141 142 #endif 143 144 #define PROTO_LINK 0 145 #define PROTO_DHCP 1 146 #define PROTO_IPV4LL 2 147 #define PROTO_RA 3 148 #define PROTO_DHCP6 4 149 #define PROTO_STATIC6 5 150 static const char *protocols[] = { 151 "link", 152 "dhcp", 153 "ipv4ll", 154 "ra", 155 "dhcp6", 156 "static6" 157 }; 158 159 int 160 efprintf(FILE *fp, const char *fmt, ...) 161 { 162 va_list args; 163 int r; 164 165 va_start(args, fmt); 166 r = vfprintf(fp, fmt, args); 167 va_end(args); 168 if (r == -1) 169 return -1; 170 /* Write a trailing NULL so we can easily create env strings. */ 171 if (fputc('\0', fp) == EOF) 172 return -1; 173 return r; 174 } 175 176 static long 177 make_env(const struct interface *ifp, const char *reason) 178 { 179 struct dhcpcd_ctx *ctx = ifp->ctx; 180 FILE *fp; 181 char **env, **envp, *bufp, *endp, *path; 182 size_t nenv; 183 long buf_pos, i; 184 int protocol = PROTO_LINK; 185 const struct if_options *ifo = ifp->options; 186 const struct interface *ifp2; 187 int af; 188 #ifdef INET 189 const struct dhcp_state *state; 190 #ifdef IPV4LL 191 const struct ipv4ll_state *istate; 192 #endif 193 #endif 194 #ifdef DHCP6 195 const struct dhcp6_state *d6_state; 196 #endif 197 198 #ifdef HAVE_OPEN_MEMSTREAM 199 if (ctx->script_fp == NULL) { 200 fp = open_memstream(&ctx->script_buf, &ctx->script_buflen); 201 if (fp == NULL) 202 goto eexit; 203 ctx->script_fp = fp; 204 } else { 205 fp = ctx->script_fp; 206 rewind(fp); 207 } 208 #else 209 char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX"; 210 int tmpfd; 211 212 fp = NULL; 213 tmpfd = mkstemp(tmpfile); 214 if (tmpfd == -1) 215 goto eexit; 216 unlink(tmpfile); 217 fp = fdopen(tmpfd, "w+"); 218 if (fp == NULL) { 219 close(tmpfd); 220 goto eexit; 221 } 222 #endif 223 224 #ifdef INET 225 state = D_STATE(ifp); 226 #ifdef IPV4LL 227 istate = IPV4LL_CSTATE(ifp); 228 #endif 229 #endif 230 #ifdef DHCP6 231 d6_state = D6_CSTATE(ifp); 232 #endif 233 if (strcmp(reason, "TEST") == 0) { 234 if (1 == 2) {} 235 #ifdef INET6 236 #ifdef DHCP6 237 else if (d6_state && d6_state->new) 238 protocol = PROTO_DHCP6; 239 #endif 240 else if (ipv6nd_hasra(ifp)) 241 protocol = PROTO_RA; 242 #endif 243 #ifdef INET 244 #ifdef IPV4LL 245 else if (istate && istate->addr != NULL) 246 protocol = PROTO_IPV4LL; 247 #endif 248 else 249 protocol = PROTO_DHCP; 250 #endif 251 } 252 #ifdef INET6 253 else if (strcmp(reason, "STATIC6") == 0) 254 protocol = PROTO_STATIC6; 255 #ifdef DHCP6 256 else if (reason[strlen(reason) - 1] == '6') 257 protocol = PROTO_DHCP6; 258 #endif 259 else if (strcmp(reason, "ROUTERADVERT") == 0) 260 protocol = PROTO_RA; 261 #endif 262 else if (strcmp(reason, "PREINIT") == 0 || 263 strcmp(reason, "CARRIER") == 0 || 264 strcmp(reason, "NOCARRIER") == 0 || 265 strcmp(reason, "UNKNOWN") == 0 || 266 strcmp(reason, "DEPARTED") == 0 || 267 strcmp(reason, "STOPPED") == 0) 268 protocol = PROTO_LINK; 269 #ifdef INET 270 #ifdef IPV4LL 271 else if (strcmp(reason, "IPV4LL") == 0) 272 protocol = PROTO_IPV4LL; 273 #endif 274 else 275 protocol = PROTO_DHCP; 276 #endif 277 278 /* Needed for scripts */ 279 path = getenv("PATH"); 280 if (efprintf(fp, "PATH=%s", path == NULL ? DEFAULT_PATH:path) == -1) 281 goto eexit; 282 283 if (efprintf(fp, "interface=%s", ifp->name) == -1) 284 goto eexit; 285 if (efprintf(fp, "reason=%s", reason) == -1) 286 goto eexit; 287 if (ifp->ctx->options & DHCPCD_DUMPLEASE) 288 goto dumplease; 289 if (efprintf(fp, "pid=%d", getpid()) == -1) 290 goto eexit; 291 if (efprintf(fp, "ifcarrier=%s", 292 ifp->carrier == LINK_UNKNOWN ? "unknown" : 293 ifp->carrier == LINK_UP ? "up" : "down") == -1) 294 goto eexit; 295 if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1) 296 goto eexit; 297 if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1) 298 goto eexit; 299 if (efprintf(fp, "ifflags=%u", ifp->flags) == -1) 300 goto eexit; 301 if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1) 302 goto eexit; 303 304 if (fprintf(fp, "interface_order=") == -1) 305 goto eexit; 306 TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 307 if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) { 308 if (fputc(' ', fp) == EOF) 309 return -1; 310 } 311 if (fprintf(fp, "%s", ifp2->name) == -1) 312 return -1; 313 } 314 if (fputc('\0', fp) == EOF) 315 return -1; 316 317 if (strcmp(reason, "STOPPED") == 0) { 318 if (efprintf(fp, "if_up=false") == -1) 319 goto eexit; 320 if (efprintf(fp, "if_down=%s", 321 ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1) 322 goto eexit; 323 } else if (strcmp(reason, "TEST") == 0 || 324 strcmp(reason, "PREINIT") == 0 || 325 strcmp(reason, "CARRIER") == 0 || 326 strcmp(reason, "UNKNOWN") == 0) 327 { 328 if (efprintf(fp, "if_up=false") == -1) 329 goto eexit; 330 if (efprintf(fp, "if_down=false") == -1) 331 goto eexit; 332 } else if (1 == 2 /* appease ifdefs */ 333 #ifdef INET 334 || (protocol == PROTO_DHCP && state && state->new) 335 #ifdef IPV4LL 336 || (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp)) 337 #endif 338 #endif 339 #ifdef INET6 340 || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp)) 341 #ifdef DHCP6 342 || (protocol == PROTO_DHCP6 && d6_state && d6_state->new) 343 #endif 344 || (protocol == PROTO_RA && ipv6nd_hasra(ifp)) 345 #endif 346 ) 347 { 348 if (efprintf(fp, "if_up=true") == -1) 349 goto eexit; 350 if (efprintf(fp, "if_down=false") == -1) 351 goto eexit; 352 } else { 353 if (efprintf(fp, "if_up=false") == -1) 354 goto eexit; 355 if (efprintf(fp, "if_down=true") == -1) 356 goto eexit; 357 } 358 if (protocols[protocol] != NULL) { 359 if (efprintf(fp, "protocol=%s", protocols[protocol]) == -1) 360 goto eexit; 361 } 362 if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) { 363 if (efprintf(fp, "if_afwaiting=%d", af) == -1) 364 goto eexit; 365 } 366 if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) { 367 TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) { 368 if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX) 369 break; 370 } 371 } 372 if (af != AF_MAX) { 373 if (efprintf(fp, "af_waiting=%d", af) == -1) 374 goto eexit; 375 } 376 if (ifo->options & DHCPCD_DEBUG) { 377 if (efprintf(fp, "syslog_debug=true") == -1) 378 goto eexit; 379 } 380 if (*ifp->profile) { 381 if (efprintf(fp, "profile=%s", ifp->profile) == -1) 382 goto eexit; 383 } 384 if (ifp->wireless) { 385 char pssid[IF_SSIDLEN * 4]; 386 387 if (print_string(pssid, sizeof(pssid), OT_ESCSTRING, 388 ifp->ssid, ifp->ssid_len) != -1) 389 { 390 if (efprintf(fp, "ifssid=%s", pssid) == -1) 391 goto eexit; 392 } 393 } 394 #ifdef INET 395 if (protocol == PROTO_DHCP && state && state->old) { 396 if (dhcp_env(fp, "old", ifp, 397 state->old, state->old_len) == -1) 398 goto eexit; 399 if (append_config(fp, "old", 400 (const char *const *)ifo->config) == -1) 401 goto eexit; 402 } 403 #endif 404 #ifdef DHCP6 405 if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) { 406 if (dhcp6_env(fp, "old", ifp, 407 d6_state->old, d6_state->old_len) == -1) 408 goto eexit; 409 } 410 #endif 411 412 dumplease: 413 #ifdef INET 414 #ifdef IPV4LL 415 if (protocol == PROTO_IPV4LL) { 416 if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1) 417 goto eexit; 418 } 419 #endif 420 if (protocol == PROTO_DHCP && state && state->new) { 421 if (dhcp_env(fp, "new", ifp, 422 state->new, state->new_len) == -1) 423 goto eexit; 424 if (append_config(fp, "new", 425 (const char *const *)ifo->config) == -1) 426 goto eexit; 427 } 428 #endif 429 #ifdef INET6 430 if (protocol == PROTO_STATIC6) { 431 if (ipv6_env(fp, "new", ifp) == -1) 432 goto eexit; 433 } 434 #ifdef DHCP6 435 if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) { 436 if (dhcp6_env(fp, "new", ifp, 437 d6_state->new, d6_state->new_len) == -1) 438 goto eexit; 439 } 440 #endif 441 if (protocol == PROTO_RA) { 442 if (ipv6nd_env(fp, ifp) == -1) 443 goto eexit; 444 } 445 #endif 446 447 /* Add our base environment */ 448 if (ifo->environ) { 449 for (i = 0; ifo->environ[i] != NULL; i++) 450 if (efprintf(fp, "%s", ifo->environ[i]) == -1) 451 goto eexit; 452 } 453 454 /* Convert buffer to argv */ 455 fflush(fp); 456 457 buf_pos = ftell(fp); 458 if (buf_pos == -1) { 459 logerr(__func__); 460 goto eexit; 461 } 462 463 #ifndef HAVE_OPEN_MEMSTREAM 464 size_t buf_len = (size_t)buf_pos; 465 if (ctx->script_buflen < buf_len) { 466 char *buf = realloc(ctx->script_buf, buf_len); 467 if (buf == NULL) 468 goto eexit; 469 ctx->script_buf = buf; 470 ctx->script_buflen = buf_len; 471 } 472 rewind(fp); 473 if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len) 474 goto eexit; 475 fclose(fp); 476 fp = NULL; 477 #endif 478 479 /* Count the terminated env strings. 480 * Assert that the terminations are correct. */ 481 nenv = 0; 482 endp = ctx->script_buf + buf_pos; 483 for (bufp = ctx->script_buf; bufp < endp; bufp++) { 484 if (*bufp == '\0') { 485 #ifndef NDEBUG 486 if (bufp + 1 < endp) 487 assert(*(bufp + 1) != '\0'); 488 #endif 489 nenv++; 490 } 491 } 492 assert(*(bufp - 1) == '\0'); 493 494 if (ctx->script_envlen < nenv) { 495 env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env)); 496 if (env == NULL) 497 goto eexit; 498 ctx->script_env = env; 499 ctx->script_envlen = nenv; 500 } 501 502 bufp = ctx->script_buf; 503 envp = ctx->script_env; 504 *envp++ = bufp++; 505 endp--; /* Avoid setting the last \0 to an invalid pointer */ 506 for (; bufp < endp; bufp++) { 507 if (*bufp == '\0') 508 *envp++ = bufp + 1; 509 } 510 *envp = NULL; 511 512 return buf_pos - 1; 513 514 eexit: 515 logerr(__func__); 516 #ifndef HAVE_OPEN_MEMSTREAM 517 if (fp != NULL) 518 fclose(fp); 519 #endif 520 return -1; 521 } 522 523 static int 524 send_interface1(struct fd_list *fd, const struct interface *ifp, 525 const char *reason) 526 { 527 struct dhcpcd_ctx *ctx = ifp->ctx; 528 long len; 529 530 len = make_env(ifp, reason); 531 if (len == -1) 532 return -1; 533 return control_queue(fd, ctx->script_buf, (size_t)len, 1); 534 } 535 536 int 537 send_interface(struct fd_list *fd, const struct interface *ifp) 538 { 539 const char *reason; 540 int retval = 0; 541 #ifdef INET 542 const struct dhcp_state *d; 543 #endif 544 #ifdef DHCP6 545 const struct dhcp6_state *d6; 546 #endif 547 548 switch (ifp->carrier) { 549 case LINK_UP: 550 reason = "CARRIER"; 551 break; 552 case LINK_DOWN: 553 case LINK_DOWN_IFFUP: 554 reason = "NOCARRIER"; 555 break; 556 default: 557 reason = "UNKNOWN"; 558 break; 559 } 560 if (send_interface1(fd, ifp, reason) == -1) 561 retval = -1; 562 #ifdef INET 563 if (D_STATE_RUNNING(ifp)) { 564 d = D_CSTATE(ifp); 565 if (send_interface1(fd, ifp, d->reason) == -1) 566 retval = -1; 567 } 568 #ifdef IPV4LL 569 if (IPV4LL_STATE_RUNNING(ifp)) { 570 if (send_interface1(fd, ifp, "IPV4LL") == -1) 571 retval = -1; 572 } 573 #endif 574 #endif 575 576 #ifdef INET6 577 if (IPV6_STATE_RUNNING(ifp)) { 578 if (send_interface1(fd, ifp, "STATIC6") == -1) 579 retval = -1; 580 } 581 if (RS_STATE_RUNNING(ifp)) { 582 if (send_interface1(fd, ifp, "ROUTERADVERT") == -1) 583 retval = -1; 584 } 585 #ifdef DHCP6 586 if (D6_STATE_RUNNING(ifp)) { 587 d6 = D6_CSTATE(ifp); 588 if (send_interface1(fd, ifp, d6->reason) == -1) 589 retval = -1; 590 } 591 #endif 592 #endif 593 594 return retval; 595 } 596 597 int 598 script_runreason(const struct interface *ifp, const char *reason) 599 { 600 struct dhcpcd_ctx *ctx = ifp->ctx; 601 char *argv[2]; 602 pid_t pid; 603 int status = 0; 604 struct fd_list *fd; 605 606 if (ifp->options->script == NULL && 607 TAILQ_FIRST(&ifp->ctx->control_fds) == NULL) 608 return 0; 609 610 /* Make our env */ 611 if (make_env(ifp, reason) == -1) { 612 logerr(__func__); 613 return -1; 614 } 615 616 if (ifp->options->script == NULL) 617 goto send_listeners; 618 619 argv[0] = ifp->options->script; 620 argv[1] = NULL; 621 logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason); 622 623 pid = exec_script(ctx, argv, ctx->script_env); 624 if (pid == -1) 625 logerr("%s: %s", __func__, argv[0]); 626 else if (pid != 0) { 627 /* Wait for the script to finish */ 628 while (waitpid(pid, &status, 0) == -1) { 629 if (errno != EINTR) { 630 logerr("%s: waitpid", __func__); 631 status = 0; 632 break; 633 } 634 } 635 if (WIFEXITED(status)) { 636 if (WEXITSTATUS(status)) 637 logerrx("%s: %s: WEXITSTATUS %d", 638 __func__, argv[0], WEXITSTATUS(status)); 639 } else if (WIFSIGNALED(status)) 640 logerrx("%s: %s: %s", 641 __func__, argv[0], strsignal(WTERMSIG(status))); 642 } 643 644 send_listeners: 645 /* Send to our listeners */ 646 status = 0; 647 TAILQ_FOREACH(fd, &ctx->control_fds, next) { 648 if (!(fd->flags & FD_LISTEN)) 649 continue; 650 if (control_queue(fd, ctx->script_buf, ctx->script_buflen, 651 true) == -1) 652 logerr("%s: control_queue", __func__); 653 else 654 status = 1; 655 } 656 657 return WEXITSTATUS(status); 658 } 659