dhclient - Properly handle zero-length option hints.
[dragonfly.git] / sbin / dhclient / options.c
CommitLineData
1c6d9dd3 1/* $OpenBSD: src/sbin/dhclient/options.c,v 1.38 2011/04/17 19:57:23 phessler Exp $ */
846204b6
HT
2
3/* DHCP options parsing and reassembly. */
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 <ctype.h>
44
45#include "dhcpd.h"
46
47int parse_option_buffer(struct option_data *, unsigned char *, int);
48
49/*
50 * Parse options out of the specified buffer, storing addresses of
51 * option values in options and setting client->options_valid if
52 * no errors are encountered.
53 */
54int
55parse_option_buffer(struct option_data *options, unsigned char *buffer,
56 int length)
57{
58 unsigned char *s, *t, *end = buffer + length;
59 int len, code;
60
61 for (s = buffer; *s != DHO_END && s < end; ) {
62 code = s[0];
63
64 /* Pad options don't have a length - just skip them. */
65 if (code == DHO_PAD) {
66 s++;
67 continue;
68 }
69
70 /*
71 * All options other than DHO_PAD and DHO_END have a
72 * one-byte length field.
73 */
74 if (s + 2 > end)
75 len = 0;
76 else
77 len = s[1];
78
79 /*
80 * If the option claims to extend beyond the end of the buffer
81 * then mark the options buffer bad.
82 */
83 if (s + len + 2 > end) {
84 warning("option %s (%d) larger than buffer.",
85 dhcp_options[code].name, len);
86 warning("rejecting bogus offer.");
87 return (0);
88 }
89 /*
90 * If we haven't seen this option before, just make
91 * space for it and copy it there.
92 */
93 if (!options[code].data) {
94 if (!(t = calloc(1, len + 1)))
95 error("Can't allocate storage for option %s.",
96 dhcp_options[code].name);
97 /*
98 * Copy and NUL-terminate the option (in case
99 * it's an ASCII string).
100 */
101 memcpy(t, &s[2], len);
102 t[len] = 0;
103 options[code].len = len;
104 options[code].data = t;
105 } else {
106 /*
107 * If it's a repeat, concatenate it to whatever
108 * we last saw. This is really only required
109 * for clients, but what the heck...
110 */
111 t = calloc(1, len + options[code].len + 1);
112 if (!t)
113 error("Can't expand storage for option %s.",
114 dhcp_options[code].name);
115 memcpy(t, options[code].data, options[code].len);
116 memcpy(t + options[code].len, &s[2], len);
117 options[code].len += len;
118 t[options[code].len] = 0;
119 free(options[code].data);
120 options[code].data = t;
121 }
122 s += len + 2;
123 }
124
125 return (1);
126}
127
128/*
129 * Copy as many options as fit in buflen bytes of buf. Return the
130 * offset of the start of the last option copied. A caller can check
131 * to see if it's DHO_END to decide if all the options were copied.
132 */
133int
134cons_options(unsigned char *buf, const int buflen, struct option_data *options)
135{
136 int ix, incr, length, bufix, code, lastopt = -1;
137
138 bzero(buf, buflen);
139
140 if (buflen > 3)
141 memcpy(buf, DHCP_OPTIONS_COOKIE, 4);
142 bufix = 4;
143
144 for (code = DHO_SUBNET_MASK; code < DHO_END; code++) {
145 if (!options[code].data)
146 continue;
147
148 length = options[code].len;
149 if (bufix + length + 2*((length+254)/255) >= buflen)
150 return (lastopt);
151
152 lastopt = bufix;
153 ix = 0;
154
155 while (length) {
156 incr = length > 255 ? 255 : length;
157
158 buf[bufix++] = code;
159 buf[bufix++] = incr;
160 memcpy(buf + bufix, options[code].data + ix, incr);
161
162 length -= incr;
163 ix += incr;
164 bufix += incr;
165 }
166 }
167
168 if (bufix < buflen) {
169 buf[bufix] = DHO_END;
170 lastopt = bufix;
171 }
172
173 return (lastopt);
174}
175
176/*
177 * Format the specified option so that a human can easily read it.
178 */
179char *
180pretty_print_option(unsigned int code, unsigned char *data, int len,
181 int emit_commas, int emit_quotes)
182{
183 static char optbuf[32768]; /* XXX */
184 int hunksize = 0, numhunk = -1, numelem = 0;
185 char fmtbuf[32], *op = optbuf;
186 int i, j, k, opleft = sizeof(optbuf);
187 unsigned char *dp = data;
188 struct in_addr foo;
189 char comma;
190
191 /* Code should be between 0 and 255. */
192 if (code > 255)
193 error("pretty_print_option: bad code %d", code);
194
195 if (emit_commas)
196 comma = ',';
197 else
198 comma = ' ';
199
200 /* Figure out the size of the data. */
201 for (i = 0; dhcp_options[code].format[i]; i++) {
202 if (!numhunk) {
203 warning("%s: Excess information in format string: %s",
204 dhcp_options[code].name,
205 &(dhcp_options[code].format[i]));
206 break;
207 }
208 numelem++;
209 fmtbuf[i] = dhcp_options[code].format[i];
210 switch (dhcp_options[code].format[i]) {
211 case 'A':
212 --numelem;
213 fmtbuf[i] = 0;
214 numhunk = 0;
1c6d9dd3
AHJ
215 if (hunksize == 0) {
216 warning("%s: no size indicator before A"
217 " in format string: %s",
218 dhcp_options[code].name,
219 dhcp_options[code].format);
220 return ("<fmt error>");
221 }
846204b6
HT
222 break;
223 case 'X':
224 for (k = 0; k < len; k++)
225 if (!isascii(data[k]) ||
226 !isprint(data[k]))
227 break;
228 if (k == len) {
229 fmtbuf[i] = 't';
230 numhunk = -2;
231 } else {
232 fmtbuf[i] = 'x';
233 hunksize++;
234 comma = ':';
235 numhunk = 0;
236 }
237 fmtbuf[i + 1] = 0;
238 break;
239 case 't':
240 fmtbuf[i] = 't';
241 fmtbuf[i + 1] = 0;
242 numhunk = -2;
243 break;
244 case 'I':
245 case 'l':
246 case 'L':
247 hunksize += 4;
248 break;
249 case 's':
250 case 'S':
251 hunksize += 2;
252 break;
253 case 'b':
254 case 'B':
255 case 'f':
256 hunksize++;
257 break;
258 case 'e':
259 break;
260 default:
261 warning("%s: garbage in format string: %s",
262 dhcp_options[code].name,
263 &(dhcp_options[code].format[i]));
264 break;
265 }
266 }
267
268 /* Check for too few bytes... */
269 if (hunksize > len) {
270 warning("%s: expecting at least %d bytes; got %d",
271 dhcp_options[code].name, hunksize, len);
272 return ("<error>");
273 }
274 /* Check for too many bytes... */
275 if (numhunk == -1 && hunksize < len)
276 warning("%s: %d extra bytes",
277 dhcp_options[code].name, len - hunksize);
278
279 /* If this is an array, compute its size. */
280 if (!numhunk)
281 numhunk = len / hunksize;
282 /* See if we got an exact number of hunks. */
283 if (numhunk > 0 && numhunk * hunksize < len)
284 warning("%s: %d extra bytes at end of array",
285 dhcp_options[code].name, len - numhunk * hunksize);
286
287 /* A one-hunk array prints the same as a single hunk. */
288 if (numhunk < 0)
289 numhunk = 1;
290
291 /* Cycle through the array (or hunk) printing the data. */
292 for (i = 0; i < numhunk; i++) {
293 for (j = 0; j < numelem; j++) {
294 int opcount;
295 size_t oplen;
296 switch (fmtbuf[j]) {
297 case 't':
298 if (emit_quotes) {
299 *op++ = '"';
300 opleft--;
301 }
302 for (; dp < data + len; dp++) {
303 if (!isascii(*dp) ||
304 !isprint(*dp)) {
305 if (dp + 1 != data + len ||
306 *dp != 0) {
307 size_t oplen;
308 snprintf(op, opleft,
309 "\\%03o", *dp);
310 oplen = strlen(op);
311 op += oplen;
312 opleft -= oplen;
313 }
314 } else if (*dp == '"' ||
315 *dp == '\'' ||
316 *dp == '$' ||
317 *dp == '`' ||
318 *dp == '\\') {
319 *op++ = '\\';
320 *op++ = *dp;
321 opleft -= 2;
322 } else {
323 *op++ = *dp;
324 opleft--;
325 }
326 }
327 if (emit_quotes) {
328 *op++ = '"';
329 opleft--;
330 }
331
332 *op = 0;
333 break;
334 case 'I':
335 foo.s_addr = htonl(getULong(dp));
336 opcount = strlcpy(op, inet_ntoa(foo), opleft);
337 if (opcount >= opleft)
338 goto toobig;
339 opleft -= opcount;
340 dp += 4;
341 break;
342 case 'l':
343 opcount = snprintf(op, opleft, "%ld",
344 (long)getLong(dp));
345 if (opcount >= opleft || opcount == -1)
346 goto toobig;
347 opleft -= opcount;
348 dp += 4;
349 break;
350 case 'L':
351 opcount = snprintf(op, opleft, "%ld",
352 (unsigned long)getULong(dp));
353 if (opcount >= opleft || opcount == -1)
354 goto toobig;
355 opleft -= opcount;
356 dp += 4;
357 break;
358 case 's':
359 opcount = snprintf(op, opleft, "%d",
360 getShort(dp));
361 if (opcount >= opleft || opcount == -1)
362 goto toobig;
363 opleft -= opcount;
364 dp += 2;
365 break;
366 case 'S':
367 opcount = snprintf(op, opleft, "%d",
368 getUShort(dp));
369 if (opcount >= opleft || opcount == -1)
370 goto toobig;
371 opleft -= opcount;
372 dp += 2;
373 break;
374 case 'b':
375 opcount = snprintf(op, opleft, "%d",
376 *(char *)dp++);
377 if (opcount >= opleft || opcount == -1)
378 goto toobig;
379 opleft -= opcount;
380 break;
381 case 'B':
382 opcount = snprintf(op, opleft, "%d", *dp++);
383 if (opcount >= opleft || opcount == -1)
384 goto toobig;
385 opleft -= opcount;
386 break;
387 case 'x':
388 opcount = snprintf(op, opleft, "%x", *dp++);
389 if (opcount >= opleft || opcount == -1)
390 goto toobig;
391 opleft -= opcount;
392 break;
393 case 'f':
394 opcount = strlcpy(op,
395 *dp++ ? "true" : "false", opleft);
396 if (opcount >= opleft)
397 goto toobig;
398 opleft -= opcount;
399 break;
400 default:
401 warning("Unexpected format code %c", fmtbuf[j]);
402 }
403 oplen = strlen(op);
404 op += oplen;
405 opleft -= oplen;
406 if (opleft < 1)
407 goto toobig;
408 if (j + 1 < numelem && comma != ':') {
409 *op++ = ' ';
410 opleft--;
411 }
412 }
413 if (i + 1 < numhunk) {
414 *op++ = comma;
415 opleft--;
416 }
417 if (opleft < 1)
418 goto toobig;
419
420 }
421 return (optbuf);
422 toobig:
423 warning("dhcp option too large");
424 return ("<error>");
425}
426
427void
428do_packet(int len, unsigned int from_port, struct iaddr from,
429 struct hardware *hfrom)
430{
431 struct dhcp_packet *packet = &client->packet;
432 struct option_data options[256];
433 struct iaddrlist *ap;
434 void (*handler)(struct iaddr, struct option_data *);
435 char *type;
436 int i, options_valid = 1;
437
438 if (packet->hlen > sizeof(packet->chaddr)) {
439 note("Discarding packet with invalid hlen.");
440 return;
441 }
442
443 /*
444 * Silently drop the packet if the client hardware address in the
445 * packet is not the hardware address of the interface being managed.
446 */
447 if ((ifi->hw_address.hlen != packet->hlen) ||
448 (memcmp(ifi->hw_address.haddr, packet->chaddr, packet->hlen)))
449 return;
450
451 memset(options, 0, sizeof(options));
452
453 if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) {
454 /* Parse the BOOTP/DHCP options field. */
455 options_valid = parse_option_buffer(options,
456 &packet->options[4], sizeof(packet->options) - 4);
457
458 /* Only DHCP packets have overload areas for options. */
459 if (options_valid &&
460 options[DHO_DHCP_MESSAGE_TYPE].data &&
461 options[DHO_DHCP_OPTION_OVERLOAD].data) {
462 if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
463 options_valid = parse_option_buffer(options,
464 (unsigned char *)packet->file,
465 sizeof(packet->file));
466 if (options_valid &&
467 options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
468 options_valid = parse_option_buffer(options,
469 (unsigned char *)packet->sname,
470 sizeof(packet->sname));
471 }
472 }
473
474 type = "";
475 handler = NULL;
476
477 if (options[DHO_DHCP_MESSAGE_TYPE].data) {
478 /* Always try a DHCP packet, even if a bad option was seen. */
479 switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) {
480 case DHCPOFFER:
481 handler = dhcpoffer;
482 type = "DHCPOFFER";
483 break;
484 case DHCPNAK:
485 handler = dhcpnak;
486 type = "DHCPNACK";
487 break;
488 case DHCPACK:
489 handler = dhcpack;
490 type = "DHCPACK";
491 break;
492 default:
493 break;
494 }
495 } else if (options_valid && packet->op == BOOTREPLY) {
496 handler = dhcpoffer;
497 type = "BOOTREPLY";
498 }
499
bdf60627
AHJ
500 if (handler && client->xid == client->packet.xid) {
501 if (hfrom->hlen == 6)
502 note("%s from %s (%s)", type, piaddr(from),
503 ether_ntoa((struct ether_addr *)hfrom->haddr));
504 else
505 note("%s from %s", type, piaddr(from));
506 } else
507 handler = NULL;
508
846204b6
HT
509 for (ap = config->reject_list; ap && handler; ap = ap->next)
510 if (addr_eq(from, ap->addr)) {
511 note("%s from %s rejected.", type, piaddr(from));
512 handler = NULL;
513 }
514
515 if (handler)
516 (*handler)(from, options);
517
518 for (i = 0; i < 256; i++)
519 if (options[i].len && options[i].data)
520 free(options[i].data);
521}