acl_list.c revision 307729
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 {
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) */
131static struct acl_addr*
132acl_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 */
157static int
158acl_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 */
174static int
175acl_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 */
212static int
213check_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 */
231static int
232acl_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 */
281static int
282read_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 */
294static int
295read_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 */
316static int
317read_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 */
341static int
342read_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
364int
365acl_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
394enum acl_access
395acl_get_control(struct acl_addr* acl)
396{
397	if(acl) return acl->control;
398	return acl_deny;
399}
400
401struct acl_addr*
402acl_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
409size_t
410acl_list_get_mem(struct acl_list* acl)
411{
412	if(!acl) return 0;
413	return sizeof(*acl) + regional_get_mem(acl->region);
414}
415