Blob Blame History Raw
From 653481c6ebf46dcadb5a017085325d956dd04a28 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 21 Aug 2018 22:06:36 +0100
Subject: [PATCH] Properly deal with unaligned addresses in DHCPv6 packets.

Thanks to Vladislav Grishenko for spotting this.

(cherry picked from commit 97f876b64c22b2b18412e2e3d8506ee33e42db7c)

Conflicts:
	src/rfc3315.c
---
 src/rfc1035.c |   2 +-
 src/rfc3315.c | 101 ++++++++++++++++++++++++++++++++++------------------------
 2 files changed, 61 insertions(+), 42 deletions(-)

diff --git a/src/rfc1035.c b/src/rfc1035.c
index 6b3bb27..ee5f7a0 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -1376,7 +1376,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
 		    if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
 					    daemon->local_ttl, NULL,
 					    t->class, C_IN, "t", t->len, t->txt))
-		      anscount ++;
+		      anscount++;
 		  }
 	      }
 		
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 21fcd9b..ee1cf17 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -639,9 +639,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 	    int plain_range = 1;
 	    u32 lease_time;
 	    struct dhcp_lease *ltmp;
-	    struct in6_addr *req_addr;
-	    struct in6_addr addr;
-
+	    struct in6_addr req_addr, addr;
+	    
 	    if (!check_ia(state, opt, &ia_end, &ia_option))
 	      continue;
 	    
@@ -709,9 +708,10 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 
 	    for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
 	      {
-		req_addr = opt6_ptr(ia_option, 0);
+		/* worry about alignment here. */
+		memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
 				
-		if ((c = address6_valid(state->context, req_addr, solicit_tags, plain_range)))
+		if ((c = address6_valid(state->context, &req_addr, solicit_tags, plain_range)))
 		  {
 		    lease_time = c->lease_time;
 		    /* If the client asks for an address on the same network as a configured address, 
@@ -719,14 +719,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		       addresses automatic. */
 		    if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(state, &addr))
 		      {
-			req_addr = &addr;
+			req_addr = addr;
 			mark_config_used(c, &addr);
 			if (have_config(config, CONFIG_TIME))
 			  lease_time = config->lease_time;
 		      }
-		    else if (!(c = address6_available(state->context, req_addr, solicit_tags, plain_range)))
+		    else if (!(c = address6_available(state->context, &req_addr, solicit_tags, plain_range)))
 		      continue; /* not an address we're allowed */
-		    else if (!check_address(state, req_addr))
+		    else if (!check_address(state, &req_addr))
 		      continue; /* address leased elsewhere */
 		    
 		    /* add address to output packet */
@@ -734,8 +734,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		    if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
 		      state->send_prefix_class = prefix_class_from_context(c);
 #endif		    
-		    add_address(state, c, lease_time, ia_option, &min_time, req_addr, now);
-		    mark_context_used(state, req_addr);
+		    add_address(state, c, lease_time, ia_option, &min_time, &req_addr, now);
+		    mark_context_used(state, &req_addr);
 		    get_context_tag(state, c);
 		    address_assigned = 1;
 		  }
@@ -768,15 +768,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 	    ltmp = NULL;
 	    while ((ltmp = lease6_find_by_client(ltmp, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->clid, state->clid_len, state->iaid)))
 	      {
-		req_addr = &ltmp->addr6;
-		if ((c = address6_available(state->context, req_addr, solicit_tags, plain_range)))
+		req_addr = ltmp->addr6;
+		if ((c = address6_available(state->context, &req_addr, solicit_tags, plain_range)))
 		  {
 #ifdef OPTION6_PREFIX_CLASS
 		    if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
 		      state->send_prefix_class = prefix_class_from_context(c);
 #endif
-		    add_address(state, c, c->lease_time, NULL, &min_time, req_addr, now);
-		    mark_context_used(state, req_addr);
+		    add_address(state, c, c->lease_time, NULL, &min_time, &req_addr, now);
+		    mark_context_used(state, &req_addr);
 		    get_context_tag(state, c);
 		    address_assigned = 1;
 		  }
@@ -892,16 +892,19 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 	      
 	    for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
 	      {
-		struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
+		struct in6_addr req_addr;
 		struct dhcp_context *dynamic, *c;
 		unsigned int lease_time;
 		struct in6_addr addr;
 		int config_ok = 0;
+
+		/* align. */
+		memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
 		
-		if ((c = address6_valid(state->context, req_addr, tagif, 1)))
-		  config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr);
+		if ((c = address6_valid(state->context, &req_addr, tagif, 1)))
+		  config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr);
 		
