1 /*	$NetBSD: dhcpleasequery.c,v 1.5 2014/07/12 12:09:38 spz Exp $	*/
2 /*
3  * Copyright (C) 2011-2013 by Internet Systems Consortium, Inc. ("ISC")
4  * Copyright (C) 2006-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/cdefs.h>
20 __RCSID("$NetBSD: dhcpleasequery.c,v 1.5 2014/07/12 12:09:38 spz Exp $");
21 
22 #include "dhcpd.h"
23 
24 /*
25  * TODO: RFC4388 specifies that the server SHOULD return the same
26  *       options it would for a DHCREQUEST message, if no Parameter
27  *       Request List option (option 55) is passed. We do not do that.
28  *
29  * TODO: RFC4388 specifies the creation of a "non-sensitive options"
30  *       configuration list, and that these SHOULD be returned. We
31  *       have no such list.
32  *
33  * TODO: RFC4388 says the server SHOULD use RFC3118, "Authentication
34  *       for DHCP Messages".
35  *
36  * TODO: RFC4388 specifies that you SHOULD insure that you cannot be
37  *       DoS'ed by DHCPLEASEQUERY message.
38  */
39 
40 /*
41  * If you query by hardware address or by client ID, then you may have
42  * more than one IP address for your query argument. We need to do two
43  * things:
44  *
45  *   1. Find the most recent lease.
46  *   2. Find all additional IP addresses for the query argument.
47  *
48  * We do this by looking through all of the leases associated with a
49  * given hardware address or client ID. We use the cltt (client last
50  * transaction time) of the lease, which only has a resolution of one
51  * second, so we might not actually give the very latest IP.
52  */
53 
54 static struct lease*
next_hw(const struct lease * lease)55 next_hw(const struct lease *lease) {
56 	/* INSIST(lease != NULL); */
57 	return lease->n_hw;
58 }
59 
60 static struct lease*
next_uid(const struct lease * lease)61 next_uid(const struct lease *lease) {
62 	/* INSIST(lease != NULL); */
63 	return lease->n_uid;
64 }
65 
66 static void
get_newest_lease(struct lease ** retval,struct lease * lease,struct lease * (* next)(const struct lease *))67 get_newest_lease(struct lease **retval,
68 		 struct lease *lease,
69 		 struct lease *(*next)(const struct lease *)) {
70 
71 	struct lease *p;
72 	struct lease *newest;
73 
74 	/* INSIST(newest != NULL); */
75 	/* INSIST(next != NULL); */
76 
77 	*retval = NULL;
78 
79 	if (lease == NULL) {
80 		return;
81 	}
82 
83 	newest = lease;
84 	for (p=next(lease); p != NULL; p=next(p)) {
85 		if (newest->binding_state == FTS_ACTIVE) {
86 			if ((p->binding_state == FTS_ACTIVE) &&
87 		    	(p->cltt > newest->cltt)) {
88 				newest = p;
89 			}
90 		} else {
91 			if (p->ends > newest->ends) {
92 				newest = p;
93 			}
94 		}
95 	}
96 
97 	lease_reference(retval, newest, MDL);
98 }
99 
100 static int
get_associated_ips(const struct lease * lease,struct lease * (* next)(const struct lease *),const struct lease * newest,u_int32_t * associated_ips,unsigned int associated_ips_size)101 get_associated_ips(const struct lease *lease,
102 		   struct lease *(*next)(const struct lease *),
103 		   const struct lease *newest,
104 		   u_int32_t *associated_ips,
105 		   unsigned int associated_ips_size) {
106 
107 	const struct lease *p;
108 	int cnt;
109 
110 	/* INSIST(next != NULL); */
111 	/* INSIST(associated_ips != NULL); */
112 
113 	if (lease == NULL) {
114 		return 0;
115 	}
116 
117 	cnt = 0;
118 	for (p=lease; p != NULL; p=next(p)) {
119 		if ((p->binding_state == FTS_ACTIVE) && (p != newest)) {
120 			if (cnt < associated_ips_size) {
121 				memcpy(&associated_ips[cnt],
122 				       p->ip_addr.iabuf,
123 				       sizeof(associated_ips[cnt]));
124 			}
125 			cnt++;
126 		}
127 	}
128 	return cnt;
129 }
130 
131 
132 void
dhcpleasequery(struct packet * packet,int ms_nulltp)133 dhcpleasequery(struct packet *packet, int ms_nulltp) {
134 	char msgbuf[256];
135 	char dbg_info[128];
136 	struct iaddr cip;
137 	struct iaddr gip;
138 	struct data_string uid;
139 	struct hardware h;
140 	struct lease *tmp_lease;
141 	struct lease *lease;
142 	int want_associated_ip;
143 	int assoc_ip_cnt;
144 	u_int32_t assoc_ips[40];  /* XXXSK: arbitrary maximum number of IPs */
145 	const int nassoc_ips = sizeof(assoc_ips) / sizeof(assoc_ips[0]);
146 
147 	unsigned char dhcpMsgType;
148 	const char *dhcp_msg_type_name;
149 	struct subnet *subnet;
150 	struct group *relay_group;
151 	struct option_state *options;
152 	struct option_cache *oc;
153 	int allow_leasequery;
154 	int ignorep;
155 	u_int32_t lease_duration;
156 	u_int32_t time_renewal;
157 	u_int32_t time_rebinding;
158 	u_int32_t time_expiry;
159 	u_int32_t client_last_transaction_time;
160 	struct sockaddr_in to;
161 	struct in_addr siaddr;
162 	struct data_string prl;
163 	struct data_string *prl_ptr;
164 
165 	int i;
166 	struct interface_info *interface;
167 
168 	/* INSIST(packet != NULL); */
169 
170 	/*
171 	 * Prepare log information.
172 	 */
173 	snprintf(msgbuf, sizeof(msgbuf),
174 		"DHCPLEASEQUERY from %s", inet_ntoa(packet->raw->giaddr));
175 
176 	/*
177 	 * We can't reply if there is no giaddr field.
178 	 */
179 	if (!packet->raw->giaddr.s_addr) {
180 		log_info("%s: missing giaddr, ciaddr is %s, no reply sent",
181 			 msgbuf, inet_ntoa(packet->raw->ciaddr));
182 		return;
183 	}
184 
185 	/*
186 	 * Initially we use the 'giaddr' subnet options scope to determine if
187 	 * the giaddr-identified relay agent is permitted to perform a
188 	 * leasequery.  The subnet is not required, and may be omitted, in
189 	 * which case we are essentially interrogating the root options class
190 	 * to find a globally permit.
191 	 */
192 	gip.len = sizeof(packet->raw->giaddr);
193 	memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr));
194 
195 	subnet = NULL;
196 	find_subnet(&subnet, gip, MDL);
197 	if (subnet != NULL)
198 		relay_group = subnet->group;
199 	else
200 		relay_group = root_group;
201 
202 	subnet_dereference(&subnet, MDL);
203 
204 	options = NULL;
205 	if (!option_state_allocate(&options, MDL)) {
206 		log_error("No memory for option state.");
207 		log_info("%s: out of memory, no reply sent", msgbuf);
208 		return;
209 	}
210 
211 	execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options,
212 				    options, &global_scope, relay_group,
213 				    NULL, NULL);
214 
215 	for (i=packet->class_count-1; i>=0; i--) {
216 		execute_statements_in_scope(NULL, packet, NULL, NULL,
217 					    packet->options, options,
218 					    &global_scope,
219 					    packet->classes[i]->group,
220 					    relay_group, NULL);
221 	}
222 
223 	/*
224 	 * Because LEASEQUERY has some privacy concerns, default to deny.
225 	 */
226 	allow_leasequery = 0;
227 
228 	/*
229 	 * See if we are authorized to do LEASEQUERY.
230 	 */
231 	oc = lookup_option(&server_universe, options, SV_LEASEQUERY);
232 	if (oc != NULL) {
233 		allow_leasequery = evaluate_boolean_option_cache(&ignorep,
234 					 packet, NULL, NULL, packet->options,
235 					 options, &global_scope, oc, MDL);
236 	}
237 
238 	if (!allow_leasequery) {
239 		log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf);
240 		option_state_dereference(&options, MDL);
241 		return;
242 	}
243 
244 
245 	/*
246 	 * Copy out the client IP address.
247 	 */
248 	cip.len = sizeof(packet->raw->ciaddr);
249 	memcpy(cip.iabuf, &packet->raw->ciaddr, sizeof(packet->raw->ciaddr));
250 
251 	/*
252 	 * If the client IP address is valid (not all zero), then we
253 	 * are looking for information about that IP address.
254 	 */
255 	assoc_ip_cnt = 0;
256 	lease = tmp_lease = NULL;
257 	if (memcmp(cip.iabuf, "\0\0\0", 4)) {
258 
259 		want_associated_ip = 0;
260 
261 		snprintf(dbg_info, sizeof(dbg_info), "IP %s", piaddr(cip));
262 		find_lease_by_ip_addr(&lease, cip, MDL);
263 
264 
265 	} else {
266 
267 		want_associated_ip = 1;
268 
269 		/*
270 		 * If the client IP address is all zero, then we will
271 		 * either look up by the client identifier (if we have
272 		 * one), or by the MAC address.
273 		 */
274 
275 		memset(&uid, 0, sizeof(uid));
276 		if (get_option(&uid,
277 			       &dhcp_universe,
278 			       packet,
279 			       NULL,
280 			       NULL,
281 			       packet->options,
282 			       NULL,
283 			       packet->options,
284 			       &global_scope,
285 			       DHO_DHCP_CLIENT_IDENTIFIER,
286 			       MDL)) {
287 
288 			snprintf(dbg_info,
289 				 sizeof(dbg_info),
290 				 "client-id %s",
291 				 print_hex_1(uid.len, uid.data, 60));
292 
293 			find_lease_by_uid(&tmp_lease, uid.data, uid.len, MDL);
294 			data_string_forget(&uid, MDL);
295 			get_newest_lease(&lease, tmp_lease, next_uid);
296 			assoc_ip_cnt = get_associated_ips(tmp_lease,
297 							  next_uid,
298 							  lease,
299 							  assoc_ips,
300 							  nassoc_ips);
301 
302 		} else {
303 
304 			if (packet->raw->hlen+1 > sizeof(h.hbuf)) {
305 				log_info("%s: hardware length too long, "
306 					 "no reply sent", msgbuf);
307 				option_state_dereference(&options, MDL);
308 				return;
309 			}
310 
311 			h.hlen = packet->raw->hlen + 1;
312 			h.hbuf[0] = packet->raw->htype;
313 			memcpy(&h.hbuf[1],
314 			       packet->raw->chaddr,
315 			       packet->raw->hlen);
316 
317 			snprintf(dbg_info,
318 				 sizeof(dbg_info),
319 				 "MAC address %s",
320 				 print_hw_addr(h.hbuf[0],
321 					       h.hlen - 1,
322 					       &h.hbuf[1]));
323 
324 			find_lease_by_hw_addr(&tmp_lease, h.hbuf, h.hlen, MDL);
325 			get_newest_lease(&lease, tmp_lease, next_hw);
326 			assoc_ip_cnt = get_associated_ips(tmp_lease,
327 							  next_hw,
328 							  lease,
329 							  assoc_ips,
330 							  nassoc_ips);
331 
332 		}
333 
334 		lease_dereference(&tmp_lease, MDL);
335 
336 		if (lease != NULL) {
337 			memcpy(&packet->raw->ciaddr,
338 			       lease->ip_addr.iabuf,
339 			       sizeof(packet->raw->ciaddr));
340 		}
341 
342 		/*
343 		 * Log if we have too many IP addresses associated
344 		 * with this client.
345 		 */
346 		if (want_associated_ip && (assoc_ip_cnt > nassoc_ips)) {
347 			log_info("%d IP addresses associated with %s, "
348 				 "only %d sent in reply.",
349 				 assoc_ip_cnt, dbg_info, nassoc_ips);
350 		}
351 	}
352 
353 	/*
354 	 * We now know the query target too, so can report this in
355 	 * our log message.
356 	 */
357 	snprintf(msgbuf, sizeof(msgbuf),
358 		"DHCPLEASEQUERY from %s for %s",
359 		inet_ntoa(packet->raw->giaddr), dbg_info);
360 
361 	/*
362 	 * Figure our our return type.
363 	 */
364 	if (lease == NULL) {
365 		dhcpMsgType = DHCPLEASEUNKNOWN;
366 		dhcp_msg_type_name = "DHCPLEASEUNKNOWN";
367 	} else {
368 		if (lease->binding_state == FTS_ACTIVE) {
369 			dhcpMsgType = DHCPLEASEACTIVE;
370 			dhcp_msg_type_name = "DHCPLEASEACTIVE";
371 		} else {
372 			dhcpMsgType = DHCPLEASEUNASSIGNED;
373 			dhcp_msg_type_name = "DHCPLEASEUNASSIGNED";
374 		}
375 	}
376 
377 	/*
378 	 * Set options that only make sense if we have an active lease.
379 	 */
380 
381 	if (dhcpMsgType == DHCPLEASEACTIVE)
382 	{
383 		/*
384 		 * RFC 4388 uses the PRL to request options for the agent to
385 		 * receive that are "about" the client.  It is confusing
386 		 * because in some cases it wants to know what was sent to
387 		 * the client (lease times, adjusted), and in others it wants
388 		 * to know information the client sent.  You're supposed to
389 		 * know this on a case-by-case basis.
390 		 *
391 		 * "Name servers", "domain name", and the like from the relay
392 		 * agent's scope seems less than useful.  Our options are to
393 		 * restart the option cache from the lease's best point of view
394 		 * (execute statements from the lease pool's group), or to
395 		 * simply restart the option cache from empty.
396 		 *
397 		 * I think restarting the option cache from empty best
398 		 * approaches RFC 4388's intent; specific options are included.
399 		 */
400 		option_state_dereference(&options, MDL);
401 
402 		if (!option_state_allocate(&options, MDL)) {
403 			log_error("%s: out of memory, no reply sent", msgbuf);
404 			lease_dereference(&lease, MDL);
405 			return;
406 		}
407 
408 		/*
409 		 * Set the hardware address fields.
410 		 */
411 
412 		packet->raw->hlen = lease->hardware_addr.hlen - 1;
413 		packet->raw->htype = lease->hardware_addr.hbuf[0];
414 		memcpy(packet->raw->chaddr,
415 		       &lease->hardware_addr.hbuf[1],
416 		       sizeof(packet->raw->chaddr));
417 
418 		/*
419 		 * Set client identifier option.
420 		 */
421 		if (lease->uid_len > 0) {
422 			if (!add_option(options,
423 					DHO_DHCP_CLIENT_IDENTIFIER,
424 					lease->uid,
425 					lease->uid_len)) {
426 				option_state_dereference(&options, MDL);
427 				lease_dereference(&lease, MDL);
428 				log_info("%s: out of memory, no reply sent",
429 					 msgbuf);
430 				return;
431 			}
432 		}
433 
434 
435 		/*
436 		 * Calculate T1 and T2, the times when the client
437 		 * tries to extend its lease on its networking
438 		 * address.
439 		 * These seem to be hard-coded in ISC DHCP, to 0.5 and
440 		 * 0.875 of the lease time.
441 		 */
442 
443 		lease_duration = lease->ends - lease->starts;
444 		time_renewal = lease->starts +
445 			(lease_duration / 2);
446 		time_rebinding = lease->starts +
447 			(lease_duration / 2) +
448 			(lease_duration / 4) +
449 			(lease_duration / 8);
450 
451 		if (time_renewal > cur_time) {
452 			time_renewal = htonl(time_renewal - cur_time);
453 
454 			if (!add_option(options,
455 					DHO_DHCP_RENEWAL_TIME,
456 					&time_renewal,
457 					sizeof(time_renewal))) {
458 				option_state_dereference(&options, MDL);
459 				lease_dereference(&lease, MDL);
460 				log_info("%s: out of memory, no reply sent",
461 					 msgbuf);
462 				return;
463 			}
464 		}
465 
466 		if (time_rebinding > cur_time) {
467 			time_rebinding = htonl(time_rebinding - cur_time);
468 
469 			if (!add_option(options,
470 					DHO_DHCP_REBINDING_TIME,
471 					&time_rebinding,
472 					sizeof(time_rebinding))) {
473 				option_state_dereference(&options, MDL);
474 				lease_dereference(&lease, MDL);
475 				log_info("%s: out of memory, no reply sent",
476 					 msgbuf);
477 				return;
478 			}
479 		}
480 
481 		if (lease->ends > cur_time) {
482 			time_expiry = htonl(lease->ends - cur_time);
483 
484 			if (!add_option(options,
485 					DHO_DHCP_LEASE_TIME,
486 					&time_expiry,
487 					sizeof(time_expiry))) {
488 				option_state_dereference(&options, MDL);
489 				lease_dereference(&lease, MDL);
490 				log_info("%s: out of memory, no reply sent",
491 					 msgbuf);
492 				return;
493 			}
494 		}
495 
496 		/* Supply the Vendor-Class-Identifier. */
497 		if (lease->scope != NULL) {
498 			struct data_string vendor_class;
499 
500 			memset(&vendor_class, 0, sizeof(vendor_class));
501 
502 			if (find_bound_string(&vendor_class, lease->scope,
503 					      "vendor-class-identifier")) {
504 				if (!add_option(options,
505 						DHO_VENDOR_CLASS_IDENTIFIER,
506 						(void *)vendor_class.data,
507 						vendor_class.len)) {
508 					option_state_dereference(&options,
509 								 MDL);
510 					lease_dereference(&lease, MDL);
511 					log_error("%s: error adding vendor "
512 						  "class identifier, no reply "
513 						  "sent", msgbuf);
514 					data_string_forget(&vendor_class, MDL);
515 					return;
516 				}
517 				data_string_forget(&vendor_class, MDL);
518 			}
519 		}
520 
521 		/*
522 		 * Set the relay agent info.
523 		 *
524 		 * Note that because agent info is appended without regard
525 		 * to the PRL in cons_options(), this will be sent as the
526 		 * last option in the packet whether it is listed on PRL or
527 		 * not.
528 		 */
529 
530 		if (lease->agent_options != NULL) {
531 			int idx = agent_universe.index;
532 			struct option_chain_head **tmp1 =
533 				(struct option_chain_head **)
534 				&(options->universes[idx]);
535 				struct option_chain_head *tmp2 =
536 				(struct option_chain_head *)
537 				lease->agent_options;
538 
539 			option_chain_head_reference(tmp1, tmp2, MDL);
540 		}
541 
542 		/*
543 	 	 * Set the client last transaction time.
544 		 * We check to make sure we have a timestamp. For
545 		 * lease files that were saved before running a
546 		 * timestamp-aware version of the server, this may
547 		 * not be set.
548 	 	 */
549 
550 		if (lease->cltt != MIN_TIME) {
551 			if (cur_time > lease->cltt) {
552 				client_last_transaction_time =
553 					htonl(cur_time - lease->cltt);
554 			} else {
555 				client_last_transaction_time = htonl(0);
556 			}
557 			if (!add_option(options,
558 					DHO_CLIENT_LAST_TRANSACTION_TIME,
559 					&client_last_transaction_time,
560 		     			sizeof(client_last_transaction_time))) {
561 				option_state_dereference(&options, MDL);
562 				lease_dereference(&lease, MDL);
563 				log_info("%s: out of memory, no reply sent",
564 					 msgbuf);
565 				return;
566 			}
567 		}
568 
569 		/*
570 	 	 * Set associated IPs, if requested and there are some.
571 	 	 */
572 		if (want_associated_ip && (assoc_ip_cnt > 0)) {
573 			if (!add_option(options,
574 					DHO_ASSOCIATED_IP,
575 					assoc_ips,
576 					assoc_ip_cnt * sizeof(assoc_ips[0]))) {
577 				option_state_dereference(&options, MDL);
578 				lease_dereference(&lease, MDL);
579 				log_info("%s: out of memory, no reply sent",
580 					 msgbuf);
581 				return;
582 			}
583 		}
584 	}
585 
586 	/*
587 	 * Set the message type.
588 	 */
589 
590 	packet->raw->op = BOOTREPLY;
591 
592 	/*
593 	 * Set DHCP message type.
594 	 */
595 	if (!add_option(options,
596 		        DHO_DHCP_MESSAGE_TYPE,
597 		        &dhcpMsgType,
598 			sizeof(dhcpMsgType))) {
599 		option_state_dereference(&options, MDL);
600 		lease_dereference(&lease, MDL);
601 		log_info("%s: error adding option, no reply sent", msgbuf);
602 		return;
603 	}
604 
605 	/*
606 	 * Log the message we've received.
607 	 */
608 	log_info("%s", msgbuf);
609 
610 	/*
611 	 * Figure out which address to use to send from.
612 	 */
613 	get_server_source_address(&siaddr, options, options, packet);
614 
615 	/*
616 	 * Set up the option buffer.
617 	 */
618 
619 	memset(&prl, 0, sizeof(prl));
620 	oc = lookup_option(&dhcp_universe, options,
621 			   DHO_DHCP_PARAMETER_REQUEST_LIST);
622 	if (oc != NULL) {
623 		evaluate_option_cache(&prl,
624 				      packet,
625 				      NULL,
626 				      NULL,
627 				      packet->options,
628 				      options,
629 				      &global_scope,
630 				      oc,
631 				      MDL);
632 	}
633 	if (prl.len > 0) {
634 		prl_ptr = &prl;
635 	} else {
636 		prl_ptr = NULL;
637 	}
638 
639 	packet->packet_length = cons_options(packet,
640 					     packet->raw,
641 					     lease,
642 					     NULL,
643 					     0,
644 					     packet->options,
645 					     options,
646 					     &global_scope,
647 					     0,
648 					     0,
649 					     0,
650 					     prl_ptr,
651 					     NULL);
652 
653 	data_string_forget(&prl, MDL);	/* SK: safe, even if empty */
654 	option_state_dereference(&options, MDL);
655 	lease_dereference(&lease, MDL);
656 
657 	to.sin_family = AF_INET;
658 #ifdef HAVE_SA_LEN
659 	to.sin_len = sizeof(to);
660 #endif
661 	memset(to.sin_zero, 0, sizeof(to.sin_zero));
662 
663 	/*
664 	 * Leasequery packets are be sent to the gateway address.
665 	 */
666 	to.sin_addr = packet->raw->giaddr;
667 	if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) {
668 		to.sin_port = local_port;
669 	} else {
670 		to.sin_port = remote_port; /* XXXSK: For debugging. */
671 	}
672 
673 	/*
674 	 * The fallback_interface lets us send with a real IP
675 	 * address. The packet interface sends from all-zeros.
676 	 */
677 	if (fallback_interface != NULL) {
678 		interface = fallback_interface;
679 	} else {
680 		interface = packet->interface;
681 	}
682 
683 	/*
684 	 * Report what we're sending.
685 	 */
686 	log_info("%s to %s for %s (%d associated IPs)",
687 		dhcp_msg_type_name,
688 		inet_ntoa(to.sin_addr), dbg_info, assoc_ip_cnt);
689 
690 	send_packet(interface,
691 		    NULL,
692 		    packet->raw,
693 		    packet->packet_length,
694 		    siaddr,
695 		    &to,
696 		    NULL);
697 }
698 
699 #ifdef DHCPv6
700 
701 /*
702  * TODO: RFC5007 query-by-clientid.
703  *
704  * TODO: RFC5007 look at the pools according to the link-address.
705  *
706  * TODO: get fixed leases too.
707  *
708  * TODO: RFC5007 ORO in query-options.
709  *
710  * TODO: RFC5007 lq-relay-data.
711  *
712  * TODO: RFC5007 lq-client-link.
713  *
714  * Note: the code is still nearly compliant and usable for the target
715  * case with these missing features!
716  */
717 
718 /*
719  * The structure to handle a leasequery.
720  */
721 struct lq6_state {
722 	struct packet *packet;
723 	struct data_string client_id;
724 	struct data_string server_id;
725 	struct data_string lq_query;
726 	uint8_t query_type;
727 	struct in6_addr link_addr;
728 	struct option_state *query_opts;
729 
730 	struct option_state *reply_opts;
731 	unsigned cursor;
732 	union reply_buffer {
733 		unsigned char data[65536];
734 		struct dhcpv6_packet reply;
735 	} buf;
736 };
737 
738 /*
739  * Options that we want to send.
740  */
741 static const int required_opts_lq[] = {
742 	D6O_CLIENTID,
743 	D6O_SERVERID,
744 	D6O_STATUS_CODE,
745 	D6O_CLIENT_DATA,
746 	D6O_LQ_RELAY_DATA,
747 	D6O_LQ_CLIENT_LINK,
748 	0
749 };
750 static const int required_opt_CLIENT_DATA[] = {
751 	D6O_CLIENTID,
752 	D6O_IAADDR,
753 	D6O_IAPREFIX,
754 	D6O_CLT_TIME,
755 	0
756 };
757 
758 /*
759  * Get the lq-query option from the packet.
760  */
761 static isc_result_t
get_lq_query(struct lq6_state * lq)762 get_lq_query(struct lq6_state *lq)
763 {
764 	struct data_string *lq_query = &lq->lq_query;
765 	struct packet *packet = lq->packet;
766 	struct option_cache *oc;
767 
768 	/*
769 	 * Verify our lq_query structure is empty.
770 	 */
771 	if ((lq_query->data != NULL) || (lq_query->len != 0)) {
772 		return DHCP_R_INVALIDARG;
773 	}
774 
775 	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_LQ_QUERY);
776 	if (oc == NULL) {
777 		return ISC_R_NOTFOUND;
778 	}
779 
780 	if (!evaluate_option_cache(lq_query, packet, NULL, NULL,
781 				   packet->options, NULL,
782 				   &global_scope, oc, MDL)) {
783 		return ISC_R_FAILURE;
784 	}
785 
786 	return ISC_R_SUCCESS;
787 }
788 
789 /*
790  * Message validation, RFC 5007 section 4.2.1:
791  *  dhcpv6.c:valid_client_msg() - unicast + lq-query option.
792  */
793 static int
valid_query_msg(struct lq6_state * lq)794 valid_query_msg(struct lq6_state *lq) {
795 	struct packet *packet = lq->packet;
796 	int ret_val = 0;
797 	struct option_cache *oc;
798 
799 	/* INSIST((lq != NULL) || (packet != NULL)); */
800 
801 	switch (get_client_id(packet, &lq->client_id)) {
802 		case ISC_R_SUCCESS:
803 			break;
804 		case ISC_R_NOTFOUND:
805 			log_debug("Discarding %s from %s; "
806 				  "client identifier missing",
807 				  dhcpv6_type_names[packet->dhcpv6_msg_type],
808 				  piaddr(packet->client_addr));
809 			goto exit;
810 		default:
811 			log_error("Error processing %s from %s; "
812 				  "unable to evaluate Client Identifier",
813 				  dhcpv6_type_names[packet->dhcpv6_msg_type],
814 				  piaddr(packet->client_addr));
815 			goto exit;
816 	}
817 
818 	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
819 	if (oc != NULL) {
820 		if (evaluate_option_cache(&lq->server_id, packet, NULL, NULL,
821 					  packet->options, NULL,
822 					  &global_scope, oc, MDL)) {
823 			log_debug("Discarding %s from %s; "
824 				  "server identifier found "
825 				  "(CLIENTID %s, SERVERID %s)",
826 				  dhcpv6_type_names[packet->dhcpv6_msg_type],
827 				  piaddr(packet->client_addr),
828 				  print_hex_1(lq->client_id.len,
829 				  	      lq->client_id.data, 60),
830 				  print_hex_2(lq->server_id.len,
831 				  	      lq->server_id.data, 60));
832 		} else {
833 			log_debug("Discarding %s from %s; "
834 				  "server identifier found "
835 				  "(CLIENTID %s)",
836 				  dhcpv6_type_names[packet->dhcpv6_msg_type],
837 				  print_hex_1(lq->client_id.len,
838 				  	      lq->client_id.data, 60),
839 				  piaddr(packet->client_addr));
840 		}
841 		goto exit;
842 	}
843 
844 	switch (get_lq_query(lq)) {
845 		case ISC_R_SUCCESS:
846 			break;
847 		case ISC_R_NOTFOUND:
848 			log_debug("Discarding %s from %s; lq-query missing",
849 				  dhcpv6_type_names[packet->dhcpv6_msg_type],
850 				  piaddr(packet->client_addr));
851 			goto exit;
852 		default:
853 			log_error("Error processing %s from %s; "
854 				  "unable to evaluate LQ-Query",
855 				  dhcpv6_type_names[packet->dhcpv6_msg_type],
856 				  piaddr(packet->client_addr));
857 			goto exit;
858 	}
859 
860 	/* looks good */
861 	ret_val = 1;
862 
863 exit:
864 	if (!ret_val) {
865 		if (lq->client_id.len > 0) {
866 			data_string_forget(&lq->client_id, MDL);
867 		}
868 		if (lq->server_id.len > 0) {
869 			data_string_forget(&lq->server_id, MDL);
870 		}
871 		if (lq->lq_query.len > 0) {
872 			data_string_forget(&lq->lq_query, MDL);
873 		}
874 	}
875 	return ret_val;
876 }
877 
878 /*
879  * Set an error in a status-code option (from set_status_code).
880  */
881 static int
set_error(struct lq6_state * lq,u_int16_t code,const char * message)882 set_error(struct lq6_state *lq, u_int16_t code, const char *message) {
883 	struct data_string d;
884 	int ret_val;
885 
886 	memset(&d, 0, sizeof(d));
887 	d.len = sizeof(code) + strlen(message);
888 	if (!buffer_allocate(&d.buffer, d.len, MDL)) {
889 		log_fatal("set_error: no memory for status code.");
890 	}
891 	d.data = d.buffer->data;
892 	putUShort(d.buffer->data, code);
893 	memcpy(d.buffer->data + sizeof(code), message, d.len - sizeof(code));
894 	if (!save_option_buffer(&dhcpv6_universe, lq->reply_opts,
895 				d.buffer, (unsigned char *)d.data, d.len,
896 				D6O_STATUS_CODE, 0)) {
897 		log_error("set_error: error saving status code.");
898 		ret_val = 0;
899 	} else {
900 		ret_val = 1;
901 	}
902 	data_string_forget(&d, MDL);
903 	return ret_val;
904 }
905 
906 /*
907  * Process a by-address lease query.
908  */
909 static int
process_lq_by_address(struct lq6_state * lq)910 process_lq_by_address(struct lq6_state *lq) {
911 	struct packet *packet = lq->packet;
912 	struct option_cache *oc;
913 	struct ipv6_pool *pool = NULL;
914 	struct data_string data;
915 	struct in6_addr addr;
916 	struct iasubopt *iaaddr = NULL;
917 	struct option_state *opt_state = NULL;
918 	u_int32_t lifetime;
919 	unsigned opt_cursor;
920 	int ret_val = 0;
921 
922 	/*
923 	 * Get the IAADDR.
924 	 */
925 	oc = lookup_option(&dhcpv6_universe, lq->query_opts, D6O_IAADDR);
926 	if (oc == NULL) {
927 		if (!set_error(lq, STATUS_MalformedQuery,
928 			       "No OPTION_IAADDR.")) {
929 			log_error("process_lq_by_address: unable "
930 				  "to set MalformedQuery status code.");
931 			return 0;
932 		}
933 		return 1;
934 	}
935 	memset(&data, 0, sizeof(data));
936 	if (!evaluate_option_cache(&data, packet,
937 				   NULL, NULL,
938 				   lq->query_opts, NULL,
939 				   &global_scope, oc, MDL) ||
940 	    (data.len < IAADDR_OFFSET)) {
941 		log_error("process_lq_by_address: error evaluating IAADDR.");
942 		goto exit;
943 	}
944 	memcpy(&addr, data.data, sizeof(addr));
945 	data_string_forget(&data, MDL);
946 
947 	/*
948 	 * Find the lease.
949 	 * Note the RFC 5007 says to use the link-address to find the link
950 	 * or the ia-aadr when it is :: but in any case the ia-addr has
951 	 * to be on the link, so we ignore the link-address here.
952 	 */
953 	if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
954 		if (!set_error(lq, STATUS_NotConfigured,
955 			       "Address not in a pool.")) {
956 			log_error("process_lq_by_address: unable "
957 				  "to set NotConfigured status code.");
958 			goto exit;
959 		}
960 		ret_val = 1;
961 		goto exit;
962 	}
963 	if (iasubopt_hash_lookup(&iaaddr, pool->leases, &addr,
964 				 sizeof(addr), MDL) == 0) {
965 		ret_val = 1;
966 		goto exit;
967 	}
968 	if ((iaaddr == NULL) || (iaaddr->state != FTS_ACTIVE) ||
969 	    (iaaddr->ia == NULL) || (iaaddr->ia->iaid_duid.len <= 4)) {
970 		ret_val = 1;
971 		goto exit;
972 	}
973 
974 	/*
975 	 * Build the client-data option (with client-id, ia-addr and clt-time).
976 	 */
977 	if (!option_state_allocate(&opt_state, MDL)) {
978 		log_error("process_lq_by_address: "
979 			  "no memory for option state.");
980 		goto exit;
981 	}
982 
983 	data_string_copy(&data, &iaaddr->ia->iaid_duid, MDL);
984 	data.data += 4;
985 	data.len -= 4;
986 	if (!save_option_buffer(&dhcpv6_universe, opt_state,
987 				NULL, (unsigned char *)data.data, data.len,
988 				D6O_CLIENTID, 0)) {
989 		log_error("process_lq_by_address: error saving client ID.");
990 		goto exit;
991 	}
992 	data_string_forget(&data, MDL);
993 
994 	data.len = IAADDR_OFFSET;
995 	if (!buffer_allocate(&data.buffer, data.len, MDL)) {
996 		log_error("process_lq_by_address: no memory for ia-addr.");
997 		goto exit;
998 	}
999 	data.data = data.buffer->data;
1000 	memcpy(data.buffer->data, &iaaddr->addr, 16);
1001 	lifetime = iaaddr->prefer;
1002 	putULong(data.buffer->data + 16, lifetime);
1003 	lifetime = iaaddr->valid;
1004 	putULong(data.buffer->data + 20, lifetime);
1005 	if (!save_option_buffer(&dhcpv6_universe, opt_state,
1006 				NULL, (unsigned char *)data.data, data.len,
1007 				D6O_IAADDR, 0)) {
1008 		log_error("process_lq_by_address: error saving ia-addr.");
1009 		goto exit;
1010 	}
1011 	data_string_forget(&data, MDL);
1012 
1013 	lifetime = htonl(iaaddr->ia->cltt);
1014 	if (!save_option_buffer(&dhcpv6_universe, opt_state,
1015 				NULL, (unsigned char *)&lifetime, 4,
1016 				D6O_CLT_TIME, 0)) {
1017 		log_error("process_lq_by_address: error saving clt time.");
1018 		goto exit;
1019 	}
1020 
1021 	/*
1022 	 * Store the client-data option.
1023 	 */
1024 	opt_cursor = lq->cursor;
1025 	putUShort(lq->buf.data + lq->cursor, (unsigned)D6O_CLIENT_DATA);
1026 	lq->cursor += 2;
1027 	/* Skip option length. */
1028 	lq->cursor += 2;
1029 
1030 	lq->cursor += store_options6((char *)lq->buf.data + lq->cursor,
1031 				     sizeof(lq->buf) - lq->cursor,
1032 				     opt_state, lq->packet,
1033 				     required_opt_CLIENT_DATA, NULL);
1034 	/* Reset the length. */
1035 	putUShort(lq->buf.data + opt_cursor + 2,
1036 		  lq->cursor - (opt_cursor + 4));
1037 
1038 	/* Done. */
1039 	ret_val = 1;
1040 
1041      exit:
1042 	if (data.data != NULL)
1043 		data_string_forget(&data, MDL);
1044 	if (pool != NULL)
1045 		ipv6_pool_dereference(&pool, MDL);
1046 	if (iaaddr != NULL)
1047 		iasubopt_dereference(&iaaddr, MDL);
1048 	if (opt_state != NULL)
1049 		option_state_dereference(&opt_state, MDL);
1050 	return ret_val;
1051 }
1052 
1053 
1054 /*
1055  * Process a lease query.
1056  */
1057 void
dhcpv6_leasequery(struct data_string * reply_ret,struct packet * packet)1058 dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
1059 	static struct lq6_state lq;
1060 	struct option_cache *oc;
1061 	int allow_lq;
1062 
1063 	/*
1064 	 * Initialize the lease query state.
1065 	 */
1066 	lq.packet = NULL;
1067 	memset(&lq.client_id, 0, sizeof(lq.client_id));
1068 	memset(&lq.server_id, 0, sizeof(lq.server_id));
1069 	memset(&lq.lq_query, 0, sizeof(lq.lq_query));
1070 	lq.query_opts = NULL;
1071 	lq.reply_opts = NULL;
1072 	packet_reference(&lq.packet, packet, MDL);
1073 
1074 	/*
1075 	 * Validate our input.
1076 	 */
1077 	if (!valid_query_msg(&lq)) {
1078 		goto exit;
1079 	}
1080 
1081 	/*
1082 	 * Prepare our reply.
1083 	 */
1084 	if (!option_state_allocate(&lq.reply_opts, MDL)) {
1085 		log_error("dhcpv6_leasequery: no memory for option state.");
1086 		goto exit;
1087 	}
1088 	execute_statements_in_scope(NULL, lq.packet, NULL, NULL,
1089 				    lq.packet->options, lq.reply_opts,
1090 				    &global_scope, root_group, NULL, NULL);
1091 
1092 	lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY;
1093 
1094 	memcpy(lq.buf.reply.transaction_id,
1095 	       lq.packet->dhcpv6_transaction_id,
1096 	       sizeof(lq.buf.reply.transaction_id));
1097 
1098 	/*
1099 	 * Because LEASEQUERY has some privacy concerns, default to deny.
1100 	 */
1101 	allow_lq = 0;
1102 
1103 	/*
1104 	 * See if we are authorized to do LEASEQUERY.
1105 	 */
1106 	oc = lookup_option(&server_universe, lq.reply_opts, SV_LEASEQUERY);
1107 	if (oc != NULL) {
1108 		allow_lq = evaluate_boolean_option_cache(NULL,
1109 							 lq.packet,
1110 							 NULL, NULL,
1111 							 lq.packet->options,
1112 							 lq.reply_opts,
1113 							 &global_scope,
1114 							 oc, MDL);
1115 	}
1116 
1117 	if (!allow_lq) {
1118 		log_info("dhcpv6_leasequery: not allowed, query ignored.");
1119 		goto exit;
1120 	}
1121 
1122 	/*
1123 	 * Same than transmission of REPLY message in RFC 3315:
1124 	 *  server-id
1125 	 *  client-id
1126 	 */
1127 
1128 	oc = lookup_option(&dhcpv6_universe, lq.reply_opts, D6O_SERVERID);
1129 	if (oc == NULL) {
1130 		/* If not already in options, get from query then global. */
1131 		if (lq.server_id.data == NULL)
1132 			copy_server_duid(&lq.server_id, MDL);
1133 		if (!save_option_buffer(&dhcpv6_universe,
1134 					lq.reply_opts,
1135 					NULL,
1136 					(unsigned char *)lq.server_id.data,
1137 					lq.server_id.len,
1138 					D6O_SERVERID,
1139 					0)) {
1140 			log_error("dhcpv6_leasequery: "
1141 				  "error saving server identifier.");
1142 			goto exit;
1143 		}
1144 	}
1145 
1146 	if (!save_option_buffer(&dhcpv6_universe,
1147 				lq.reply_opts,
1148 				lq.client_id.buffer,
1149 				(unsigned char *)lq.client_id.data,
1150 				lq.client_id.len,
1151 				D6O_CLIENTID,
1152 				0)) {
1153 		log_error("dhcpv6_leasequery: "
1154 			  "error saving client identifier.");
1155 		goto exit;
1156 	}
1157 
1158 	lq.cursor = 4;
1159 
1160 	/*
1161 	 * Decode the lq-query option.
1162 	 */
1163 
1164 	if (lq.lq_query.len <= LQ_QUERY_OFFSET) {
1165 		if (!set_error(&lq, STATUS_MalformedQuery,
1166 			       "OPTION_LQ_QUERY too short.")) {
1167 			log_error("dhcpv6_leasequery: unable "
1168 				  "to set MalformedQuery status code.");
1169 			goto exit;
1170 		}
1171 		goto done;
1172 	}
1173 
1174 	lq.query_type = lq.lq_query.data [0];
1175 	memcpy(&lq.link_addr, lq.lq_query.data + 1, sizeof(lq.link_addr));
1176 	switch (lq.query_type) {
1177 		case LQ6QT_BY_ADDRESS:
1178 			break;
1179 		case LQ6QT_BY_CLIENTID:
1180 			if (!set_error(&lq, STATUS_UnknownQueryType,
1181 				       "QUERY_BY_CLIENTID not supported.")) {
1182 				log_error("dhcpv6_leasequery: unable to "
1183 					  "set UnknownQueryType status code.");
1184 				goto exit;
1185 			}
1186 			goto done;
1187 		default:
1188 			if (!set_error(&lq, STATUS_UnknownQueryType,
1189 				       "Unknown query-type.")) {
1190 				log_error("dhcpv6_leasequery: unable to "
1191 					  "set UnknownQueryType status code.");
1192 				goto exit;
1193 			}
1194 			goto done;
1195 	}
1196 
1197 	if (!option_state_allocate(&lq.query_opts, MDL)) {
1198 		log_error("dhcpv6_leasequery: no memory for option state.");
1199 		goto exit;
1200 	}
1201 	if (!parse_option_buffer(lq.query_opts,
1202 				 lq.lq_query.data + LQ_QUERY_OFFSET,
1203 				 lq.lq_query.len - LQ_QUERY_OFFSET,
1204 				 &dhcpv6_universe)) {
1205 		log_error("dhcpv6_leasequery: error parsing query-options.");
1206 		if (!set_error(&lq, STATUS_MalformedQuery,
1207 			       "Bad query-options.")) {
1208 			log_error("dhcpv6_leasequery: unable "
1209 				  "to set MalformedQuery status code.");
1210 			goto exit;
1211 		}
1212 		goto done;
1213 	}
1214 
1215 	/* Do it. */
1216 	if (!process_lq_by_address(&lq))
1217 		goto exit;
1218 
1219       done:
1220 	/* Store the options. */
1221 	lq.cursor += store_options6((char *)lq.buf.data + lq.cursor,
1222 				    sizeof(lq.buf) - lq.cursor,
1223 				    lq.reply_opts,
1224 				    lq.packet,
1225 				    required_opts_lq,
1226 				    NULL);
1227 
1228 	/* Return our reply to the caller. */
1229 	reply_ret->len = lq.cursor;
1230 	reply_ret->buffer = NULL;
1231 	if (!buffer_allocate(&reply_ret->buffer, lq.cursor, MDL)) {
1232 		log_fatal("dhcpv6_leasequery: no memory to store Reply.");
1233 	}
1234 	memcpy(reply_ret->buffer->data, lq.buf.data, lq.cursor);
1235 	reply_ret->data = reply_ret->buffer->data;
1236 
1237       exit:
1238 	/* Cleanup. */
1239 	if (lq.packet != NULL)
1240 		packet_dereference(&lq.packet, MDL);
1241 	if (lq.client_id.data != NULL)
1242 		data_string_forget(&lq.client_id, MDL);
1243 	if (lq.server_id.data != NULL)
1244 		data_string_forget(&lq.server_id, MDL);
1245 	if (lq.lq_query.data != NULL)
1246 		data_string_forget(&lq.lq_query, MDL);
1247 	if (lq.query_opts != NULL)
1248 		option_state_dereference(&lq.query_opts, MDL);
1249 	if (lq.reply_opts != NULL)
1250 		option_state_dereference(&lq.reply_opts, MDL);
1251 }
1252 
1253 #endif /* DHCPv6 */
1254