xref: /dragonfly/contrib/dhcpcd/src/dhcp-common.c (revision e9678c6f9939e4603a10c5f27f033a91d33856b4)
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