xref: /freebsd/contrib/unbound/daemon/acl_list.c (revision 076ad2f8)
1 /*
2  * daemon/acl_list.h - client access control storage for the server.
3  *
4  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5  *
6  * This software is open source.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 /**
37  * \file
38  *
39  * This file helps the server keep out queries from outside sources, that
40  * should not be answered.
41  */
42 #include "config.h"
43 #include "daemon/acl_list.h"
44 #include "util/regional.h"
45 #include "util/log.h"
46 #include "util/config_file.h"
47 #include "util/net_help.h"
48 #include "services/localzone.h"
49 #include "sldns/str2wire.h"
50 
51 struct acl_list*
52 acl_list_create(void)
53 {
54 	struct acl_list* acl = (struct acl_list*)calloc(1,
55 		sizeof(struct acl_list));
56 	if(!acl)
57 		return NULL;
58 	acl->region = regional_create();
59 	if(!acl->region) {
60 		acl_list_delete(acl);
61 		return NULL;
62 	}
63 	return acl;
64 }
65 
66 void
67 acl_list_delete(struct acl_list* acl)
68 {
69 	if(!acl)
70 		return;
71 	regional_destroy(acl->region);
72 	free(acl);
73 }
74 
75 /** insert new address into acl_list structure */
76 static struct acl_addr*
77 acl_list_insert(struct acl_list* acl, struct sockaddr_storage* addr,
78 	socklen_t addrlen, int net, enum acl_access control,
79 	int complain_duplicates)
80 {
81 	struct acl_addr* node = regional_alloc_zero(acl->region,
82 		sizeof(struct acl_addr));
83 	if(!node)
84 		return NULL;
85 	node->control = control;
86 	if(!addr_tree_insert(&acl->tree, &node->node, addr, addrlen, net)) {
87 		if(complain_duplicates)
88 			verbose(VERB_QUERY, "duplicate acl address ignored.");
89 	}
90 	return node;
91 }
92 
93 /** apply acl_list string */
94 static int
95 acl_list_str_cfg(struct acl_list* acl, const char* str, const char* s2,
96 	int complain_duplicates)
97 {
98 	struct sockaddr_storage addr;
99 	int net;
100 	socklen_t addrlen;
101 	enum acl_access control;
102 	if(strcmp(s2, "allow") == 0)
103 		control = acl_allow;
104 	else if(strcmp(s2, "deny") == 0)
105 		control = acl_deny;
106 	else if(strcmp(s2, "refuse") == 0)
107 		control = acl_refuse;
108 	else if(strcmp(s2, "deny_non_local") == 0)
109 		control = acl_deny_non_local;
110 	else if(strcmp(s2, "refuse_non_local") == 0)
111 		control = acl_refuse_non_local;
112 	else if(strcmp(s2, "allow_snoop") == 0)
113 		control = acl_allow_snoop;
114 	else {
115 		log_err("access control type %s unknown", str);
116 		return 0;
117 	}
118 	if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
119 		log_err("cannot parse access control: %s %s", str, s2);
120 		return 0;
121 	}
122 	if(!acl_list_insert(acl, &addr, addrlen, net, control,
123 		complain_duplicates)) {
124 		log_err("out of memory");
125 		return 0;
126 	}
127 	return 1;
128 }
129 
130 /** find or create node (NULL on parse or error) */
131 static struct acl_addr*
132 acl_find_or_create(struct acl_list* acl, const char* str)
133 {
134 	struct acl_addr* node;
135 	struct sockaddr_storage addr;
136 	int net;
137 	socklen_t addrlen;
138 	if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
139 		log_err("cannot parse netblock: %s", str);
140 		return NULL;
141 	}
142 	/* find or create node */
143 	if(!(node=(struct acl_addr*)addr_tree_find(&acl->tree, &addr,
144 		addrlen, net))) {
145 		/* create node, type 'allow' since otherwise tags are
146 		 * pointless, can override with specific access-control: cfg */
147 		if(!(node=(struct acl_addr*)acl_list_insert(acl, &addr,
148 			addrlen, net, acl_allow, 1))) {
149 			log_err("out of memory");
150 			return NULL;
151 		}
152 	}
153 	return node;
154 }
155 
156 /** apply acl_tag string */
157 static int
158 acl_list_tags_cfg(struct acl_list* acl, const char* str, uint8_t* bitmap,
159 	size_t bitmaplen)
160 {
161 	struct acl_addr* node;
162 	if(!(node=acl_find_or_create(acl, str)))
163 		return 0;
164 	node->taglen = bitmaplen;
165 	node->taglist = regional_alloc_init(acl->region, bitmap, bitmaplen);
166 	if(!node->taglist) {
167 		log_err("out of memory");
168 		return 0;
169 	}
170 	return 1;
171 }
172 
173 /** apply acl_tag_action string */
174 static int
175 acl_list_tag_action_cfg(struct acl_list* acl, struct config_file* cfg,
176 	const char* str, const char* tag, const char* action)
177 {
178 	struct acl_addr* node;
179 	int tagid;
180 	enum localzone_type t;
181 	if(!(node=acl_find_or_create(acl, str)))
182 		return 0;
183 	/* allocate array if not yet */
184 	if(!node->tag_actions) {
185 		node->tag_actions = (uint8_t*)regional_alloc_zero(acl->region,
186 			sizeof(*node->tag_actions)*cfg->num_tags);
187 		if(!node->tag_actions) {
188 			log_err("out of memory");
189 			return 0;
190 		}
191 		node->tag_actions_size = (size_t)cfg->num_tags;
192 	}
193 	/* parse tag */
194 	if((tagid=find_tag_id(cfg, tag)) == -1) {
195 		log_err("cannot parse tag (define-tag it): %s %s", str, tag);
196 		return 0;
197 	}
198 	if((size_t)tagid >= node->tag_actions_size) {
199 		log_err("tagid too large for array %s %s", str, tag);
200 		return 0;
201 	}
202 	if(!local_zone_str2type(action, &t)) {
203 		log_err("cannot parse access control action type: %s %s %s",
204 			str, tag, action);
205 		return 0;
206 	}
207 	node->tag_actions[tagid] = (uint8_t)t;
208 	return 1;
209 }
210 
211 /** check wire data parse */
212 static int
213 check_data(const char* data)
214 {
215 	char buf[65536];
216 	uint8_t rr[LDNS_RR_BUF_SIZE];
217 	size_t len = sizeof(rr);
218 	int res;
219 	snprintf(buf, sizeof(buf), "%s %s", "example.com.", data);
220 	res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, NULL, 0,
221 		NULL, 0);
222 	if(res == 0)
223 		return 1;
224 	log_err("rr data [char %d] parse error %s",
225 		(int)LDNS_WIREPARSE_OFFSET(res)-13,
226 		sldns_get_errorstr_parse(res));
227 	return 0;
228 }
229 
230 /** apply acl_tag_data string */
231 static int
232 acl_list_tag_data_cfg(struct acl_list* acl, struct config_file* cfg,
233 	const char* str, const char* tag, const char* data)
234 {
235 	struct acl_addr* node;
236 	int tagid;
237 	char* dupdata;
238 	if(!(node=acl_find_or_create(acl, str)))
239 		return 0;
240 	/* allocate array if not yet */
241 	if(!node->tag_datas) {
242 		node->tag_datas = (struct config_strlist**)regional_alloc_zero(
243 			acl->region, sizeof(*node->tag_datas)*cfg->num_tags);
244 		if(!node->tag_datas) {
245 			log_err("out of memory");
246 			return 0;
247 		}
248 		node->tag_datas_size = (size_t)cfg->num_tags;
249 	}
250 	/* parse tag */
251 	if((tagid=find_tag_id(cfg, tag)) == -1) {
252 		log_err("cannot parse tag (define-tag it): %s %s", str, tag);
253 		return 0;
254 	}
255 	if((size_t)tagid >= node->tag_datas_size) {
256 		log_err("tagid too large for array %s %s", str, tag);
257 		return 0;
258 	}
259 
260 	/* check data? */
261 	if(!check_data(data)) {
262 		log_err("cannot parse access-control-tag data: %s %s '%s'",
263 			str, tag, data);
264 		return 0;
265 	}
266 
267 	dupdata = regional_strdup(acl->region, data);
268 	if(!dupdata) {
269 		log_err("out of memory");
270 		return 0;
271 	}
272 	if(!cfg_region_strlist_insert(acl->region,
273 		&(node->tag_datas[tagid]), dupdata)) {
274 		log_err("out of memory");
275 		return 0;
276 	}
277 	return 1;
278 }
279 
280 /** read acl_list config */
281 static int
282 read_acl_list(struct acl_list* acl, struct config_file* cfg)
283 {
284 	struct config_str2list* p;
285 	for(p = cfg->acls; p; p = p->next) {
286 		log_assert(p->str && p->str2);
287 		if(!acl_list_str_cfg(acl, p->str, p->str2, 1))
288 			return 0;
289 	}
290 	return 1;
291 }
292 
293 /** read acl tags config */
294 static int
295 read_acl_tags(struct acl_list* acl, struct config_file* cfg)
296 {
297 	struct config_strbytelist* np, *p = cfg->acl_tags;
298 	cfg->acl_tags = NULL;
299 	while(p) {
300 		log_assert(p->str && p->str2);
301 		if(!acl_list_tags_cfg(acl, p->str, p->str2, p->str2len)) {
302 			config_del_strbytelist(p);
303 			return 0;
304 		}
305 		/* free the items as we go to free up memory */
306 		np = p->next;
307 		free(p->str);
308 		free(p->str2);
309 		free(p);
310 		p = np;
311 	}
312 	return 1;
313 }
314 
315 /** read acl tag actions config */
316 static int
317 read_acl_tag_actions(struct acl_list* acl, struct config_file* cfg)
318 {
319 	struct config_str3list* p, *np;
320 	p = cfg->acl_tag_actions;
321 	cfg->acl_tag_actions = NULL;
322 	while(p) {
323 		log_assert(p->str && p->str2 && p->str3);
324 		if(!acl_list_tag_action_cfg(acl, cfg, p->str, p->str2,
325 			p->str3)) {
326 			config_deltrplstrlist(p);
327 			return 0;
328 		}
329 		/* free the items as we go to free up memory */
330 		np = p->next;
331 		free(p->str);
332 		free(p->str2);
333 		free(p->str3);
334 		free(p);
335 		p = np;
336 	}
337 	return 1;
338 }
339 
340 /** read acl tag datas config */
341 static int
342 read_acl_tag_datas(struct acl_list* acl, struct config_file* cfg)
343 {
344 	struct config_str3list* p, *np;
345 	p = cfg->acl_tag_datas;
346 	cfg->acl_tag_datas = NULL;
347 	while(p) {
348 		log_assert(p->str && p->str2 && p->str3);
349 		if(!acl_list_tag_data_cfg(acl, cfg, p->str, p->str2, p->str3)) {
350 			config_deltrplstrlist(p);
351 			return 0;
352 		}
353 		/* free the items as we go to free up memory */
354 		np = p->next;
355 		free(p->str);
356 		free(p->str2);
357 		free(p->str3);
358 		free(p);
359 		p = np;
360 	}
361 	return 1;
362 }
363 
364 int
365 acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg)
366 {
367 	regional_free_all(acl->region);
368 	addr_tree_init(&acl->tree);
369 	if(!read_acl_list(acl, cfg))
370 		return 0;
371 	if(!read_acl_tags(acl, cfg))
372 		return 0;
373 	if(!read_acl_tag_actions(acl, cfg))
374 		return 0;
375 	if(!read_acl_tag_datas(acl, cfg))
376 		return 0;
377 	/* insert defaults, with '0' to ignore them if they are duplicates */
378 	if(!acl_list_str_cfg(acl, "0.0.0.0/0", "refuse", 0))
379 		return 0;
380 	if(!acl_list_str_cfg(acl, "127.0.0.0/8", "allow", 0))
381 		return 0;
382 	if(cfg->do_ip6) {
383 		if(!acl_list_str_cfg(acl, "::0/0", "refuse", 0))
384 			return 0;
385 		if(!acl_list_str_cfg(acl, "::1", "allow", 0))
386 			return 0;
387 		if(!acl_list_str_cfg(acl, "::ffff:127.0.0.1", "allow", 0))
388 			return 0;
389 	}
390 	addr_tree_init_parents(&acl->tree);
391 	return 1;
392 }
393 
394 enum acl_access
395 acl_get_control(struct acl_addr* acl)
396 {
397 	if(acl) return acl->control;
398 	return acl_deny;
399 }
400 
401 struct acl_addr*
402 acl_addr_lookup(struct acl_list* acl, struct sockaddr_storage* addr,
403         socklen_t addrlen)
404 {
405 	return (struct acl_addr*)addr_tree_lookup(&acl->tree,
406 		addr, addrlen);
407 }
408 
409 size_t
410 acl_list_get_mem(struct acl_list* acl)
411 {
412 	if(!acl) return 0;
413 	return sizeof(*acl) + regional_get_mem(acl->region);
414 }
415