xref: /openbsd-src/usr.sbin/unbound/iterator/iter_priv.c (revision 9c7f0a49a49eaaf8a541e2c1c60150e851bc44d2)
1933707f3Ssthen /*
2933707f3Ssthen  * iterator/iter_priv.c - iterative resolver private address and domain store
3933707f3Ssthen  *
4933707f3Ssthen  * Copyright (c) 2008, NLnet Labs. All rights reserved.
5933707f3Ssthen  *
6933707f3Ssthen  * This software is open source.
7933707f3Ssthen  *
8933707f3Ssthen  * Redistribution and use in source and binary forms, with or without
9933707f3Ssthen  * modification, are permitted provided that the following conditions
10933707f3Ssthen  * are met:
11933707f3Ssthen  *
12933707f3Ssthen  * Redistributions of source code must retain the above copyright notice,
13933707f3Ssthen  * this list of conditions and the following disclaimer.
14933707f3Ssthen  *
15933707f3Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
16933707f3Ssthen  * this list of conditions and the following disclaimer in the documentation
17933707f3Ssthen  * and/or other materials provided with the distribution.
18933707f3Ssthen  *
19933707f3Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
20933707f3Ssthen  * be used to endorse or promote products derived from this software without
21933707f3Ssthen  * specific prior written permission.
22933707f3Ssthen  *
23933707f3Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
245d76a658Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
255d76a658Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
265d76a658Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
275d76a658Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
285d76a658Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
295d76a658Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
305d76a658Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
315d76a658Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
325d76a658Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
335d76a658Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34933707f3Ssthen  */
35933707f3Ssthen 
36933707f3Ssthen /**
37933707f3Ssthen  * \file
38933707f3Ssthen  *
39933707f3Ssthen  * This file contains functions to assist the iterator module.
40933707f3Ssthen  * Keep track of the private addresses and lookup fast.
41933707f3Ssthen  */
42933707f3Ssthen 
43933707f3Ssthen #include "config.h"
44933707f3Ssthen #include "iterator/iter_priv.h"
45933707f3Ssthen #include "util/regional.h"
46933707f3Ssthen #include "util/log.h"
47933707f3Ssthen #include "util/config_file.h"
48933707f3Ssthen #include "util/data/dname.h"
49933707f3Ssthen #include "util/data/msgparse.h"
50933707f3Ssthen #include "util/net_help.h"
51933707f3Ssthen #include "util/storage/dnstree.h"
52fdfb4ba6Ssthen #include "sldns/str2wire.h"
53fdfb4ba6Ssthen #include "sldns/sbuffer.h"
54933707f3Ssthen 
priv_create(void)55933707f3Ssthen struct iter_priv* priv_create(void)
56933707f3Ssthen {
57933707f3Ssthen 	struct iter_priv* priv = (struct iter_priv*)calloc(1, sizeof(*priv));
58933707f3Ssthen 	if(!priv)
59933707f3Ssthen 		return NULL;
60933707f3Ssthen 	priv->region = regional_create();
61933707f3Ssthen 	if(!priv->region) {
62933707f3Ssthen 		priv_delete(priv);
63933707f3Ssthen 		return NULL;
64933707f3Ssthen 	}
65933707f3Ssthen 	addr_tree_init(&priv->a);
66933707f3Ssthen 	name_tree_init(&priv->n);
67933707f3Ssthen 	return priv;
68933707f3Ssthen }
69933707f3Ssthen 
priv_delete(struct iter_priv * priv)70933707f3Ssthen void priv_delete(struct iter_priv* priv)
71933707f3Ssthen {
72933707f3Ssthen 	if(!priv) return;
73933707f3Ssthen 	regional_destroy(priv->region);
74933707f3Ssthen 	free(priv);
75933707f3Ssthen }
76933707f3Ssthen 
77933707f3Ssthen /** Read private-addr declarations from config */
read_addrs(struct iter_priv * priv,struct config_file * cfg)78933707f3Ssthen static int read_addrs(struct iter_priv* priv, struct config_file* cfg)
79933707f3Ssthen {
80933707f3Ssthen 	/* parse addresses, report errors, insert into tree */
81933707f3Ssthen 	struct config_strlist* p;
82933707f3Ssthen 	struct addr_tree_node* n;
83933707f3Ssthen 	struct sockaddr_storage addr;
84933707f3Ssthen 	int net;
85933707f3Ssthen 	socklen_t addrlen;
86933707f3Ssthen 
87933707f3Ssthen 	for(p = cfg->private_address; p; p = p->next) {
88933707f3Ssthen 		log_assert(p->str);
89933707f3Ssthen 		if(!netblockstrtoaddr(p->str, UNBOUND_DNS_PORT, &addr,
90933707f3Ssthen 			&addrlen, &net)) {
91933707f3Ssthen 			log_err("cannot parse private-address: %s", p->str);
92933707f3Ssthen 			return 0;
93933707f3Ssthen 		}
94933707f3Ssthen 		n = (struct addr_tree_node*)regional_alloc(priv->region,
95933707f3Ssthen 			sizeof(*n));
96933707f3Ssthen 		if(!n) {
97933707f3Ssthen 			log_err("out of memory");
98933707f3Ssthen 			return 0;
99933707f3Ssthen 		}
100933707f3Ssthen 		if(!addr_tree_insert(&priv->a, n, &addr, addrlen, net)) {
101933707f3Ssthen 			verbose(VERB_QUERY, "ignoring duplicate "
102933707f3Ssthen 				"private-address: %s", p->str);
103933707f3Ssthen 		}
104933707f3Ssthen 	}
105933707f3Ssthen 	return 1;
106933707f3Ssthen }
107933707f3Ssthen 
108933707f3Ssthen /** Read private-domain declarations from config */
read_names(struct iter_priv * priv,struct config_file * cfg)109933707f3Ssthen static int read_names(struct iter_priv* priv, struct config_file* cfg)
110933707f3Ssthen {
111933707f3Ssthen 	/* parse names, report errors, insert into tree */
112933707f3Ssthen 	struct config_strlist* p;
113933707f3Ssthen 	struct name_tree_node* n;
1145d76a658Ssthen 	uint8_t* nm, *nmr;
115933707f3Ssthen 	size_t nm_len;
116933707f3Ssthen 	int nm_labs;
117933707f3Ssthen 
118933707f3Ssthen 	for(p = cfg->private_domain; p; p = p->next) {
119933707f3Ssthen 		log_assert(p->str);
1205d76a658Ssthen 		nm = sldns_str2wire_dname(p->str, &nm_len);
1215d76a658Ssthen 		if(!nm) {
122933707f3Ssthen 			log_err("cannot parse private-domain: %s", p->str);
123933707f3Ssthen 			return 0;
124933707f3Ssthen 		}
125933707f3Ssthen 		nm_labs = dname_count_size_labels(nm, &nm_len);
1265d76a658Ssthen 		nmr = (uint8_t*)regional_alloc_init(priv->region, nm, nm_len);
1275d76a658Ssthen 		free(nm);
1285d76a658Ssthen 		if(!nmr) {
129933707f3Ssthen 			log_err("out of memory");
130933707f3Ssthen 			return 0;
131933707f3Ssthen 		}
132933707f3Ssthen 		n = (struct name_tree_node*)regional_alloc(priv->region,
133933707f3Ssthen 			sizeof(*n));
134933707f3Ssthen 		if(!n) {
135933707f3Ssthen 			log_err("out of memory");
136933707f3Ssthen 			return 0;
137933707f3Ssthen 		}
1385d76a658Ssthen 		if(!name_tree_insert(&priv->n, n, nmr, nm_len, nm_labs,
139933707f3Ssthen 			LDNS_RR_CLASS_IN)) {
140933707f3Ssthen 			verbose(VERB_QUERY, "ignoring duplicate "
141933707f3Ssthen 				"private-domain: %s", p->str);
142933707f3Ssthen 		}
143933707f3Ssthen 	}
144933707f3Ssthen 	return 1;
145933707f3Ssthen }
146933707f3Ssthen 
priv_apply_cfg(struct iter_priv * priv,struct config_file * cfg)147933707f3Ssthen int priv_apply_cfg(struct iter_priv* priv, struct config_file* cfg)
148933707f3Ssthen {
149933707f3Ssthen 	/* empty the current contents */
150933707f3Ssthen 	regional_free_all(priv->region);
151933707f3Ssthen 	addr_tree_init(&priv->a);
152933707f3Ssthen 	name_tree_init(&priv->n);
153933707f3Ssthen 
154933707f3Ssthen 	/* read new contents */
155933707f3Ssthen 	if(!read_addrs(priv, cfg))
156933707f3Ssthen 		return 0;
157933707f3Ssthen 	if(!read_names(priv, cfg))
158933707f3Ssthen 		return 0;
159933707f3Ssthen 
160933707f3Ssthen 	/* prepare for lookups */
161933707f3Ssthen 	addr_tree_init_parents(&priv->a);
162933707f3Ssthen 	name_tree_init_parents(&priv->n);
163933707f3Ssthen 	return 1;
164933707f3Ssthen }
165933707f3Ssthen 
166933707f3Ssthen /**
167933707f3Ssthen  * See if an address is blocked.
168933707f3Ssthen  * @param priv: structure for address storage.
169933707f3Ssthen  * @param addr: address to check
170933707f3Ssthen  * @param addrlen: length of addr.
171933707f3Ssthen  * @return: true if the address must not be queried. false if unlisted.
172933707f3Ssthen  */
173933707f3Ssthen static int
priv_lookup_addr(struct iter_priv * priv,struct sockaddr_storage * addr,socklen_t addrlen)174933707f3Ssthen priv_lookup_addr(struct iter_priv* priv, struct sockaddr_storage* addr,
175933707f3Ssthen 	socklen_t addrlen)
176933707f3Ssthen {
177933707f3Ssthen 	return addr_tree_lookup(&priv->a, addr, addrlen) != NULL;
178933707f3Ssthen }
179933707f3Ssthen 
180933707f3Ssthen /**
181933707f3Ssthen  * See if a name is whitelisted.
182933707f3Ssthen  * @param priv: structure for address storage.
183933707f3Ssthen  * @param pkt: the packet (for compression ptrs).
184933707f3Ssthen  * @param name: name to check.
185933707f3Ssthen  * @param name_len: uncompressed length of the name to check.
186933707f3Ssthen  * @param dclass: class to check.
187933707f3Ssthen  * @return: true if the name is OK. false if unlisted.
188933707f3Ssthen  */
189933707f3Ssthen static int
priv_lookup_name(struct iter_priv * priv,sldns_buffer * pkt,uint8_t * name,size_t name_len,uint16_t dclass)1905d76a658Ssthen priv_lookup_name(struct iter_priv* priv, sldns_buffer* pkt,
191933707f3Ssthen 	uint8_t* name, size_t name_len, uint16_t dclass)
192933707f3Ssthen {
193933707f3Ssthen 	size_t len;
194933707f3Ssthen 	uint8_t decomp[256];
195933707f3Ssthen 	int labs;
196933707f3Ssthen 	if(name_len >= sizeof(decomp))
197933707f3Ssthen 		return 0;
198933707f3Ssthen 	dname_pkt_copy(pkt, decomp, name);
199933707f3Ssthen 	labs = dname_count_size_labels(decomp, &len);
200933707f3Ssthen 	log_assert(name_len == len);
201933707f3Ssthen 	return name_tree_lookup(&priv->n, decomp, len, labs, dclass) != NULL;
202933707f3Ssthen }
203933707f3Ssthen 
priv_get_mem(struct iter_priv * priv)204933707f3Ssthen size_t priv_get_mem(struct iter_priv* priv)
205933707f3Ssthen {
206933707f3Ssthen 	if(!priv) return 0;
207933707f3Ssthen 	return sizeof(*priv) + regional_get_mem(priv->region);
208933707f3Ssthen }
209933707f3Ssthen 
priv_rrset_bad(struct iter_priv * priv,sldns_buffer * pkt,struct rrset_parse * rrset)2105d76a658Ssthen int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt,
211933707f3Ssthen 	struct rrset_parse* rrset)
212933707f3Ssthen {
213933707f3Ssthen 	if(priv->a.count == 0)
214933707f3Ssthen 		return 0; /* there are no blocked addresses */
215933707f3Ssthen 
216933707f3Ssthen 	/* see if it is a private name, that is allowed to have any */
217933707f3Ssthen 	if(priv_lookup_name(priv, pkt, rrset->dname, rrset->dname_len,
218933707f3Ssthen 		ntohs(rrset->rrset_class))) {
219933707f3Ssthen 		return 0;
220933707f3Ssthen 	} else {
221933707f3Ssthen 		/* so its a public name, check the address */
222933707f3Ssthen 		socklen_t len;
223229e174cSsthen 		struct rr_parse* rr, *prev = NULL;
224933707f3Ssthen 		if(rrset->type == LDNS_RR_TYPE_A) {
225933707f3Ssthen 			struct sockaddr_storage addr;
226933707f3Ssthen 			struct sockaddr_in sa;
227933707f3Ssthen 
228933707f3Ssthen 			len = (socklen_t)sizeof(sa);
229933707f3Ssthen 			memset(&sa, 0, len);
230933707f3Ssthen 			sa.sin_family = AF_INET;
231933707f3Ssthen 			sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
232933707f3Ssthen 			for(rr = rrset->rr_first; rr; rr = rr->next) {
2335d76a658Ssthen 				if(sldns_read_uint16(rr->ttl_data+4)
234229e174cSsthen 					!= INET_SIZE) {
235229e174cSsthen 					prev = rr;
236933707f3Ssthen 					continue;
237229e174cSsthen 				}
238933707f3Ssthen 				memmove(&sa.sin_addr, rr->ttl_data+4+2,
239933707f3Ssthen 					INET_SIZE);
240933707f3Ssthen 				memmove(&addr, &sa, len);
241229e174cSsthen 				if(priv_lookup_addr(priv, &addr, len)) {
242*9c7f0a49Ssthen 					if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len))
243933707f3Ssthen 						return 1;
244229e174cSsthen 					continue;
245229e174cSsthen 				}
246229e174cSsthen 				prev = rr;
247933707f3Ssthen 			}
248933707f3Ssthen 		} else if(rrset->type == LDNS_RR_TYPE_AAAA) {
249933707f3Ssthen 			struct sockaddr_storage addr;
250933707f3Ssthen 			struct sockaddr_in6 sa;
251933707f3Ssthen 			len = (socklen_t)sizeof(sa);
252933707f3Ssthen 			memset(&sa, 0, len);
253933707f3Ssthen 			sa.sin6_family = AF_INET6;
254933707f3Ssthen 			sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
255933707f3Ssthen 			for(rr = rrset->rr_first; rr; rr = rr->next) {
2565d76a658Ssthen 				if(sldns_read_uint16(rr->ttl_data+4)
257229e174cSsthen 					!= INET6_SIZE) {
258229e174cSsthen 					prev = rr;
259933707f3Ssthen 					continue;
260229e174cSsthen 				}
261933707f3Ssthen 				memmove(&sa.sin6_addr, rr->ttl_data+4+2,
262933707f3Ssthen 					INET6_SIZE);
263933707f3Ssthen 				memmove(&addr, &sa, len);
264229e174cSsthen 				if(priv_lookup_addr(priv, &addr, len)) {
265*9c7f0a49Ssthen 					if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len))
266933707f3Ssthen 						return 1;
267229e174cSsthen 					continue;
268229e174cSsthen 				}
269229e174cSsthen 				prev = rr;
270933707f3Ssthen 			}
271933707f3Ssthen 		}
272933707f3Ssthen 	}
273933707f3Ssthen 	return 0;
274933707f3Ssthen }
275