Bug Summary

File:src/usr.sbin/dhcpd/parse.c
Warning:line 242, column 3
Value stored to 'token' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name parse.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/dhcpd/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/dhcpd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/dhcpd/parse.c
1/* $OpenBSD: parse.c,v 1.27 2020/04/16 23:23:21 dtucker Exp $ */
2
3/* Common parser code for dhcpd and dhclient. */
4
5/*
6 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38 * Enterprises. To learn more about the Internet Software Consortium,
39 * see ``http://www.vix.com/isc''. To learn more about Vixie
40 * Enterprises, see ``http://www.vix.com''.
41 */
42
43#include <sys/types.h>
44#include <sys/socket.h>
45
46#include <net/if.h>
47
48#include <netinet/in.h>
49#include <netinet/if_ether.h>
50
51#include <ctype.h>
52#include <errno(*__errno()).h>
53#include <stdarg.h>
54#include <stdint.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <syslog.h>
59#include <time.h>
60#include <unistd.h>
61
62#include "dhcp.h"
63#include "tree.h"
64#include "dhcpd.h"
65#include "dhctoken.h"
66#include "log.h"
67
68/*
69 * Skip to the semicolon ending the current statement. If we encounter
70 * braces, the matching closing brace terminates the statement. If we
71 * encounter a right brace but haven't encountered a left brace, return
72 * leaving the brace in the token buffer for the caller. If we see a
73 * semicolon and haven't seen a left brace, return. This lets us skip
74 * over:
75 *
76 * statement;
77 * statement foo bar { }
78 * statement foo bar { statement { } }
79 * statement}
80 *
81 * ...et cetera.
82 */
83void
84skip_to_semi(FILE *cfile)
85{
86 int token;
87 char *val;
88 int brace_count = 0;
89
90 do {
91 token = peek_token(&val, cfile);
92 if (token == '}') {
93 if (brace_count) {
94 token = next_token(&val, cfile);
95 if (!--brace_count)
96 return;
97 } else
98 return;
99 } else if (token == '{') {
100 brace_count++;
101 } else if (token == ';' && !brace_count) {
102 token = next_token(&val, cfile);
103 return;
104 } else if (token == '\n') {
105 /*
106 * EOL only happens when parsing
107 * /etc/resolv.conf, and we treat it like a
108 * semicolon because the resolv.conf file is
109 * line-oriented.
110 */
111 token = next_token(&val, cfile);
112 return;
113 }
114 token = next_token(&val, cfile);
115 } while (token != EOF(-1));
116}
117
118int
119parse_semi(FILE *cfile)
120{
121 int token;
122 char *val;
123
124 token = next_token(&val, cfile);
125 if (token != ';') {
126 parse_warn("semicolon expected.");
127 skip_to_semi(cfile);
128 return (0);
129 }
130 return (1);
131}
132
133/*
134 * string-parameter :== STRING SEMI
135 */
136char *
137parse_string(FILE *cfile)
138{
139 char *val, *s;
140 int token;
141
142 token = next_token(&val, cfile);
143 if (token != TOK_STRING262) {
144 parse_warn("filename must be a string");
145 skip_to_semi(cfile);
146 return (NULL((void *)0));
147 }
148 s = strdup(val);
149 if (s == NULL((void *)0))
150 fatalx("no memory for string %s.", val);
151
152 if (!parse_semi(cfile)) {
153 free(s);
154 return (NULL((void *)0));
155 }
156 return (s);
157}
158
159/*
160 * hostname :== identifier | hostname DOT identifier
161 */
162char *
163parse_host_name(FILE *cfile)
164{
165 char *val, *s, *t;
166 int token, len = 0;
167 pair c = NULL((void *)0);
168
169 /* Read a dotted hostname... */
170 do {
171 /* Read a token, which should be an identifier. */
172 token = next_token(&val, cfile);
173 if (!is_identifier(token)((token) >= 256 && (token) != 262 && (token
) != 263 && (token) != (-1))
) {
174 parse_warn("expecting an identifier in hostname");
175 skip_to_semi(cfile);
176 return (NULL((void *)0));
177 }
178 /* Store this identifier... */
179 s = strdup(val);
180 if (s == NULL((void *)0))
181 fatalx("can't allocate temp space for hostname.");
182 c = cons((caddr_t) s, c);
183 len += strlen(s) + 1;
184 /*
185 * Look for a dot; if it's there, keep going, otherwise
186 * we're done.
187 */
188 token = peek_token(&val, cfile);
189 if (token == '.')
190 token = next_token(&val, cfile);
191 } while (token == '.');
192
193 /* Assemble the hostname together into a string. */
194 if (!(s = malloc(len)))
195 fatalx("can't allocate space for hostname.");
196 t = s + len;
197 *--t = '\0';
198 while (c) {
199 pair cdr = c->cdr;
200 int l = strlen((char *)c->car);
201
202 t -= l;
203 memcpy(t, (char *)c->car, l);
204 /* Free up temp space. */
205 free(c->car);
206 free(c);
207 c = cdr;
208 if (t != s)
209 *--t = '.';
210 }
211 return (s);
212}
213
214/*
215 * hardware-parameter :== HARDWARE ETHERNET csns SEMI
216 * csns :== NUMBER | csns COLON NUMBER
217 */
218void
219parse_hardware_param(FILE *cfile, struct hardware *hardware)
220{
221 char *val;
222 int token, hlen = 0;
223 unsigned char *e, *t = NULL((void *)0);
224
225 token = next_token(&val, cfile);
226 switch (token) {
227 case TOK_ETHERNET261:
228 hardware->htype = HTYPE_ETHER1;
229 break;
230 case TOK_IPSEC_TUNNEL336:
231 hardware->htype = HTYPE_IPSEC_TUNNEL31;
232 break;
233 default:
234 parse_warn("expecting a network hardware type");
235 skip_to_semi(cfile);
236 return;
237 }
238
239
240 /* Try looking up in /etc/ethers first. */
241 if (hardware->htype == HTYPE_ETHER1) {
242 token = peek_token(&val, cfile);
Value stored to 'token' is never read
243 hlen = sizeof(struct ether_addr);
244 if ((e = malloc(hlen)) == NULL((void *)0))
245 fatalx("can't allocate space for ethernet address.");
246 if (ether_hostton(val, (struct ether_addr *)e) == 0) {
247 (void)next_token(&val, cfile); /* consume token */
248 t = e;
249 } else
250 free(e);
251 }
252
253 /*
254 * Parse the hardware address information. Technically, it
255 * would make a lot of sense to restrict the length of the data
256 * we'll accept here to the length of a particular hardware
257 * address type. Unfortunately, there are some broken clients
258 * out there that put bogus data in the chaddr buffer, and we
259 * accept that data in the lease file rather than simply failing
260 * on such clients. Yuck.
261 */
262 if (!t)
263 t = parse_numeric_aggregate(cfile, NULL((void *)0), &hlen, ':', 16, 8);
264
265 if (!t)
266 return;
267 if (hlen > sizeof(hardware->haddr)) {
268 free(t);
269 parse_warn("hardware address too long");
270 } else {
271 hardware->hlen = hlen;
272 memcpy((unsigned char *)&hardware->haddr[0], t,
273 hardware->hlen);
274 if (hlen < sizeof(hardware->haddr))
275 memset(&hardware->haddr[hlen], 0,
276 sizeof(hardware->haddr) - hlen);
277 free(t);
278 }
279
280 token = next_token(&val, cfile);
281 if (token != ';') {
282 parse_warn("expecting semicolon.");
283 skip_to_semi(cfile);
284 }
285}
286
287/*
288 * lease-time :== NUMBER SEMI
289 */
290void
291parse_lease_time(FILE *cfile, time_t *timep)
292{
293 const char *errstr;
294 char *val;
295 uint32_t value;
296 int token;
297
298 token = next_token(&val, cfile);
299
300 value = strtonum(val, 0, UINT32_MAX0xffffffffU, &errstr);
301 if (errstr) {
302 parse_warn("lease time is %s: %s", errstr, val);
303 skip_to_semi(cfile);
304 return;
305 }
306
307 *timep = value;
308
309 parse_semi(cfile);
310}
311
312/*
313 * No BNF for numeric aggregates - that's defined by the caller. What
314 * this function does is to parse a sequence of numbers separated by the
315 * token specified in separator. If max is zero, any number of numbers
316 * will be parsed; otherwise, exactly max numbers are expected. Base
317 * and size tell us how to internalize the numbers once they've been
318 * tokenized.
319 */
320unsigned char *
321parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max,
322 int separator, int base, int size)
323{
324 char *val, *t;
325 int token, count = 0;
326 unsigned char *bufp = buf, *s = NULL((void *)0);
327 pair c = NULL((void *)0);
328
329 if (!bufp && *max) {
330 bufp = malloc(*max * size / 8);
331 if (!bufp)
332 fatalx("can't allocate space for numeric aggregate");
333 } else
334 s = bufp;
335
336 do {
337 if (count) {
338 token = peek_token(&val, cfile);
339 if (token != separator) {
340 if (!*max)
341 break;
342 if (token != '{' && token != '}')
343 token = next_token(&val, cfile);
344 parse_warn("too few numbers.");
345 if (token != ';')
346 skip_to_semi(cfile);
347 return (NULL((void *)0));
348 }
349 token = next_token(&val, cfile);
350 }
351 token = next_token(&val, cfile);
352
353 if (token == EOF(-1)) {
354 parse_warn("unexpected end of file");
355 break;
356 }
357 if (token != TOK_NUMBER263 && token != TOK_NUMBER_OR_NAME264) {
358 parse_warn("expecting numeric value.");
359 skip_to_semi(cfile);
360 return (NULL((void *)0));
361 }
362 /*
363 * If we can, convert the number now; otherwise, build a
364 * linked list of all the numbers.
365 */
366 if (s) {
367 convert_num(s, val, base, size);
368 s += size / 8;
369 } else {
370 t = strdup(val);
371 if (t == NULL((void *)0))
372 fatalx("no temp space for number.");
373 c = cons(t, c);
374 }
375 } while (++count != *max);
376
377 /* If we had to cons up a list, convert it now. */
378 if (c) {
379 bufp = malloc(count * size / 8);
380 if (!bufp)
381 fatalx("can't allocate space for numeric aggregate.");
382 s = bufp + count - size / 8;
383 *max = count;
384 }
385 while (c) {
386 pair cdr = c->cdr;
387 convert_num(s, (char *)c->car, base, size);
388 s -= size / 8;
389 /* Free up temp space. */
390 free(c->car);
391 free(c);
392 c = cdr;
393 }
394 return (bufp);
395}
396
397void
398convert_num(unsigned char *buf, char *str, int base, int size)
399{
400 int negative = 0, tval, max;
401 u_int32_t val = 0;
402 char *ptr = str;
403
404 if (*ptr == '-') {
405 negative = 1;
406 ptr++;
407 }
408
409 /* If base wasn't specified, figure it out from the data. */
410 if (!base) {
411 if (ptr[0] == '0') {
412 if (ptr[1] == 'x') {
413 base = 16;
414 ptr += 2;
415 } else if (isascii((unsigned char)ptr[1]) &&
416 isdigit((unsigned char)ptr[1])) {
417 base = 8;
418 ptr += 1;
419 } else
420 base = 10;
421 } else
422 base = 10;
423 }
424
425 do {
426 tval = *ptr++;
427 /* XXX assumes ASCII... */
428 if (tval >= 'a')
429 tval = tval - 'a' + 10;
430 else if (tval >= 'A')
431 tval = tval - 'A' + 10;
432 else if (tval >= '0')
433 tval -= '0';
434 else {
435 log_warnx("Bogus number: %s.", str);
436 break;
437 }
438 if (tval >= base) {
439 log_warnx("Bogus number: %s: digit %d not in base %d",
440 str, tval, base);
441 break;
442 }
443 val = val * base + tval;
444 } while (*ptr);
445
446 if (negative)
447 max = (1 << (size - 1));
448 else
449 max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
450 if (val > max) {
451 switch (base) {
452 case 8:
453 log_warnx("value %s%o exceeds max (%d) for precision.",
454 negative ? "-" : "", val, max);
455 break;
456 case 16:
457 log_warnx("value %s%x exceeds max (%d) for precision.",
458 negative ? "-" : "", val, max);
459 break;
460 default:
461 log_warnx("value %s%u exceeds max (%d) for precision.",
462 negative ? "-" : "", val, max);
463 break;
464 }
465 }
466
467 if (negative) {
468 switch (size) {
469 case 8:
470 *buf = -(unsigned long)val;
471 break;
472 case 16:
473 putShort(buf, -(unsigned long)val);
474 break;
475 case 32:
476 putLong(buf, -(unsigned long)val);
477 break;
478 default:
479 log_warnx("Unexpected integer size: %d", size);
480 break;
481 }
482 } else {
483 switch (size) {
484 case 8:
485 *buf = (u_int8_t)val;
486 break;
487 case 16:
488 putUShort(buf, (u_int16_t)val);
489 break;
490 case 32:
491 putULong(buf, val);
492 break;
493 default:
494 log_warnx("Unexpected integer size: %d", size);
495 break;
496 }
497 }
498}
499
500/*
501 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
502 * NUMBER COLON NUMBER COLON NUMBER UTC SEMI
503 *
504 * Dates are always in UTC; first number is day of week; next is
505 * year/month/day; next is hours:minutes:seconds on a 24-hour
506 * clock.
507 */
508time_t
509parse_date(FILE *cfile)
510{
511 struct tm tm;
512 char timestr[26]; /* "w yyyy/mm/dd hh:mm:ss UTC" */
513 char *val, *p;
514 size_t n;
515 time_t guess;
516 int token;
517
518 memset(timestr, 0, sizeof(timestr));
519
520 do {
521 token = peek_token(NULL((void *)0), cfile);
522 switch (token) {
523 case TOK_NAME265:
524 case TOK_NUMBER263:
525 case TOK_NUMBER_OR_NAME264:
526 case '/':
527 case ':':
528 token = next_token(&val, cfile);
529 n = strlcat(timestr, val, sizeof(timestr));
530 if (n >= sizeof(timestr)) {
531 /* XXX Will break after year 9999! */
532 parse_warn("time string too long");
533 skip_to_semi(cfile);
534 return (0);
535 }
536 break;
537 case';':
538 break;
539 default:
540 parse_warn("invalid time string");
541 skip_to_semi(cfile);
542 return (0);
543 }
544 } while (token != ';');
545
546 parse_semi(cfile);
547
548 memset(&tm, 0, sizeof(tm)); /* 'cuz strptime ignores tm_isdt. */
549 p = strptime(timestr, DB_TIMEFMT"%w %Y/%m/%d %T UTC", &tm);
550 if (p == NULL((void *)0) || *p != '\0') {
551 p = strptime(timestr, OLD_DB_TIMEFMT"%w %Y/%m/%d %T", &tm);
552 if (p == NULL((void *)0) || *p != '\0') {
553 parse_warn("unparseable time string");
554 return (0);
555 }
556 }
557
558 guess = timegm(&tm);
559 if (guess == -1) {
560 parse_warn("time could not be represented");
561 return (0);
562 }
563
564 return (guess);
565}
566
567int warnings_occurred;
568
569int
570parse_warn(char *fmt, ...)
571{
572 static char fbuf[1024];
573 static char mbuf[1024];
574 static char spaces[81];
575 va_list list;
576 int i;
577
578 snprintf(fbuf, sizeof(fbuf), "%s line %d: %s", tlname, lexline, mbuf);
579 va_start(list, fmt)__builtin_va_start(list, fmt);
580 vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
581 va_end(list)__builtin_va_end(list);
582
583 log_warnx("%s", mbuf);
584 log_warnx("%s", token_line);
585 if (lexchar < sizeof(spaces)) {
586 memset(spaces, 0, sizeof(spaces));
587 for (i = 0; i < lexchar - 1; i++) {
588 if (token_line[i] == '\t')
589 spaces[i] = '\t';
590 else
591 spaces[i] = ' ';
592 }
593 }
594 log_warnx("%s^", spaces);
595
596 warnings_occurred = 1;
597
598 return (0);
599}