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