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