1 /* $OpenBSD: src/sbin/dhclient/options.c,v 1.42 2012/10/27 23:08:53 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, struct option_data *option, 202 int emit_punct) 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 *data = option->data; 209 unsigned char *dp = data; 210 int len = option->len; 211 struct in_addr foo; 212 char comma; 213 214 /* Code should be between 0 and 255. */ 215 if (code > 255) 216 error("pretty_print_option: bad code %d", code); 217 218 if (emit_punct) 219 comma = ','; 220 else 221 comma = ' '; 222 223 /* Figure out the size of the data. */ 224 for (i = 0; dhcp_options[code].format[i]; i++) { 225 if (!numhunk) { 226 warning("%s: Excess information in format string: %s", 227 dhcp_options[code].name, 228 &(dhcp_options[code].format[i])); 229 break; 230 } 231 numelem++; 232 fmtbuf[i] = dhcp_options[code].format[i]; 233 switch (dhcp_options[code].format[i]) { 234 case 'A': 235 --numelem; 236 fmtbuf[i] = 0; 237 numhunk = 0; 238 if (hunksize == 0) { 239 warning("%s: no size indicator before A" 240 " in format string: %s", 241 dhcp_options[code].name, 242 dhcp_options[code].format); 243 return ("<fmt error>"); 244 } 245 break; 246 case 'X': 247 for (k = 0; k < len; k++) 248 if (!isascii(data[k]) || 249 !isprint(data[k])) 250 break; 251 if (k == len) { 252 fmtbuf[i] = 't'; 253 numhunk = -2; 254 } else { 255 fmtbuf[i] = 'x'; 256 hunksize++; 257 comma = ':'; 258 numhunk = 0; 259 } 260 fmtbuf[i + 1] = 0; 261 break; 262 case 't': 263 fmtbuf[i] = 't'; 264 fmtbuf[i + 1] = 0; 265 numhunk = -2; 266 break; 267 case 'I': 268 case 'l': 269 case 'L': 270 hunksize += 4; 271 break; 272 case 's': 273 case 'S': 274 hunksize += 2; 275 break; 276 case 'b': 277 case 'B': 278 case 'f': 279 hunksize++; 280 break; 281 case 'e': 282 break; 283 default: 284 warning("%s: garbage in format string: %s", 285 dhcp_options[code].name, 286 &(dhcp_options[code].format[i])); 287 break; 288 } 289 } 290 291 /* Check for too few bytes... */ 292 if (hunksize > len) { 293 warning("%s: expecting at least %d bytes; got %d", 294 dhcp_options[code].name, hunksize, len); 295 return ("<error>"); 296 } 297 /* Check for too many bytes... */ 298 if (numhunk == -1 && hunksize < len) 299 warning("%s: %d extra bytes", 300 dhcp_options[code].name, len - hunksize); 301 302 /* If this is an array, compute its size. */ 303 if (!numhunk) 304 numhunk = len / hunksize; 305 /* See if we got an exact number of hunks. */ 306 if (numhunk > 0 && numhunk * hunksize < len) 307 warning("%s: %d extra bytes at end of array", 308 dhcp_options[code].name, len - numhunk * hunksize); 309 310 /* A one-hunk array prints the same as a single hunk. */ 311 if (numhunk < 0) 312 numhunk = 1; 313 314 /* Cycle through the array (or hunk) printing the data. */ 315 for (i = 0; i < numhunk; i++) { 316 for (j = 0; j < numelem; j++) { 317 int opcount; 318 size_t oplen; 319 switch (fmtbuf[j]) { 320 case 't': 321 if (emit_punct) { 322 *op++ = '"'; 323 opleft--; 324 } 325 for (; dp < data + len; dp++) { 326 if (!isascii(*dp) || 327 !isprint(*dp)) { 328 if (dp + 1 != data + len || 329 *dp != 0) { 330 size_t oplen; 331 snprintf(op, opleft, 332 "\\%03o", *dp); 333 oplen = strlen(op); 334 op += oplen; 335 opleft -= oplen; 336 } 337 } else if (*dp == '"' || 338 *dp == '\'' || 339 *dp == '$' || 340 *dp == '`' || 341 *dp == '\\') { 342 *op++ = '\\'; 343 *op++ = *dp; 344 opleft -= 2; 345 } else { 346 *op++ = *dp; 347 opleft--; 348 } 349 } 350 if (emit_punct) { 351 *op++ = '"'; 352 opleft--; 353 } 354 355 *op = 0; 356 break; 357 case 'I': 358 foo.s_addr = htonl(getULong(dp)); 359 opcount = strlcpy(op, inet_ntoa(foo), opleft); 360 if (opcount >= opleft) 361 goto toobig; 362 opleft -= opcount; 363 dp += 4; 364 break; 365 case 'l': 366 opcount = snprintf(op, opleft, "%ld", 367 (long)getLong(dp)); 368 if (opcount >= opleft || opcount == -1) 369 goto toobig; 370 opleft -= opcount; 371 dp += 4; 372 break; 373 case 'L': 374 opcount = snprintf(op, opleft, "%ld", 375 (unsigned long)getULong(dp)); 376 if (opcount >= opleft || opcount == -1) 377 goto toobig; 378 opleft -= opcount; 379 dp += 4; 380 break; 381 case 's': 382 opcount = snprintf(op, opleft, "%d", 383 getShort(dp)); 384 if (opcount >= opleft || opcount == -1) 385 goto toobig; 386 opleft -= opcount; 387 dp += 2; 388 break; 389 case 'S': 390 opcount = snprintf(op, opleft, "%d", 391 getUShort(dp)); 392 if (opcount >= opleft || opcount == -1) 393 goto toobig; 394 opleft -= opcount; 395 dp += 2; 396 break; 397 case 'b': 398 opcount = snprintf(op, opleft, "%d", 399 *(char *)dp++); 400 if (opcount >= opleft || opcount == -1) 401 goto toobig; 402 opleft -= opcount; 403 break; 404 case 'B': 405 opcount = snprintf(op, opleft, "%d", *dp++); 406 if (opcount >= opleft || opcount == -1) 407 goto toobig; 408 opleft -= opcount; 409 break; 410 case 'x': 411 opcount = snprintf(op, opleft, "%x", *dp++); 412 if (opcount >= opleft || opcount == -1) 413 goto toobig; 414 opleft -= opcount; 415 break; 416 case 'f': 417 opcount = strlcpy(op, 418 *dp++ ? "true" : "false", opleft); 419 if (opcount >= opleft) 420 goto toobig; 421 opleft -= opcount; 422 break; 423 default: 424 warning("Unexpected format code %c", fmtbuf[j]); 425 } 426 oplen = strlen(op); 427 op += oplen; 428 opleft -= oplen; 429 if (opleft < 1) 430 goto toobig; 431 if (j + 1 < numelem && comma != ':') { 432 *op++ = ' '; 433 opleft--; 434 } 435 } 436 if (i + 1 < numhunk) { 437 *op++ = comma; 438 opleft--; 439 } 440 if (opleft < 1) 441 goto toobig; 442 443 } 444 return (optbuf); 445 toobig: 446 warning("dhcp option too large"); 447 return ("<error>"); 448 } 449 450 void 451 do_packet(int len, unsigned int from_port, struct iaddr from, 452 struct hardware *hfrom) 453 { 454 struct dhcp_packet *packet = &client->packet; 455 struct option_data options[256]; 456 struct iaddrlist *ap; 457 void (*handler)(struct iaddr, struct option_data *); 458 char *type; 459 int i, options_valid = 1; 460 461 if (packet->hlen > sizeof(packet->chaddr)) { 462 note("Discarding packet with invalid hlen."); 463 return; 464 } 465 466 /* 467 * Silently drop the packet if the client hardware address in the 468 * packet is not the hardware address of the interface being managed. 469 */ 470 if ((ifi->hw_address.hlen != packet->hlen) || 471 (memcmp(ifi->hw_address.haddr, packet->chaddr, packet->hlen))) 472 return; 473 474 memset(options, 0, sizeof(options)); 475 476 if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) { 477 /* Parse the BOOTP/DHCP options field. */ 478 options_valid = parse_option_buffer(options, 479 &packet->options[4], sizeof(packet->options) - 4); 480 481 /* Only DHCP packets have overload areas for options. */ 482 if (options_valid && 483 options[DHO_DHCP_MESSAGE_TYPE].data && 484 options[DHO_DHCP_OPTION_OVERLOAD].data) { 485 if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) 486 options_valid = parse_option_buffer(options, 487 (unsigned char *)packet->file, 488 sizeof(packet->file)); 489 if (options_valid && 490 options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) 491 options_valid = parse_option_buffer(options, 492 (unsigned char *)packet->sname, 493 sizeof(packet->sname)); 494 } 495 } 496 497 type = ""; 498 handler = NULL; 499 500 if (options[DHO_DHCP_MESSAGE_TYPE].data) { 501 /* Always try a DHCP packet, even if a bad option was seen. */ 502 switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) { 503 case DHCPOFFER: 504 handler = dhcpoffer; 505 type = "DHCPOFFER"; 506 break; 507 case DHCPNAK: 508 handler = dhcpnak; 509 type = "DHCPNACK"; 510 break; 511 case DHCPACK: 512 handler = dhcpack; 513 type = "DHCPACK"; 514 break; 515 default: 516 break; 517 } 518 } else if (options_valid && packet->op == BOOTREPLY) { 519 handler = dhcpoffer; 520 type = "BOOTREPLY"; 521 } 522 523 if (handler && client->xid == client->packet.xid) { 524 if (hfrom->hlen == 6) 525 note("%s from %s (%s)", type, piaddr(from), 526 ether_ntoa((struct ether_addr *)hfrom->haddr)); 527 else 528 note("%s from %s", type, piaddr(from)); 529 } else 530 handler = NULL; 531 532 for (ap = config->reject_list; ap && handler; ap = ap->next) 533 if (addr_eq(from, ap->addr)) { 534 note("%s from %s rejected.", type, piaddr(from)); 535 handler = NULL; 536 } 537 538 if (handler) 539 (*handler)(from, options); 540 541 for (i = 0; i < 256; i++) 542 if (options[i].len && options[i].data) 543 free(options[i].data); 544 } 545