-		if ((dynamic = address6_available(state->context, req_addr, tagif, 1)) || c)
+		if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c)
 		  {
 		    if (!dynamic && !config_ok)
 		      {
@@ -911,7 +914,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 			put_opt6_string(_("address unavailable"));
 			end_opt6(o1);
 		      }
-		    else if (!check_address(state, req_addr))
+		    else if (!check_address(state, &req_addr))
 		      {
 			/* Address leased to another DUID/IAID */
 			o1 = new_opt6(OPTION6_STATUS_CODE);
@@ -933,7 +936,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 			if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
 			  state->send_prefix_class = prefix_class_from_context(c);
 #endif
-			add_address(state, dynamic, lease_time, ia_option, &min_time, req_addr, now);
+			add_address(state, dynamic, lease_time, ia_option, &min_time, &req_addr, now);
 			get_context_tag(state, dynamic);
 			address_assigned = 1;
 		      }
@@ -996,15 +999,17 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 	    for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
 	      {
 		struct dhcp_lease *lease = NULL;
-		struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
+		struct in6_addr req_addr;
 		unsigned int preferred_time =  opt6_uint(ia_option, 16, 4);
 		unsigned int valid_time =  opt6_uint(ia_option, 20, 4);
 		char *message = NULL;
 		struct dhcp_context *this_context;
+
+		memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); 
 		
 		if (!(lease = lease6_find(state->clid, state->clid_len,
 					  state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, 
-					  state->iaid, req_addr)))
+					  state->iaid, &req_addr)))
 		  {
 		    /* If the server cannot find a client entry for the IA the server
 		       returns the IA containing no addresses with a Status Code option set
@@ -1012,7 +1017,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		    save_counter(iacntr);
 		    t1cntr = 0;
 		    
-		    log6_packet(state, "DHCPREPLY", req_addr, _("lease not found"));
+		    log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found"));
 		    
 		    o1 = new_opt6(OPTION6_STATUS_CODE);
 		    put_opt6_short(DHCP6NOBINDING);
@@ -1024,15 +1029,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		  }
 		
 		
-		if ((this_context = address6_available(state->context, req_addr, tagif, 1)) ||
-		    (this_context = address6_valid(state->context, req_addr, tagif, 1)))
+		if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) ||
+		    (this_context = address6_valid(state->context, &req_addr, tagif, 1)))
 		  {
 		    struct in6_addr addr;
 		    unsigned int lease_time;
 
 		    get_context_tag(state, this_context);
 		    
-		    if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr) && have_config(config, CONFIG_TIME))
+		    if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr) && have_config(config, CONFIG_TIME))
 		      lease_time = config->lease_time;
 		    else 
 		      lease_time = this_context->lease_time;
@@ -1045,7 +1050,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		      lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0);
 		    if (state->ia_type == OPTION6_IA_NA && state->hostname)
 		      {
-			char *addr_domain = get_domain6(req_addr);
+			char *addr_domain = get_domain6(&req_addr);
 			if (!state->send_domain)
 			  state->send_domain = addr_domain;
 			lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain); 
@@ -1063,12 +1068,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		  } 
 
 		if (message && (message != state->hostname))
-		  log6_packet(state, "DHCPREPLY", req_addr, message);	
+		  log6_packet(state, "DHCPREPLY", &req_addr, message);	
 		else
-		  log6_quiet(state, "DHCPREPLY", req_addr, message);
+		  log6_quiet(state, "DHCPREPLY", &req_addr, message);
 	
 		o1 =  new_opt6(OPTION6_IAADDR);
-		put_opt6(req_addr, sizeof(*req_addr));
+		put_opt6(&req_addr, sizeof(req_addr));
 		put_opt6_long(preferred_time);
 		put_opt6_long(valid_time);
 		end_opt6(o1);
@@ -1100,19 +1105,23 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		 ia_option;
 		 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
 	      {
-		struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
+		struct in6_addr req_addr;
+
+		/* alignment */
+		memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
 		
-		if (!address6_valid(state->context, req_addr, tagif, 1))
+		if (!address6_valid(state->context, &req_addr, tagif, 1))
 		  {
 		    o1 = new_opt6(OPTION6_STATUS_CODE);
 		    put_opt6_short(DHCP6NOTONLINK);
 		    put_opt6_string(_("confirm failed"));
 		    end_opt6(o1);
+		    log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed"));
 		    return 1;
 		  }
 
 		good_addr = 1;
-		log6_quiet(state, "DHCPREPLY", req_addr, state->hostname);
+		log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname);
 	      }
 	  }	 
 	
@@ -1171,9 +1180,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) 
 	      {
 		struct dhcp_lease *lease;
-		
+		struct in6_addr addr;
+
+		/* align */
+		memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
 		if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
-					 state->iaid, opt6_ptr(ia_option, 0))))
+					 state->iaid, &addr)))
 		  lease_prune(lease, now);
 		else
 		  {
@@ -1233,12 +1245,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		 ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) 
 	      {
 		struct dhcp_lease *lease;
-		struct in6_addr *addrp = opt6_ptr(ia_option, 0);
+		struct in6_addr addr;
 
-		if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, addrp))
+		/* align */
+		memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
+
+		if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, &addr))
 		  {
 		    prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);
-		    inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN);
+		    inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
 		    my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), 
 			      daemon->addrbuff, daemon->dhcp_buff3);
 		    config->flags |= CONFIG_DECLINED;
@@ -1250,7 +1265,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		    context_tmp->addr_epoch++;
 		
 		if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
-					 state->iaid, opt6_ptr(ia_option, 0))))
+					 state->iaid, &addr)))
 		  lease_prune(lease, now);
 		else
 		  {
@@ -1267,7 +1282,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
 		      }
 		    
 		    o1 = new_opt6(OPTION6_IAADDR);
-		    put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
+		    put_opt6(&addr, IN6ADDRSZ);
 		    put_opt6_long(0);
 		    put_opt6_long(0);
 		    end_opt6(o1);
@@ -1935,7 +1950,11 @@ static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_op
 	}
       else if (type == OPTION6_IAADDR)
 	{
-	  inet_ntop(AF_INET6, opt6_ptr(opt, 0), daemon->addrbuff, ADDRSTRLEN);
+	  struct in6_addr addr;
+
+	  /* align */
+	  memcpy(&addr, opt6_ptr(opt, 0), IN6ADDRSZ);
+	  inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
 	  sprintf(daemon->namebuff, "%s PL=%u VL=%u", 
 		  daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4));
 	  optname = "iaaddr";
-- 
1.8.3.1