xref: /openbsd/usr.sbin/dhcpd/options.c (revision 35318e8f)
1 /*	$OpenBSD: options.c,v 1.35 2017/02/13 22:33:39 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 <sys/types.h>
44 #include <sys/socket.h>
45 
46 #include <net/if.h>
47 
48 #include <netinet/in.h>
49 
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 
54 #include "dhcp.h"
55 #include "tree.h"
56 #include "dhcpd.h"
57 #include "log.h"
58 
59 int bad_options = 0;
60 int bad_options_max = 5;
61 
62 void	parse_options(struct packet *);
63 void	parse_option_buffer(struct packet *, unsigned char *, int);
64 void	create_priority_list(unsigned char *, unsigned char *, int);
65 int	store_option_fragment(unsigned char *, int, unsigned char,
66 	    int, unsigned char *);
67 int	store_options(unsigned char *, int, struct tree_cache **,
68 	    unsigned char *, int, int);
69 
70 
71 /*
72  * Parse all available options out of the specified packet.
73  */
74 void
parse_options(struct packet * packet)75 parse_options(struct packet *packet)
76 {
77 	/* Initially, zero all option pointers. */
78 	memset(packet->options, 0, sizeof(packet->options));
79 
80 	/* If we don't see the magic cookie, there's nothing to parse. */
81 	if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
82 		packet->options_valid = 0;
83 		return;
84 	}
85 
86 	/*
87 	 * Go through the options field, up to the end of the packet or
88 	 * the End field.
89 	 */
90 	parse_option_buffer(packet, &packet->raw->options[4],
91 	    packet->packet_length - DHCP_FIXED_NON_UDP - 4);
92 
93 	/*
94 	 * If we parsed a DHCP Option Overload option, parse more
95 	 * options out of the buffer(s) containing them.
96 	 */
97 	if (packet->options_valid &&
98 	    packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
99 		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
100 			parse_option_buffer(packet,
101 			    (unsigned char *)packet->raw->file,
102 			    sizeof(packet->raw->file));
103 		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
104 			parse_option_buffer(packet,
105 			    (unsigned char *)packet->raw->sname,
106 			    sizeof(packet->raw->sname));
107 	}
108 }
109 
110 /*
111  * Parse options out of the specified buffer, storing addresses of
112  * option values in packet->options and setting packet->options_valid if
113  * no errors are encountered.
114  */
115 void
parse_option_buffer(struct packet * packet,unsigned char * buffer,int length)116 parse_option_buffer(struct packet *packet,
117     unsigned char *buffer, int length)
118 {
119 	unsigned char *s, *t;
120 	unsigned char *end = buffer + length;
121 	int len;
122 	int code;
123 
124 	for (s = buffer; *s != DHO_END && s < end; ) {
125 		code = s[0];
126 
127 		/* Pad options don't have a length - just skip them. */
128 		if (code == DHO_PAD) {
129 			s++;
130 			continue;
131 		}
132 		if (s + 2 > end) {
133 			len = 65536;
134 			goto bogus;
135 		}
136 
137 		/*
138 		 * All other fields (except end, see above) have a
139 		 * one-byte length.
140 		 */
141 		len = s[1];
142 
143 		/*
144 		 * If the length is outrageous, silently skip the rest,
145 		 * and mark the packet bad. Unfortunately some crappy
146 		 * dhcp servers always seem to give us garbage on the
147 		 * end of a packet. so rather than keep refusing, give
148 		 * up and try to take one after seeing a few without
149 		 * anything good.
150 		 */
151 		if (s + len + 2 > end) {
152 		    bogus:
153 			bad_options++;
154 			log_warnx("option %s (%d) %s.",
155 			    dhcp_options[code].name, len,
156 			    "larger than buffer");
157 			if (bad_options == bad_options_max) {
158 				packet->options_valid = 1;
159 				bad_options = 0;
160 				log_warnx("Many bogus options seen in "
161 				    "offers.");
162 				log_warnx("Taking this offer in spite of "
163 				    "bogus");
164 				log_warnx("options - hope for the best!");
165 			} else {
166 				log_warnx("rejecting bogus offer.");
167 				packet->options_valid = 0;
168 			}
169 			return;
170 		}
171 		/*
172 		 * If we haven't seen this option before, just make
173 		 * space for it and copy it there.
174 		 */
175 		if (!packet->options[code].data) {
176 			t = calloc(1, len + 1);
177 			if (!t)
178 				fatalx("Can't allocate storage for option %s.",
179 				    dhcp_options[code].name);
180 			/*
181 			 * Copy and NUL-terminate the option (in case
182 			 * it's an ASCII string).
183 			 */
184 			memcpy(t, &s[2], len);
185 			t[len] = 0;
186 			packet->options[code].len = len;
187 			packet->options[code].data = t;
188 		} else {
189 			/*
190 			 * If it's a repeat, concatenate it to whatever
191 			 * we last saw.   This is really only required
192 			 * for clients, but what the heck...
193 			 */
194 			t = calloc(1, len + packet->options[code].len + 1);
195 			if (!t)
196 				fatalx("Can't expand storage for option %s.",
197 				    dhcp_options[code].name);
198 			memcpy(t, packet->options[code].data,
199 				packet->options[code].len);
200 			memcpy(t + packet->options[code].len,
201 				&s[2], len);
202 			packet->options[code].len += len;
203 			t[packet->options[code].len] = 0;
204 			free(packet->options[code].data);
205 			packet->options[code].data = t;
206 		}
207 		s += len + 2;
208 	}
209 	packet->options_valid = 1;
210 }
211 
212 /*
213  * Fill priority_list with a complete list of DHCP options sorted by
214  * priority. i.e.
215  *     1) Mandatory options.
216  *     2) Options from prl that are not already present.
217  *     3) Options from the default list that are not already present.
218  */
219 void
create_priority_list(unsigned char * priority_list,unsigned char * prl,int prl_len)220 create_priority_list(unsigned char *priority_list, unsigned char *prl,
221     int prl_len)
222 {
223 	unsigned char stored_list[256];
224 	int i, priority_len = 0;
225 
226 	/* clear stored_list, priority_list should be cleared before */
227 	memset(&stored_list, 0, sizeof(stored_list));
228 
229 	/* Some options we don't want on the priority list. */
230 	stored_list[DHO_PAD] = 1;
231 	stored_list[DHO_END] = 1;
232 
233 	/* Mandatory options. */
234 	for(i = 0; dhcp_option_default_priority_list[i] != DHO_END; i++) {
235 		priority_list[priority_len++] =
236 		    dhcp_option_default_priority_list[i];
237 		stored_list[dhcp_option_default_priority_list[i]] = 1;
238 	}
239 
240 	/* Supplied priority list. */
241 	if (!prl)
242 		prl_len = 0;
243 	for(i = 0; i < prl_len; i++) {
244 		/* CLASSLESS routes always have priority, sayeth RFC 3442. */
245 		if (prl[i] == DHO_CLASSLESS_STATIC_ROUTES ||
246 		    prl[i] == DHO_CLASSLESS_MS_STATIC_ROUTES) {
247 			priority_list[priority_len++] = prl[i];
248 			stored_list[prl[i]] = 1;
249 		}
250 	}
251 	for(i = 0; i < prl_len; i++) {
252 		if (stored_list[prl[i]])
253 			continue;
254 		priority_list[priority_len++] = prl[i];
255 		stored_list[prl[i]] = 1;
256 	}
257 
258 	/* Default priority list. */
259 	prl = dhcp_option_default_priority_list;
260 	for(i = 0; i < 256; i++) {
261 		if (stored_list[prl[i]])
262 			continue;
263 		priority_list[priority_len++] = prl[i];
264 		stored_list[prl[i]] = 1;
265 	}
266 }
267 /*
268  * cons options into a big buffer, and then split them out into the
269  * three separate buffers if needed.  This allows us to cons up a set of
270  * vendor options using the same routine.
271  */
272 int
cons_options(struct packet * inpacket,struct dhcp_packet * outpacket,int mms,struct tree_cache ** options,int overload,int terminate,int bootpp,u_int8_t * prl,int prl_len)273 cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
274     int mms, struct tree_cache **options,
275     int overload, /* Overload flags that may be set. */
276     int terminate, int bootpp, u_int8_t *prl, int prl_len)
277 {
278 	unsigned char priority_list[256];
279 	unsigned char buffer[4096];	/* Really big buffer... */
280 	int bufix, main_buffer_size, option_size;
281 
282 	/*
283 	 * If the client has provided a maximum DHCP message size, use
284 	 * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
285 	 * up to the minimum IP MTU size (576 bytes).
286 	 *
287 	 * XXX if a BOOTP client specifies a max message size, we will
288 	 * honor it.
289 	 */
290 	if (!mms &&
291 	    inpacket &&
292 	    inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data &&
293 	    (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >=
294 	    sizeof(u_int16_t))) {
295 		mms = getUShort(
296 		    inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data);
297 	}
298 
299 	if (mms) {
300 		if (mms < 576)
301 			mms = 576;	/* mms must be >= minimum IP MTU */
302 		main_buffer_size = mms - DHCP_FIXED_LEN;
303 	} else if (bootpp)
304 		main_buffer_size = 64;
305 	else
306 		main_buffer_size = 576 - DHCP_FIXED_LEN;
307 
308 	if (main_buffer_size > sizeof(outpacket->options))
309 		main_buffer_size = sizeof(outpacket->options);
310 
311 	/*
312 	 * Initialize the available buffers, some or all of which may not be
313 	 * used.
314 	 */
315 	memset(outpacket->options, DHO_PAD, sizeof(outpacket->options));
316 	if (overload & 1)
317 		memset(outpacket->file, DHO_PAD, DHCP_FILE_LEN);
318 	if (overload & 2)
319 		memset(outpacket->sname, DHO_PAD, DHCP_SNAME_LEN);
320 	if (bootpp)
321 		overload = 0; /* Don't use overload buffers for bootp! */
322 
323 	/*
324 	 * Get complete list of possible options in priority order. Use the
325 	 * list provided in the options. Lacking that use the list provided by
326 	 * prl. If that is not available just use the default list.
327 	 */
328 	memset(&priority_list, 0, sizeof(priority_list));
329 	if (inpacket &&
330 	    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data)
331 		create_priority_list(priority_list,
332 		    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data,
333 		    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len);
334 	else if (prl)
335 		create_priority_list(priority_list, prl, prl_len);
336 	else
337 		create_priority_list(priority_list, NULL, 0);
338 
339 	/*
340 	 * Copy the options into the big buffer, including leading cookie and
341 	 * DHCP_OVERLOAD_OPTION, and DHO_END if it fits. All unused space will
342 	 * be set to DHO_PAD
343 	 */
344 	option_size = store_options(buffer, main_buffer_size, options,
345 	    priority_list, overload, terminate);
346 	if (option_size == 0)
347 		return (DHCP_FIXED_NON_UDP);
348 
349 	/* Copy the main buffer. */
350 	memcpy(&outpacket->options[0], buffer, main_buffer_size);
351 	if (option_size <= main_buffer_size)
352 		return (DHCP_FIXED_NON_UDP + option_size);
353 
354 	/* Copy the overflow buffers. */
355 	bufix = main_buffer_size;
356 	if (overload & 1) {
357 		memcpy(outpacket->file, &buffer[bufix], DHCP_FILE_LEN);
358 		bufix += DHCP_FILE_LEN;
359 	}
360 	if (overload & 2)
361 		memcpy(outpacket->sname, &buffer[bufix], DHCP_SNAME_LEN);
362 
363 	return (DHCP_FIXED_NON_UDP + main_buffer_size);
364 }
365 
366 /*
367  * Store a <code><length><data> fragment in buffer. Return the number of
368  * characters used. Return 0 if no data could be stored.
369  */
370 int
store_option_fragment(unsigned char * buffer,int buffer_size,unsigned char code,int length,unsigned char * data)371 store_option_fragment(unsigned char *buffer, int buffer_size,
372     unsigned char code, int length, unsigned char *data)
373 {
374 	buffer_size -= 2; /* Space for option code and option length. */
375 
376 	if (buffer_size < 1)
377 		return (0);
378 
379 	if (buffer_size > 255)
380 		buffer_size = 255;
381 	if (length > buffer_size)
382 		length = buffer_size;
383 
384 	buffer[0] = code;
385 	buffer[1] = length;
386 
387 	memcpy(&buffer[2], data, length);
388 
389 	return (length + 2);
390 }
391 
392 /*
393  * Store all the requested options into the requested buffer. Insert the
394  * required cookie, DHO_DHCP_OPTION_OVERLOAD options and append a DHO_END if
395  * if fits. Ensure all buffer space is set to DHO_PAD if unused.
396  */
397 int
store_options(unsigned char * buffer,int main_buffer_size,struct tree_cache ** options,unsigned char * priority_list,int overload,int terminate)398 store_options(unsigned char *buffer, int main_buffer_size,
399     struct tree_cache **options, unsigned char *priority_list, int overload,
400     int terminate)
401 {
402 	int buflen, code, cutoff, i, incr, ix, length, optstart, overflow;
403 	int second_cutoff;
404 	int bufix = 0;
405 	int stored_classless = 0;
406 
407 	overload &= 3; /* Only consider valid bits. */
408 
409 	cutoff = main_buffer_size;
410 	second_cutoff = cutoff + ((overload & 1) ? DHCP_FILE_LEN : 0);
411 	buflen = second_cutoff + ((overload & 2) ? DHCP_SNAME_LEN : 0);
412 
413 	memset(buffer, DHO_PAD, buflen);
414 	memcpy(buffer, DHCP_OPTIONS_COOKIE, 4);
415 
416 	if (overload)
417 		bufix = 7; /* Reserve space for DHO_DHCP_OPTION_OVERLOAD. */
418 	else
419 		bufix = 4;
420 
421 	/*
422 	 * Store options in the order they appear in the priority list.
423 	 */
424 	for (i = 0; i < 256; i++) {
425 		/* Code for next option to try to store. */
426 		code = priority_list[i];
427 		if (code == DHO_PAD || code == DHO_END)
428 			continue;
429 
430 		if (!options[code] || !tree_evaluate(options[code]))
431 			continue;
432 
433 		/*
434 		 * RFC 3442 says:
435 		 *
436 		 * When a DHCP client requests the Classless Static
437 		 * Routes option and also requests either or both of the
438 		 * Router option and the Static Routes option, and the
439 		 * DHCP server is sending Classless Static Routes options
440 		 * to that client, the server SHOULD NOT include the
441 		 * Router or Static Routes options.
442 		 */
443 		if ((code == DHO_ROUTERS || code == DHO_STATIC_ROUTES) &&
444 		    stored_classless)
445 			continue;
446 
447 		/* We should now have a constant length for the option. */
448 		length = options[code]->len;
449 
450 		/* Try to store the option. */
451 		optstart = bufix;
452 		ix = 0;
453 		while (length) {
454 			incr = store_option_fragment(&buffer[bufix],
455 			    cutoff - bufix, code, length,
456 			    options[code]->value + ix);
457 
458 			if (incr > 0) {
459 				bufix += incr;
460 				length -= incr - 2;
461 				ix += incr - 2;
462 				continue;
463 			}
464 
465 			/*
466 			 * No fragment could be stored in the space before the
467 			 * cutoff. Fill the unusable space with DHO_PAD and
468 			 * move cutoff for another attempt.
469 			 */
470 			memset(&buffer[bufix], DHO_PAD, cutoff - bufix);
471 			bufix = cutoff;
472 			if (cutoff < second_cutoff)
473 				cutoff = second_cutoff;
474 			else if (cutoff < buflen)
475 				cutoff = buflen;
476 			else
477 				break;
478 		}
479 
480 		if (length > 0) {
481 zapfrags:
482 			memset(&buffer[optstart], DHO_PAD, buflen - optstart);
483 			bufix = optstart;
484 		} else if (terminate && dhcp_options[code].format[0] == 't') {
485 			if (bufix < cutoff)
486 				buffer[bufix++] = '\0';
487 			else
488 				goto zapfrags;
489 		}
490 		if (code == DHO_CLASSLESS_STATIC_ROUTES ||
491 		    code == DHO_CLASSLESS_MS_STATIC_ROUTES)
492 			stored_classless = 1;
493 	}
494 
495 	if (bufix == (4 + (overload ? 3 : 0)))
496 		/* Didn't manage to store any options. */
497 		return (0);
498 
499 	if (bufix < buflen)
500 		buffer[bufix++] = DHO_END;
501 
502 	/* Fill in overload option value based on space used for options. */
503 	if (overload) {
504 		overflow = bufix - main_buffer_size;
505 		if (overflow > 0) {
506 			buffer[4] = DHO_DHCP_OPTION_OVERLOAD;
507 			buffer[5] = 1;
508 			if (overload & 1) {
509 				buffer[6] |= 1;
510 				overflow -= DHCP_FILE_LEN;
511 			}
512 			if ((overload & 2) && overflow > 0)
513 				buffer[6] |= 2;
514 		} else {
515 			/*
516 			 * Compact buffer to eliminate the unused
517 			 * DHO_DHCP_OPTION_OVERLOAD option. Some clients
518 			 * choke on DHO_PAD options there.
519 			 */
520 			memmove(&buffer[4], &buffer[7], buflen - 7);
521 			bufix -= 3;
522 			memset(&buffer[bufix], DHO_PAD, 3);
523 		}
524 	}
525 
526 	return (bufix);
527 }
528 
529 void
do_packet(struct interface_info * interface,struct dhcp_packet * packet,int len,unsigned int from_port,struct iaddr from,struct hardware * hfrom)530 do_packet(struct interface_info *interface, struct dhcp_packet *packet,
531     int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
532 {
533 	struct packet tp;
534 	int i;
535 
536 	if (packet->hlen > sizeof(packet->chaddr)) {
537 		log_info("Discarding packet with invalid hlen.");
538 		return;
539 	}
540 
541 	memset(&tp, 0, sizeof(tp));
542 	tp.raw = packet;
543 	tp.packet_length = len;
544 	tp.client_port = from_port;
545 	tp.client_addr = from;
546 	tp.interface = interface;
547 	tp.haddr = hfrom;
548 
549 	parse_options(&tp);
550 	if (tp.options_valid &&
551 	    tp.options[DHO_DHCP_MESSAGE_TYPE].data)
552 		tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
553 
554 	if (tp.packet_type)
555 		dhcp(&tp, interface->is_udpsock);
556 	else
557 		bootp(&tp);
558 
559 	/* Free the data associated with the options. */
560 	for (i = 0; i < 256; i++)
561 		free(tp.options[i].data);
562 }
563