1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * dhcpcd - DHCP client daemon 4 * Copyright (c) 2006-2023 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/utsname.h> 30 31 #include <ctype.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <inttypes.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "config.h" 40 41 #include "common.h" 42 #include "dhcp-common.h" 43 #include "dhcp.h" 44 #include "if.h" 45 #include "ipv6.h" 46 #include "logerr.h" 47 #include "script.h" 48 49 const char * 50 dhcp_get_hostname(char *buf, size_t buf_len, const struct if_options *ifo) 51 { 52 53 if (ifo->hostname[0] == '\0') { 54 if (gethostname(buf, buf_len) != 0) 55 return NULL; 56 buf[buf_len - 1] = '\0'; 57 } else 58 strlcpy(buf, ifo->hostname, buf_len); 59 60 /* Deny sending of these local hostnames */ 61 if (buf[0] == '\0' || buf[0] == '.' || 62 strcmp(buf, "(none)") == 0 || 63 strcmp(buf, "localhost") == 0 || 64 strncmp(buf, "localhost.", strlen("localhost.")) == 0) 65 return NULL; 66 67 /* Shorten the hostname if required */ 68 if (ifo->options & DHCPCD_HOSTNAME_SHORT) { 69 char *hp; 70 71 hp = strchr(buf, '.'); 72 if (hp != NULL) 73 *hp = '\0'; 74 } 75 76 return buf; 77 } 78 79 void 80 dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols) 81 { 82 83 while (cols < 40) { 84 putchar(' '); 85 cols++; 86 } 87 putchar('\t'); 88 if (opt->type & OT_EMBED) 89 printf(" embed"); 90 if (opt->type & OT_ENCAP) 91 printf(" encap"); 92 if (opt->type & OT_INDEX) 93 printf(" index"); 94 if (opt->type & OT_ARRAY) 95 printf(" array"); 96 if (opt->type & OT_UINT8) 97 printf(" uint8"); 98 else if (opt->type & OT_INT8) 99 printf(" int8"); 100 else if (opt->type & OT_UINT16) 101 printf(" uint16"); 102 else if (opt->type & OT_INT16) 103 printf(" int16"); 104 else if (opt->type & OT_UINT32) 105 printf(" uint32"); 106 else if (opt->type & OT_INT32) 107 printf(" int32"); 108 else if (opt->type & OT_ADDRIPV4) 109 printf(" ipaddress"); 110 else if (opt->type & OT_ADDRIPV6) 111 printf(" ip6address"); 112 else if (opt->type & OT_FLAG) 113 printf(" flag"); 114 else if (opt->type & OT_BITFLAG) 115 printf(" bitflags"); 116 else if (opt->type & OT_RFC1035) 117 printf(" domain"); 118 else if (opt->type & OT_DOMAIN) 119 printf(" dname"); 120 else if (opt->type & OT_ASCII) 121 printf(" ascii"); 122 else if (opt->type & OT_RAW) 123 printf(" raw"); 124 else if (opt->type & OT_BINHEX) 125 printf(" binhex"); 126 else if (opt->type & OT_STRING) 127 printf(" string"); 128 if (opt->type & OT_RFC3361) 129 printf(" rfc3361"); 130 if (opt->type & OT_RFC3442) 131 printf(" rfc3442"); 132 if (opt->type & OT_REQUEST) 133 printf(" request"); 134 if (opt->type & OT_NOREQ) 135 printf(" norequest"); 136 putchar('\n'); 137 } 138 139 struct dhcp_opt * 140 vivso_find(uint32_t iana_en, const void *arg) 141 { 142 const struct interface *ifp; 143 size_t i; 144 struct dhcp_opt *opt; 145 146 ifp = arg; 147 for (i = 0, opt = ifp->options->vivso_override; 148 i < ifp->options->vivso_override_len; 149 i++, opt++) 150 if (opt->option == iana_en) 151 return opt; 152 for (i = 0, opt = ifp->ctx->vivso; 153 i < ifp->ctx->vivso_len; 154 i++, opt++) 155 if (opt->option == iana_en) 156 return opt; 157 return NULL; 158 } 159 160 ssize_t 161 dhcp_vendor(char *str, size_t len) 162 { 163 struct utsname utn; 164 char *p; 165 int l; 166 167 if (uname(&utn) == -1) 168 return (ssize_t)snprintf(str, len, "%s-%s", 169 PACKAGE, VERSION); 170 p = str; 171 l = snprintf(p, len, 172 "%s-%s:%s-%s:%s", PACKAGE, VERSION, 173 utn.sysname, utn.release, utn.machine); 174 if (l == -1 || (size_t)(l + 1) > len) 175 return -1; 176 p += l; 177 len -= (size_t)l; 178 l = if_machinearch(p + 1, len - 1); 179 if (l == -1 || (size_t)(l + 1) > len) 180 return -1; 181 *p = ':'; 182 p += l; 183 return p - str; 184 } 185 186 int 187 make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len, 188 const struct dhcp_opt *odopts, size_t odopts_len, 189 uint8_t *mask, const char *opts, int add) 190 { 191 char *token, *o, *p; 192 const struct dhcp_opt *opt; 193 int match, e; 194 unsigned int n; 195 size_t i; 196 197 if (opts == NULL) 198 return -1; 199 o = p = strdup(opts); 200 while ((token = strsep(&p, ", "))) { 201 if (*token == '\0') 202 continue; 203 if (strncmp(token, "dhcp6_", 6) == 0) 204 token += 6; 205 if (strncmp(token, "nd_", 3) == 0) 206 token += 3; 207 match = 0; 208 for (i = 0, opt = odopts; i < odopts_len; i++, opt++) { 209 if (opt->var == NULL || opt->option == 0) 210 continue; /* buggy dhcpcd-definitions.conf */ 211 if (strcmp(opt->var, token) == 0) 212 match = 1; 213 else { 214 n = (unsigned int)strtou(token, NULL, 0, 215 0, UINT_MAX, &e); 216 if (e == 0 && opt->option == n) 217 match = 1; 218 } 219 if (match) 220 break; 221 } 222 if (match == 0) { 223 for (i = 0, opt = dopts; i < dopts_len; i++, opt++) { 224 if (strcmp(opt->var, token) == 0) 225 match = 1; 226 else { 227 n = (unsigned int)strtou(token, NULL, 0, 228 0, UINT_MAX, &e); 229 if (e == 0 && opt->option == n) 230 match = 1; 231 } 232 if (match) 233 break; 234 } 235 } 236 if (!match || !opt->option) { 237 free(o); 238 errno = ENOENT; 239 return -1; 240 } 241 if (add == 2 && !(opt->type & OT_ADDRIPV4)) { 242 free(o); 243 errno = EINVAL; 244 return -1; 245 } 246 if (add == 1 || add == 2) 247 add_option_mask(mask, opt->option); 248 else 249 del_option_mask(mask, opt->option); 250 } 251 free(o); 252 return 0; 253 } 254 255 size_t 256 encode_rfc1035(const char *src, uint8_t *dst) 257 { 258 uint8_t *p; 259 uint8_t *lp; 260 size_t len; 261 uint8_t has_dot; 262 263 if (src == NULL || *src == '\0') 264 return 0; 265 266 if (dst) { 267 p = dst; 268 lp = p++; 269 } 270 /* Silence bogus GCC warnings */ 271 else 272 p = lp = NULL; 273 274 len = 1; 275 has_dot = 0; 276 for (; *src; src++) { 277 if (*src == '\0') 278 break; 279 if (*src == '.') { 280 /* Skip the trailing . */ 281 if (src[1] == '\0') 282 break; 283 has_dot = 1; 284 if (dst) { 285 *lp = (uint8_t)(p - lp - 1); 286 if (*lp == '\0') 287 return len; 288 lp = p++; 289 } 290 } else if (dst) 291 *p++ = (uint8_t)*src; 292 len++; 293 } 294 295 if (dst) { 296 *lp = (uint8_t)(p - lp - 1); 297 if (has_dot) 298 *p++ = '\0'; 299 } 300 301 if (has_dot) 302 len++; 303 304 return len; 305 } 306 307 /* Decode an RFC1035 DNS search order option into a space 308 * separated string. Returns length of string (including 309 * terminating zero) or zero on error. out may be NULL 310 * to just determine output length. */ 311 ssize_t 312 decode_rfc1035(char *out, size_t len, const uint8_t *p, size_t pl) 313 { 314 const char *start; 315 size_t start_len, l, d_len, o_len; 316 const uint8_t *r, *q = p, *e; 317 int hops; 318 uint8_t ltype; 319 320 o_len = 0; 321 start = out; 322 start_len = len; 323 q = p; 324 e = p + pl; 325 while (q < e) { 326 r = NULL; 327 d_len = 0; 328 hops = 0; 329 /* Check we are inside our length again in-case 330 * the name isn't fully qualified (ie, not terminated) */ 331 while (q < e && (l = (size_t)*q++)) { 332 ltype = l & 0xc0; 333 if (ltype == 0x80 || ltype == 0x40) { 334 /* Currently reserved for future use as noted 335 * in RFC1035 4.1.4 as the 10 and 01 336 * combinations. */ 337 errno = ENOTSUP; 338 return -1; 339 } 340 else if (ltype == 0xc0) { /* pointer */ 341 if (q == e) { 342 errno = ERANGE; 343 return -1; 344 } 345 l = (l & 0x3f) << 8; 346 l |= *q++; 347 /* save source of first jump. */ 348 if (!r) 349 r = q; 350 hops++; 351 if (hops > 255) { 352 errno = ERANGE; 353 return -1; 354 } 355 q = p + l; 356 if (q >= e) { 357 errno = ERANGE; 358 return -1; 359 } 360 } else { 361 /* straightforward name segment, add with '.' */ 362 if (q + l > e) { 363 errno = ERANGE; 364 return -1; 365 } 366 if (l > NS_MAXLABEL) { 367 errno = EINVAL; 368 return -1; 369 } 370 d_len += l + 1; 371 if (out) { 372 if (l + 1 > len) { 373 errno = ENOBUFS; 374 return -1; 375 } 376 memcpy(out, q, l); 377 out += l; 378 *out++ = '.'; 379 len -= l; 380 len--; 381 } 382 q += l; 383 } 384 } 385 386 /* Don't count the trailing NUL */ 387 if (d_len > NS_MAXDNAME + 1) { 388 errno = E2BIG; 389 return -1; 390 } 391 o_len += d_len; 392 393 /* change last dot to space */ 394 if (out && out != start) 395 *(out - 1) = ' '; 396 if (r) 397 q = r; 398 } 399 400 /* change last space to zero terminator */ 401 if (out) { 402 if (out != start) 403 *(out - 1) = '\0'; 404 else if (start_len > 0) 405 *out = '\0'; 406 } 407 408 /* Remove the trailing NUL */ 409 if (o_len != 0) 410 o_len--; 411 412 return (ssize_t)o_len; 413 } 414 415 /* Check for a valid name as per RFC952 and RFC1123 section 2.1 */ 416 static ssize_t 417 valid_domainname(char *lbl, int type) 418 { 419 char *slbl = lbl, *lst = NULL; 420 unsigned char c; 421 int len = 0; 422 bool start = true, errset = false; 423 424 if (lbl == NULL || *lbl == '\0') { 425 errno = EINVAL; 426 return 0; 427 } 428 429 for (;;) { 430 c = (unsigned char)*lbl++; 431 if (c == '\0') 432 return lbl - slbl - 1; 433 if (c == ' ') { 434 if (lbl - 1 == slbl) /* No space at start */ 435 break; 436 if (!(type & OT_ARRAY)) 437 break; 438 /* Skip to the next label */ 439 if (!start) { 440 start = true; 441 lst = lbl - 1; 442 } 443 if (len) 444 len = 0; 445 continue; 446 } 447 if (c == '.') { 448 if (*lbl == '.') 449 break; 450 len = 0; 451 continue; 452 } 453 if (((c == '-' || c == '_') && 454 !start && *lbl != ' ' && *lbl != '\0') || 455 isalnum(c)) 456 { 457 if (++len > NS_MAXLABEL) { 458 errno = ERANGE; 459 errset = true; 460 break; 461 } 462 } else 463 break; 464 if (start) 465 start = false; 466 } 467 468 if (!errset) 469 errno = EINVAL; 470 if (lst) { 471 /* At least one valid domain, return it */ 472 *lst = '\0'; 473 return lst - slbl; 474 } 475 return 0; 476 } 477 478 /* 479 * Prints a chunk of data to a string. 480 * PS_SHELL goes as it is these days, it's upto the target to validate it. 481 * PS_SAFE has all non ascii and non printables changes to escaped octal. 482 */ 483 static const char hexchrs[] = "0123456789abcdef"; 484 ssize_t 485 print_string(char *dst, size_t len, int type, const uint8_t *data, size_t dl) 486 { 487 char *odst; 488 uint8_t c; 489 const uint8_t *e; 490 size_t bytes; 491 492 odst = dst; 493 bytes = 0; 494 e = data + dl; 495 496 while (data < e) { 497 c = *data++; 498 if (type & OT_BINHEX) { 499 if (dst) { 500 if (len == 0 || len == 1) { 501 errno = ENOBUFS; 502 return -1; 503 } 504 *dst++ = hexchrs[(c & 0xF0) >> 4]; 505 *dst++ = hexchrs[(c & 0x0F)]; 506 len -= 2; 507 } 508 bytes += 2; 509 continue; 510 } 511 if (type & OT_ASCII && (!isascii(c))) { 512 errno = EINVAL; 513 break; 514 } 515 if (!(type & (OT_ASCII | OT_RAW | OT_ESCSTRING | OT_ESCFILE)) && 516 (!isascii(c) && !isprint(c))) 517 { 518 errno = EINVAL; 519 break; 520 } 521 if ((type & (OT_ESCSTRING | OT_ESCFILE) && 522 (c == '\\' || !isascii(c) || !isprint(c))) || 523 (type & OT_ESCFILE && (c == '/' || c == ' '))) 524 { 525 errno = EINVAL; 526 if (c == '\\') { 527 if (dst) { 528 if (len == 0 || len == 1) { 529 errno = ENOBUFS; 530 return -1; 531 } 532 *dst++ = '\\'; *dst++ = '\\'; 533 len -= 2; 534 } 535 bytes += 2; 536 continue; 537 } 538 if (dst) { 539 if (len < 5) { 540 errno = ENOBUFS; 541 return -1; 542 } 543 *dst++ = '\\'; 544 *dst++ = (char)(((c >> 6) & 03) + '0'); 545 *dst++ = (char)(((c >> 3) & 07) + '0'); 546 *dst++ = (char)(( c & 07) + '0'); 547 len -= 4; 548 } 549 bytes += 4; 550 } else { 551 if (dst) { 552 if (len == 0) { 553 errno = ENOBUFS; 554 return -1; 555 } 556 *dst++ = (char)c; 557 len--; 558 } 559 bytes++; 560 } 561 } 562 563 /* NULL */ 564 if (dst) { 565 if (len == 0) { 566 errno = ENOBUFS; 567 return -1; 568 } 569 *dst = '\0'; 570 571 /* Now we've printed it, validate the domain */ 572 if (type & OT_DOMAIN && !valid_domainname(odst, type)) { 573 *odst = '\0'; 574 return 1; 575 } 576 577 } 578 579 return (ssize_t)bytes; 580 } 581 582 #define ADDR6SZ 16 583 static ssize_t 584 dhcp_optlen(const struct dhcp_opt *opt, size_t dl) 585 { 586 size_t sz; 587 588 if (opt->type & OT_ADDRIPV6) 589 sz = ADDR6SZ; 590 else if (opt->type & (OT_INT32 | OT_UINT32 | OT_ADDRIPV4)) 591 sz = sizeof(uint32_t); 592 else if (opt->type & (OT_INT16 | OT_UINT16)) 593 sz = sizeof(uint16_t); 594 else if (opt->type & (OT_INT8 | OT_UINT8 | OT_BITFLAG)) 595 sz = sizeof(uint8_t); 596 else if (opt->type & OT_FLAG) 597 return 0; 598 else { 599 /* All other types are variable length */ 600 if (opt->len) { 601 if ((size_t)opt->len > dl) { 602 errno = EOVERFLOW; 603 return -1; 604 } 605 return (ssize_t)opt->len; 606 } 607 return (ssize_t)dl; 608 } 609 if (dl < sz) { 610 errno = EOVERFLOW; 611 return -1; 612 } 613 614 /* Trim any extra data. 615 * Maybe we need a setting to reject DHCP options with extra data? */ 616 if (opt->type & OT_ARRAY) 617 return (ssize_t)(dl - (dl % sz)); 618 return (ssize_t)sz; 619 } 620 621 static ssize_t 622 print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt, 623 int vname, 624 const uint8_t *data, size_t dl, const char *ifname) 625 { 626 fpos_t fp_pos; 627 const uint8_t *e, *t; 628 uint16_t u16; 629 int16_t s16; 630 uint32_t u32; 631 int32_t s32; 632 struct in_addr addr; 633 ssize_t sl; 634 size_t l; 635 636 /* Ensure a valid length */ 637 dl = (size_t)dhcp_optlen(opt, dl); 638 if ((ssize_t)dl == -1) 639 return 0; 640 641 if (fgetpos(fp, &fp_pos) == -1) 642 return -1; 643 if (fprintf(fp, "%s", prefix) == -1) 644 goto err; 645 646 /* We printed something, so always goto err from now-on 647 * to terminate the string. */ 648 if (vname) { 649 if (fprintf(fp, "_%s", opt->var) == -1) 650 goto err; 651 } 652 if (fputc('=', fp) == EOF) 653 goto err; 654 if (dl == 0) 655 goto done; 656 657 if (opt->type & OT_RFC1035) { 658 char domain[NS_MAXDNAME]; 659 660 sl = decode_rfc1035(domain, sizeof(domain), data, dl); 661 if (sl == -1) 662 goto err; 663 if (sl == 0) 664 goto done; 665 if (!valid_domainname(domain, opt->type)) 666 goto err; 667 return efprintf(fp, "%s", domain); 668 } 669 670 #ifdef INET 671 if (opt->type & OT_RFC3361) 672 return print_rfc3361(fp, data, dl); 673 674 if (opt->type & OT_RFC3442) 675 return print_rfc3442(fp, data, dl); 676 #endif 677 678 if (opt->type & OT_STRING) { 679 char buf[1024]; 680 681 if (print_string(buf, sizeof(buf), opt->type, data, dl) == -1) 682 goto err; 683 return efprintf(fp, "%s", buf); 684 } 685 686 if (opt->type & OT_FLAG) 687 return efprintf(fp, "1"); 688 689 if (opt->type & OT_BITFLAG) { 690 /* bitflags are a string, MSB first, such as ABCDEFGH 691 * where A is 10000000, B is 01000000, etc. */ 692 for (l = 0, sl = sizeof(opt->bitflags) - 1; 693 l < sizeof(opt->bitflags); 694 l++, sl--) 695 { 696 /* Don't print NULL or 0 flags */ 697 if (opt->bitflags[l] != '\0' && 698 opt->bitflags[l] != '0' && 699 *data & (1 << sl)) 700 { 701 if (fputc(opt->bitflags[l], fp) == EOF) 702 goto err; 703 } 704 } 705 goto done; 706 } 707 708 t = data; 709 e = data + dl; 710 while (data < e) { 711 if (data != t) { 712 if (fputc(' ', fp) == EOF) 713 goto err; 714 } 715 if (opt->type & OT_UINT8) { 716 if (fprintf(fp, "%u", *data) == -1) 717 goto err; 718 data++; 719 } else if (opt->type & OT_INT8) { 720 if (fprintf(fp, "%d", *data) == -1) 721 goto err; 722 data++; 723 } else if (opt->type & OT_UINT16) { 724 memcpy(&u16, data, sizeof(u16)); 725 u16 = ntohs(u16); 726 if (fprintf(fp, "%u", u16) == -1) 727 goto err; 728 data += sizeof(u16); 729 } else if (opt->type & OT_INT16) { 730 memcpy(&u16, data, sizeof(u16)); 731 s16 = (int16_t)ntohs(u16); 732 if (fprintf(fp, "%d", s16) == -1) 733 goto err; 734 data += sizeof(u16); 735 } else if (opt->type & OT_UINT32) { 736 memcpy(&u32, data, sizeof(u32)); 737 u32 = ntohl(u32); 738 if (fprintf(fp, "%u", u32) == -1) 739 goto err; 740 data += sizeof(u32); 741 } else if (opt->type & OT_INT32) { 742 memcpy(&u32, data, sizeof(u32)); 743 s32 = (int32_t)ntohl(u32); 744 if (fprintf(fp, "%d", s32) == -1) 745 goto err; 746 data += sizeof(u32); 747 } else if (opt->type & OT_ADDRIPV4) { 748 memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); 749 if (fprintf(fp, "%s", inet_ntoa(addr)) == -1) 750 goto err; 751 data += sizeof(addr.s_addr); 752 } else if (opt->type & OT_ADDRIPV6) { 753 char buf[INET6_ADDRSTRLEN]; 754 755 if (inet_ntop(AF_INET6, data, buf, sizeof(buf)) == NULL) 756 goto err; 757 if (fprintf(fp, "%s", buf) == -1) 758 goto err; 759 if (data[0] == 0xfe && (data[1] & 0xc0) == 0x80) { 760 if (fprintf(fp,"%%%s", ifname) == -1) 761 goto err; 762 } 763 data += 16; 764 } else { 765 errno = EINVAL; 766 goto err; 767 } 768 } 769 770 done: 771 if (fputc('\0', fp) == EOF) 772 return -1; 773 return 1; 774 775 err: 776 (void)fsetpos(fp, &fp_pos); 777 return -1; 778 } 779 780 int 781 dhcp_set_leasefile(char *leasefile, size_t len, int family, 782 const struct interface *ifp) 783 { 784 char ssid[1 + (IF_SSIDLEN * 4) + 1]; /* - prefix and NUL terminated. */ 785 786 if (ifp->name[0] == '\0') { 787 strlcpy(leasefile, ifp->ctx->pidfile, len); 788 return 0; 789 } 790 791 switch (family) { 792 case AF_INET: 793 case AF_INET6: 794 break; 795 default: 796 errno = EINVAL; 797 return -1; 798 } 799 800 if (ifp->wireless) { 801 ssid[0] = '-'; 802 print_string(ssid + 1, sizeof(ssid) - 1, 803 OT_ESCFILE, 804 (const uint8_t *)ifp->ssid, ifp->ssid_len); 805 } else 806 ssid[0] = '\0'; 807 return snprintf(leasefile, len, 808 family == AF_INET ? LEASEFILE : LEASEFILE6, 809 ifp->name, ssid); 810 } 811 812 void 813 dhcp_envoption(struct dhcpcd_ctx *ctx, FILE *fp, const char *prefix, 814 const char *ifname, struct dhcp_opt *opt, 815 const uint8_t *(*dgetopt)(struct dhcpcd_ctx *, 816 size_t *, unsigned int *, size_t *, 817 const uint8_t *, size_t, struct dhcp_opt **), 818 const uint8_t *od, size_t ol) 819 { 820 size_t i, eos, eol; 821 ssize_t eo; 822 unsigned int eoc; 823 const uint8_t *eod; 824 int ov; 825 struct dhcp_opt *eopt, *oopt; 826 char *pfx; 827 828 /* If no embedded or encapsulated options, it's easy */ 829 if (opt->embopts_len == 0 && opt->encopts_len == 0) { 830 if (opt->type & OT_RESERVED) 831 return; 832 if (print_option(fp, prefix, opt, 1, od, ol, ifname) == -1) 833 logerr("%s: %s %d", ifname, __func__, opt->option); 834 return; 835 } 836 837 /* Create a new prefix based on the option */ 838 if (opt->type & OT_INDEX) { 839 if (asprintf(&pfx, "%s_%s%d", 840 prefix, opt->var, ++opt->index) == -1) 841 pfx = NULL; 842 } else { 843 if (asprintf(&pfx, "%s_%s", prefix, opt->var) == -1) 844 pfx = NULL; 845 } 846 if (pfx == NULL) { 847 logerr(__func__); 848 return; 849 } 850 851 /* Embedded options are always processed first as that 852 * is a fixed layout */ 853 for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) { 854 eo = dhcp_optlen(eopt, ol); 855 if (eo == -1) { 856 logerrx("%s: %s %d.%d/%zu: " 857 "malformed embedded option", 858 ifname, __func__, opt->option, 859 eopt->option, i); 860 goto out; 861 } 862 if (eo == 0) { 863 /* An option was expected, but there is no data 864 * data for it. 865 * This may not be an error as some options like 866 * DHCP FQDN in RFC4702 have a string as the last 867 * option which is optional. */ 868 if (ol != 0 || !(eopt->type & OT_OPTIONAL)) 869 logerrx("%s: %s %d.%d/%zu: " 870 "missing embedded option", 871 ifname, __func__, opt->option, 872 eopt->option, i); 873 goto out; 874 } 875 /* Use the option prefix if the embedded option 876 * name is different. 877 * This avoids new_fqdn_fqdn which would be silly. */ 878 if (!(eopt->type & OT_RESERVED)) { 879 ov = strcmp(opt->var, eopt->var); 880 if (print_option(fp, pfx, eopt, ov, od, (size_t)eo, 881 ifname) == -1) 882 logerr("%s: %s %d.%d/%zu", 883 ifname, __func__, 884 opt->option, eopt->option, i); 885 } 886 od += (size_t)eo; 887 ol -= (size_t)eo; 888 } 889 890 /* Enumerate our encapsulated options */ 891 if (opt->encopts_len && ol > 0) { 892 /* Zero any option indexes 893 * We assume that referenced encapsulated options are NEVER 894 * recursive as the index order could break. */ 895 for (i = 0, eopt = opt->encopts; 896 i < opt->encopts_len; 897 i++, eopt++) 898 { 899 eoc = opt->option; 900 if (eopt->type & OT_OPTION) { 901 dgetopt(ctx, NULL, &eoc, NULL, NULL, 0, &oopt); 902 if (oopt) 903 oopt->index = 0; 904 } 905 } 906 907 while ((eod = dgetopt(ctx, &eos, &eoc, &eol, od, ol, &oopt))) { 908 for (i = 0, eopt = opt->encopts; 909 i < opt->encopts_len; 910 i++, eopt++) 911 { 912 if (eopt->option != eoc) 913 continue; 914 if (eopt->type & OT_OPTION) { 915 if (oopt == NULL) 916 /* Report error? */ 917 continue; 918 } 919 dhcp_envoption(ctx, fp, pfx, ifname, 920 eopt->type & OT_OPTION ? oopt:eopt, 921 dgetopt, eod, eol); 922 } 923 od += eos + eol; 924 ol -= eos + eol; 925 } 926 } 927 928 out: 929 free(pfx); 930 } 931 932 void 933 dhcp_zero_index(struct dhcp_opt *opt) 934 { 935 size_t i; 936 struct dhcp_opt *o; 937 938 opt->index = 0; 939 for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++) 940 dhcp_zero_index(o); 941 for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++) 942 dhcp_zero_index(o); 943 } 944 945 ssize_t 946 dhcp_readfile(struct dhcpcd_ctx *ctx, const char *file, void *data, size_t len) 947 { 948 949 #ifdef PRIVSEP 950 if (ctx->options & DHCPCD_PRIVSEP && 951 !(ctx->options & DHCPCD_PRIVSEPROOT)) 952 return ps_root_readfile(ctx, file, data, len); 953 #else 954 UNUSED(ctx); 955 #endif 956 957 return readfile(file, data, len); 958 } 959 960 ssize_t 961 dhcp_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode, 962 const void *data, size_t len) 963 { 964 965 #ifdef PRIVSEP 966 if (ctx->options & DHCPCD_PRIVSEP && 967 !(ctx->options & DHCPCD_PRIVSEPROOT)) 968 return ps_root_writefile(ctx, file, mode, data, len); 969 #else 970 UNUSED(ctx); 971 #endif 972 973 return writefile(file, mode, data, len); 974 } 975 976 int 977 dhcp_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time) 978 { 979 980 #ifdef PRIVSEP 981 if (ctx->options & DHCPCD_PRIVSEP && 982 !(ctx->options & DHCPCD_PRIVSEPROOT)) 983 return (int)ps_root_filemtime(ctx, file, time); 984 #else 985 UNUSED(ctx); 986 #endif 987 988 return filemtime(file, time); 989 } 990 991 int 992 dhcp_unlink(struct dhcpcd_ctx *ctx, const char *file) 993 { 994 995 #ifdef PRIVSEP 996 if (ctx->options & DHCPCD_PRIVSEP && 997 !(ctx->options & DHCPCD_PRIVSEPROOT)) 998 return (int)ps_root_unlink(ctx, file); 999 #else 1000 UNUSED(ctx); 1001 #endif 1002 1003 return unlink(file); 1004 } 1005 1006 size_t 1007 dhcp_read_hwaddr_aton(struct dhcpcd_ctx *ctx, uint8_t **data, const char *file) 1008 { 1009 char buf[BUFSIZ]; 1010 ssize_t bytes; 1011 size_t len; 1012 1013 bytes = dhcp_readfile(ctx, file, buf, sizeof(buf)); 1014 if (bytes == -1 || bytes == sizeof(buf)) 1015 return 0; 1016 1017 bytes[buf] = '\0'; 1018 len = hwaddr_aton(NULL, buf); 1019 if (len == 0) 1020 return 0; 1021 *data = malloc(len); 1022 if (*data == NULL) 1023 return 0; 1024 hwaddr_aton(*data, buf); 1025 return len; 1026 } 1027