1 /* $OpenBSD: options.c,v 1.36 2007/06/02 01:29:11 pvalchev Exp $ */ 2 /* $DragonFly: src/sbin/dhclient/options.c,v 1.1 2008/08/30 16:07:58 hasso Exp $ */ 3 4 /* DHCP options parsing and reassembly. */ 5 6 /* 7 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of The Internet Software Consortium nor the names 20 * of its contributors may be used to endorse or promote products derived 21 * from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 24 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 31 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 32 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * This software has been written for the Internet Software Consortium 38 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 39 * Enterprises. To learn more about the Internet Software Consortium, 40 * see ``http://www.vix.com/isc''. To learn more about Vixie 41 * Enterprises, see ``http://www.vix.com''. 42 */ 43 44 #include <ctype.h> 45 46 #include "dhcpd.h" 47 48 int parse_option_buffer(struct option_data *, unsigned char *, int); 49 50 /* 51 * Parse options out of the specified buffer, storing addresses of 52 * option values in options and setting client->options_valid if 53 * no errors are encountered. 54 */ 55 int 56 parse_option_buffer(struct option_data *options, unsigned char *buffer, 57 int length) 58 { 59 unsigned char *s, *t, *end = buffer + length; 60 int len, code; 61 62 for (s = buffer; *s != DHO_END && s < end; ) { 63 code = s[0]; 64 65 /* Pad options don't have a length - just skip them. */ 66 if (code == DHO_PAD) { 67 s++; 68 continue; 69 } 70 71 /* 72 * All options other than DHO_PAD and DHO_END have a 73 * one-byte length field. 74 */ 75 if (s + 2 > end) 76 len = 0; 77 else 78 len = s[1]; 79 80 /* 81 * If the option claims to extend beyond the end of the buffer 82 * then mark the options buffer bad. 83 */ 84 if (s + len + 2 > end) { 85 warning("option %s (%d) larger than buffer.", 86 dhcp_options[code].name, len); 87 warning("rejecting bogus offer."); 88 return (0); 89 } 90 /* 91 * If we haven't seen this option before, just make 92 * space for it and copy it there. 93 */ 94 if (!options[code].data) { 95 if (!(t = calloc(1, len + 1))) 96 error("Can't allocate storage for option %s.", 97 dhcp_options[code].name); 98 /* 99 * Copy and NUL-terminate the option (in case 100 * it's an ASCII string). 101 */ 102 memcpy(t, &s[2], len); 103 t[len] = 0; 104 options[code].len = len; 105 options[code].data = t; 106 } else { 107 /* 108 * If it's a repeat, concatenate it to whatever 109 * we last saw. This is really only required 110 * for clients, but what the heck... 111 */ 112 t = calloc(1, len + options[code].len + 1); 113 if (!t) 114 error("Can't expand storage for option %s.", 115 dhcp_options[code].name); 116 memcpy(t, options[code].data, options[code].len); 117 memcpy(t + options[code].len, &s[2], len); 118 options[code].len += len; 119 t[options[code].len] = 0; 120 free(options[code].data); 121 options[code].data = t; 122 } 123 s += len + 2; 124 } 125 126 return (1); 127 } 128 129 /* 130 * Copy as many options as fit in buflen bytes of buf. Return the 131 * offset of the start of the last option copied. A caller can check 132 * to see if it's DHO_END to decide if all the options were copied. 133 */ 134 int 135 cons_options(unsigned char *buf, const int buflen, struct option_data *options) 136 { 137 int ix, incr, length, bufix, code, lastopt = -1; 138 139 bzero(buf, buflen); 140 141 if (buflen > 3) 142 memcpy(buf, DHCP_OPTIONS_COOKIE, 4); 143 bufix = 4; 144 145 for (code = DHO_SUBNET_MASK; code < DHO_END; code++) { 146 if (!options[code].data) 147 continue; 148 149 length = options[code].len; 150 if (bufix + length + 2*((length+254)/255) >= buflen) 151 return (lastopt); 152 153 lastopt = bufix; 154 ix = 0; 155 156 while (length) { 157 incr = length > 255 ? 255 : length; 158 159 buf[bufix++] = code; 160 buf[bufix++] = incr; 161 memcpy(buf + bufix, options[code].data + ix, incr); 162 163 length -= incr; 164 ix += incr; 165 bufix += incr; 166 } 167 } 168 169 if (bufix < buflen) { 170 buf[bufix] = DHO_END; 171 lastopt = bufix; 172 } 173 174 return (lastopt); 175 } 176 177 /* 178 * Format the specified option so that a human can easily read it. 179 */ 180 char * 181 pretty_print_option(unsigned int code, unsigned char *data, int len, 182 int emit_commas, int emit_quotes) 183 { 184 static char optbuf[32768]; /* XXX */ 185 int hunksize = 0, numhunk = -1, numelem = 0; 186 char fmtbuf[32], *op = optbuf; 187 int i, j, k, opleft = sizeof(optbuf); 188 unsigned char *dp = data; 189 struct in_addr foo; 190 char comma; 191 192 /* Code should be between 0 and 255. */ 193 if (code > 255) 194 error("pretty_print_option: bad code %d", code); 195 196 if (emit_commas) 197 comma = ','; 198 else 199 comma = ' '; 200 201 /* Figure out the size of the data. */ 202 for (i = 0; dhcp_options[code].format[i]; i++) { 203 if (!numhunk) { 204 warning("%s: Excess information in format string: %s", 205 dhcp_options[code].name, 206 &(dhcp_options[code].format[i])); 207 break; 208 } 209 numelem++; 210 fmtbuf[i] = dhcp_options[code].format[i]; 211 switch (dhcp_options[code].format[i]) { 212 case 'A': 213 --numelem; 214 fmtbuf[i] = 0; 215 numhunk = 0; 216 break; 217 case 'X': 218 for (k = 0; k < len; k++) 219 if (!isascii(data[k]) || 220 !isprint(data[k])) 221 break; 222 if (k == len) { 223 fmtbuf[i] = 't'; 224 numhunk = -2; 225 } else { 226 fmtbuf[i] = 'x'; 227 hunksize++; 228 comma = ':'; 229 numhunk = 0; 230 } 231 fmtbuf[i + 1] = 0; 232 break; 233 case 't': 234 fmtbuf[i] = 't'; 235 fmtbuf[i + 1] = 0; 236 numhunk = -2; 237 break; 238 case 'I': 239 case 'l': 240 case 'L': 241 hunksize += 4; 242 break; 243 case 's': 244 case 'S': 245 hunksize += 2; 246 break; 247 case 'b': 248 case 'B': 249 case 'f': 250 hunksize++; 251 break; 252 case 'e': 253 break; 254 default: 255 warning("%s: garbage in format string: %s", 256 dhcp_options[code].name, 257 &(dhcp_options[code].format[i])); 258 break; 259 } 260 } 261 262 /* Check for too few bytes... */ 263 if (hunksize > len) { 264 warning("%s: expecting at least %d bytes; got %d", 265 dhcp_options[code].name, hunksize, len); 266 return ("<error>"); 267 } 268 /* Check for too many bytes... */ 269 if (numhunk == -1 && hunksize < len) 270 warning("%s: %d extra bytes", 271 dhcp_options[code].name, len - hunksize); 272 273 /* If this is an array, compute its size. */ 274 if (!numhunk) 275 numhunk = len / hunksize; 276 /* See if we got an exact number of hunks. */ 277 if (numhunk > 0 && numhunk * hunksize < len) 278 warning("%s: %d extra bytes at end of array", 279 dhcp_options[code].name, len - numhunk * hunksize); 280 281 /* A one-hunk array prints the same as a single hunk. */ 282 if (numhunk < 0) 283 numhunk = 1; 284 285 /* Cycle through the array (or hunk) printing the data. */ 286 for (i = 0; i < numhunk; i++) { 287 for (j = 0; j < numelem; j++) { 288 int opcount; 289 size_t oplen; 290 switch (fmtbuf[j]) { 291 case 't': 292 if (emit_quotes) { 293 *op++ = '"'; 294 opleft--; 295 } 296 for (; dp < data + len; dp++) { 297 if (!isascii(*dp) || 298 !isprint(*dp)) { 299 if (dp + 1 != data + len || 300 *dp != 0) { 301 size_t oplen; 302 snprintf(op, opleft, 303 "\\%03o", *dp); 304 oplen = strlen(op); 305 op += oplen; 306 opleft -= oplen; 307 } 308 } else if (*dp == '"' || 309 *dp == '\'' || 310 *dp == '$' || 311 *dp == '`' || 312 *dp == '\\') { 313 *op++ = '\\'; 314 *op++ = *dp; 315 opleft -= 2; 316 } else { 317 *op++ = *dp; 318 opleft--; 319 } 320 } 321 if (emit_quotes) { 322 *op++ = '"'; 323 opleft--; 324 } 325 326 *op = 0; 327 break; 328 case 'I': 329 foo.s_addr = htonl(getULong(dp)); 330 opcount = strlcpy(op, inet_ntoa(foo), opleft); 331 if (opcount >= opleft) 332 goto toobig; 333 opleft -= opcount; 334 dp += 4; 335 break; 336 case 'l': 337 opcount = snprintf(op, opleft, "%ld", 338 (long)getLong(dp)); 339 if (opcount >= opleft || opcount == -1) 340 goto toobig; 341 opleft -= opcount; 342 dp += 4; 343 break; 344 case 'L': 345 opcount = snprintf(op, opleft, "%ld", 346 (unsigned long)getULong(dp)); 347 if (opcount >= opleft || opcount == -1) 348 goto toobig; 349 opleft -= opcount; 350 dp += 4; 351 break; 352 case 's': 353 opcount = snprintf(op, opleft, "%d", 354 getShort(dp)); 355 if (opcount >= opleft || opcount == -1) 356 goto toobig; 357 opleft -= opcount; 358 dp += 2; 359 break; 360 case 'S': 361 opcount = snprintf(op, opleft, "%d", 362 getUShort(dp)); 363 if (opcount >= opleft || opcount == -1) 364 goto toobig; 365 opleft -= opcount; 366 dp += 2; 367 break; 368 case 'b': 369 opcount = snprintf(op, opleft, "%d", 370 *(char *)dp++); 371 if (opcount >= opleft || opcount == -1) 372 goto toobig; 373 opleft -= opcount; 374 break; 375 case 'B': 376 opcount = snprintf(op, opleft, "%d", *dp++); 377 if (opcount >= opleft || opcount == -1) 378 goto toobig; 379 opleft -= opcount; 380 break; 381 case 'x': 382 opcount = snprintf(op, opleft, "%x", *dp++); 383 if (opcount >= opleft || opcount == -1) 384 goto toobig; 385 opleft -= opcount; 386 break; 387 case 'f': 388 opcount = strlcpy(op, 389 *dp++ ? "true" : "false", opleft); 390 if (opcount >= opleft) 391 goto toobig; 392 opleft -= opcount; 393 break; 394 default: 395 warning("Unexpected format code %c", fmtbuf[j]); 396 } 397 oplen = strlen(op); 398 op += oplen; 399 opleft -= oplen; 400 if (opleft < 1) 401 goto toobig; 402 if (j + 1 < numelem && comma != ':') { 403 *op++ = ' '; 404 opleft--; 405 } 406 } 407 if (i + 1 < numhunk) { 408 *op++ = comma; 409 opleft--; 410 } 411 if (opleft < 1) 412 goto toobig; 413 414 } 415 return (optbuf); 416 toobig: 417 warning("dhcp option too large"); 418 return ("<error>"); 419 } 420 421 void 422 do_packet(int len, unsigned int from_port, struct iaddr from, 423 struct hardware *hfrom) 424 { 425 struct dhcp_packet *packet = &client->packet; 426 struct option_data options[256]; 427 struct iaddrlist *ap; 428 void (*handler)(struct iaddr, struct option_data *); 429 char *type; 430 int i, options_valid = 1; 431 432 if (packet->hlen > sizeof(packet->chaddr)) { 433 note("Discarding packet with invalid hlen."); 434 return; 435 } 436 437 /* 438 * Silently drop the packet if the client hardware address in the 439 * packet is not the hardware address of the interface being managed. 440 */ 441 if ((ifi->hw_address.hlen != packet->hlen) || 442 (memcmp(ifi->hw_address.haddr, packet->chaddr, packet->hlen))) 443 return; 444 445 memset(options, 0, sizeof(options)); 446 447 if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) { 448 /* Parse the BOOTP/DHCP options field. */ 449 options_valid = parse_option_buffer(options, 450 &packet->options[4], sizeof(packet->options) - 4); 451 452 /* Only DHCP packets have overload areas for options. */ 453 if (options_valid && 454 options[DHO_DHCP_MESSAGE_TYPE].data && 455 options[DHO_DHCP_OPTION_OVERLOAD].data) { 456 if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) 457 options_valid = parse_option_buffer(options, 458 (unsigned char *)packet->file, 459 sizeof(packet->file)); 460 if (options_valid && 461 options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) 462 options_valid = parse_option_buffer(options, 463 (unsigned char *)packet->sname, 464 sizeof(packet->sname)); 465 } 466 } 467 468 type = ""; 469 handler = NULL; 470 471 if (options[DHO_DHCP_MESSAGE_TYPE].data) { 472 /* Always try a DHCP packet, even if a bad option was seen. */ 473 switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) { 474 case DHCPOFFER: 475 handler = dhcpoffer; 476 type = "DHCPOFFER"; 477 break; 478 case DHCPNAK: 479 handler = dhcpnak; 480 type = "DHCPNACK"; 481 break; 482 case DHCPACK: 483 handler = dhcpack; 484 type = "DHCPACK"; 485 break; 486 default: 487 break; 488 } 489 } else if (options_valid && packet->op == BOOTREPLY) { 490 handler = dhcpoffer; 491 type = "BOOTREPLY"; 492 } 493 494 for (ap = config->reject_list; ap && handler; ap = ap->next) 495 if (addr_eq(from, ap->addr)) { 496 note("%s from %s rejected.", type, piaddr(from)); 497 handler = NULL; 498 } 499 500 if (handler) 501 (*handler)(from, options); 502 503 for (i = 0; i < 256; i++) 504 if (options[i].len && options[i].data) 505 free(options[i].data); 506 } 507