acl_list.c revision 356345
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
51struct acl_list*
52acl_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
66void
67acl_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 */
76static struct acl_addr*
77acl_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 */
94static int
95acl_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 if(strcmp(s2, "allow_setrd") == 0)
115		control = acl_allow_setrd;
116	else {
117		log_err("access control type %s unknown", str);
118		return 0;
119	}
120	if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
121		log_err("cannot parse access control: %s %s", str, s2);
122		return 0;
123	}
124	if(!acl_list_insert(acl, &addr, addrlen, net, control,
125		complain_duplicates)) {
126		log_err("out of memory");
127		return 0;
128	}
129	return 1;
130}
131
132/** find or create node (NULL on parse or error) */
133static struct acl_addr*
134acl_find_or_create(struct acl_list* acl, const char* str)
135{
136	struct acl_addr* node;
137	struct sockaddr_storage addr;
138	int net;
139	socklen_t addrlen;
140	if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
141		log_err("cannot parse netblock: %s", str);
142		return NULL;
143	}
144	/* find or create node */
145	if(!(node=(struct acl_addr*)addr_tree_find(&acl->tree, &addr,
146		addrlen, net))) {
147		/* create node, type 'allow' since otherwise tags are
148		 * pointless, can override with specific access-control: cfg */
149		if(!(node=(struct acl_addr*)acl_list_insert(acl, &addr,
150			addrlen, net, acl_allow, 1))) {
151			log_err("out of memory");
152			return NULL;
153		}
154	}
155	return node;
156}
157
158/** apply acl_tag string */
159static int
160acl_list_tags_cfg(struct acl_list* acl, const char* str, uint8_t* bitmap,
161	size_t bitmaplen)
162{
163	struct acl_addr* node;
164	if(!(node=acl_find_or_create(acl, str)))
165		return 0;
166	node->taglen = bitmaplen;
167	node->taglist = regional_alloc_init(acl->region, bitmap, bitmaplen);
168	if(!node->taglist) {
169		log_err("out of memory");
170		return 0;
171	}
172	return 1;
173}
174
175/** apply acl_view string */
176static int
177acl_list_view_cfg(struct acl_list* acl, const char* str, const char* str2,
178	struct views* vs)
179{
180	struct acl_addr* node;
181	if(!(node=acl_find_or_create(acl, str)))
182		return 0;
183	node->view = views_find_view(vs, str2, 0 /* get read lock*/);
184	if(!node->view) {
185		log_err("no view with name: %s", str2);
186		return 0;
187	}
188	lock_rw_unlock(&node->view->lock);
189	return 1;
190}
191
192/** apply acl_tag_action string */
193static int
194acl_list_tag_action_cfg(struct acl_list* acl, struct config_file* cfg,
195	const char* str, const char* tag, const char* action)
196{
197	struct acl_addr* node;
198	int tagid;
199	enum localzone_type t;
200	if(!(node=acl_find_or_create(acl, str)))
201		return 0;
202	/* allocate array if not yet */
203	if(!node->tag_actions) {
204		node->tag_actions = (uint8_t*)regional_alloc_zero(acl->region,
205			sizeof(*node->tag_actions)*cfg->num_tags);
206		if(!node->tag_actions) {
207			log_err("out of memory");
208			return 0;
209		}
210		node->tag_actions_size = (size_t)cfg->num_tags;
211	}
212	/* parse tag */
213	if((tagid=find_tag_id(cfg, tag)) == -1) {
214		log_err("cannot parse tag (define-tag it): %s %s", str, tag);
215		return 0;
216	}
217	if((size_t)tagid >= node->tag_actions_size) {
218		log_err("tagid too large for array %s %s", str, tag);
219		return 0;
220	}
221	if(!local_zone_str2type(action, &t)) {
222		log_err("cannot parse access control action type: %s %s %s",
223			str, tag, action);
224		return 0;
225	}
226	node->tag_actions[tagid] = (uint8_t)t;
227	return 1;
228}
229
230/** check wire data parse */
231static int
232check_data(const char* data, const struct config_strlist* head)
233{
234	char buf[65536];
235	uint8_t rr[LDNS_RR_BUF_SIZE];
236	size_t len = sizeof(rr);
237	int res;
238	/* '.' is sufficient for validation, and it makes the call to
239	 * sldns_wirerr_get_type() simpler below. */
240	snprintf(buf, sizeof(buf), "%s %s", ".", data);
241	res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, NULL, 0,
242		NULL, 0);
243
244	/* Reject it if we would end up having CNAME and other data (including
245	 * another CNAME) for the same tag. */
246	if(res == 0 && head) {
247		const char* err_data = NULL;
248
249		if(sldns_wirerr_get_type(rr, len, 1) == LDNS_RR_TYPE_CNAME) {
250			/* adding CNAME while other data already exists. */
251			err_data = data;
252		} else {
253			snprintf(buf, sizeof(buf), "%s %s", ".", head->str);
254			len = sizeof(rr);
255			res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600,
256				NULL, 0, NULL, 0);
257			if(res != 0) {
258				/* This should be impossible here as head->str
259				 * has been validated, but we check it just in
260				 * case. */
261				return 0;
262			}
263			if(sldns_wirerr_get_type(rr, len, 1) ==
264				LDNS_RR_TYPE_CNAME) /* already have CNAME */
265				err_data = head->str;
266		}
267		if(err_data) {
268			log_err("redirect tag data '%s' must not coexist with "
269				"other data.", err_data);
270			return 0;
271		}
272	}
273	if(res == 0)
274		return 1;
275	log_err("rr data [char %d] parse error %s",
276		(int)LDNS_WIREPARSE_OFFSET(res)-13,
277		sldns_get_errorstr_parse(res));
278	return 0;
279}
280
281/** apply acl_tag_data string */
282static int
283acl_list_tag_data_cfg(struct acl_list* acl, struct config_file* cfg,
284	const char* str, const char* tag, const char* data)
285{
286	struct acl_addr* node;
287	int tagid;
288	char* dupdata;
289	if(!(node=acl_find_or_create(acl, str)))
290		return 0;
291	/* allocate array if not yet */
292	if(!node->tag_datas) {
293		node->tag_datas = (struct config_strlist**)regional_alloc_zero(
294			acl->region, sizeof(*node->tag_datas)*cfg->num_tags);
295		if(!node->tag_datas) {
296			log_err("out of memory");
297			return 0;
298		}
299		node->tag_datas_size = (size_t)cfg->num_tags;
300	}
301	/* parse tag */
302	if((tagid=find_tag_id(cfg, tag)) == -1) {
303		log_err("cannot parse tag (define-tag it): %s %s", str, tag);
304		return 0;
305	}
306	if((size_t)tagid >= node->tag_datas_size) {
307		log_err("tagid too large for array %s %s", str, tag);
308		return 0;
309	}
310
311	/* check data? */
312	if(!check_data(data, node->tag_datas[tagid])) {
313		log_err("cannot parse access-control-tag data: %s %s '%s'",
314			str, tag, data);
315		return 0;
316	}
317
318	dupdata = regional_strdup(acl->region, data);
319	if(!dupdata) {
320		log_err("out of memory");
321		return 0;
322	}
323	if(!cfg_region_strlist_insert(acl->region,
324		&(node->tag_datas[tagid]), dupdata)) {
325		log_err("out of memory");
326		return 0;
327	}
328	return 1;
329}
330
331/** read acl_list config */
332static int
333read_acl_list(struct acl_list* acl, struct config_file* cfg)
334{
335	struct config_str2list* p;
336	for(p = cfg->acls; p; p = p->next) {
337		log_assert(p->str && p->str2);
338		if(!acl_list_str_cfg(acl, p->str, p->str2, 1))
339			return 0;
340	}
341	return 1;
342}
343
344/** read acl tags config */
345static int
346read_acl_tags(struct acl_list* acl, struct config_file* cfg)
347{
348	struct config_strbytelist* np, *p = cfg->acl_tags;
349	cfg->acl_tags = NULL;
350	while(p) {
351		log_assert(p->str && p->str2);
352		if(!acl_list_tags_cfg(acl, p->str, p->str2, p->str2len)) {
353			config_del_strbytelist(p);
354			return 0;
355		}
356		/* free the items as we go to free up memory */
357		np = p->next;
358		free(p->str);
359		free(p->str2);
360		free(p);
361		p = np;
362	}
363	return 1;
364}
365
366/** read acl view config */
367static int
368read_acl_view(struct acl_list* acl, struct config_file* cfg, struct views* v)
369{
370	struct config_str2list* np, *p = cfg->acl_view;
371	cfg->acl_view = NULL;
372	while(p) {
373		log_assert(p->str && p->str2);
374		if(!acl_list_view_cfg(acl, p->str, p->str2, v)) {
375			return 0;
376		}
377		/* free the items as we go to free up memory */
378		np = p->next;
379		free(p->str);
380		free(p->str2);
381		free(p);
382		p = np;
383	}
384	return 1;
385}
386
387/** read acl tag actions config */
388static int
389read_acl_tag_actions(struct acl_list* acl, struct config_file* cfg)
390{
391	struct config_str3list* p, *np;
392	p = cfg->acl_tag_actions;
393	cfg->acl_tag_actions = NULL;
394	while(p) {
395		log_assert(p->str && p->str2 && p->str3);
396		if(!acl_list_tag_action_cfg(acl, cfg, p->str, p->str2,
397			p->str3)) {
398			config_deltrplstrlist(p);
399			return 0;
400		}
401		/* free the items as we go to free up memory */
402		np = p->next;
403		free(p->str);
404		free(p->str2);
405		free(p->str3);
406		free(p);
407		p = np;
408	}
409	return 1;
410}
411
412/** read acl tag datas config */
413static int
414read_acl_tag_datas(struct acl_list* acl, struct config_file* cfg)
415{
416	struct config_str3list* p, *np;
417	p = cfg->acl_tag_datas;
418	cfg->acl_tag_datas = NULL;
419	while(p) {
420		log_assert(p->str && p->str2 && p->str3);
421		if(!acl_list_tag_data_cfg(acl, cfg, p->str, p->str2, p->str3)) {
422			config_deltrplstrlist(p);
423			return 0;
424		}
425		/* free the items as we go to free up memory */
426		np = p->next;
427		free(p->str);
428		free(p->str2);
429		free(p->str3);
430		free(p);
431		p = np;
432	}
433	return 1;
434}
435
436int
437acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg,
438	struct views* v)
439{
440	regional_free_all(acl->region);
441	addr_tree_init(&acl->tree);
442	if(!read_acl_list(acl, cfg))
443		return 0;
444	if(!read_acl_view(acl, cfg, v))
445		return 0;
446	if(!read_acl_tags(acl, cfg))
447		return 0;
448	if(!read_acl_tag_actions(acl, cfg))
449		return 0;
450	if(!read_acl_tag_datas(acl, cfg))
451		return 0;
452	/* insert defaults, with '0' to ignore them if they are duplicates */
453	if(!acl_list_str_cfg(acl, "0.0.0.0/0", "refuse", 0))
454		return 0;
455	if(!acl_list_str_cfg(acl, "127.0.0.0/8", "allow", 0))
456		return 0;
457	if(cfg->do_ip6) {
458		if(!acl_list_str_cfg(acl, "::0/0", "refuse", 0))
459			return 0;
460		if(!acl_list_str_cfg(acl, "::1", "allow", 0))
461			return 0;
462		if(!acl_list_str_cfg(acl, "::ffff:127.0.0.1", "allow", 0))
463			return 0;
464	}
465	addr_tree_init_parents(&acl->tree);
466	return 1;
467}
468
469enum acl_access
470acl_get_control(struct acl_addr* acl)
471{
472	if(acl) return acl->control;
473	return acl_deny;
474}
475
476struct acl_addr*
477acl_addr_lookup(struct acl_list* acl, struct sockaddr_storage* addr,
478        socklen_t addrlen)
479{
480	return (struct acl_addr*)addr_tree_lookup(&acl->tree,
481		addr, addrlen);
482}
483
484size_t
485acl_list_get_mem(struct acl_list* acl)
486{
487	if(!acl) return 0;
488	return sizeof(*acl) + regional_get_mem(acl->region);
489}
490