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