GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dhcpd/options.c Lines: 0 206 0.0 %
Date: 2017-11-13 Branches: 0 138 0.0 %

Line Branch Exec Source
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
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
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
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
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
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
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
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
}