aclconf.c 11.1 KB
Newer Older
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1999-2002  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
6
7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9
10
11
12
13
14
15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
16
17
 */

18
/* $Id: aclconf.c,v 1.15 2007/11/19 23:13:28 each Exp $ */
David Lawrence's avatar
David Lawrence committed
19

20
21
#include <config.h>

22
#include <isc/mem.h>
23
#include <isc/string.h>		/* Required for HP/UX (and others?) */
Bob Halley's avatar
Bob Halley committed
24
#include <isc/util.h>
25

26
#include <isccfg/namedconf.h>
27
#include <isccfg/aclconf.h>
28

29
#include <dns/acl.h>
30
#include <dns/iptable.h>
31
#include <dns/fixedname.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
32
#include <dns/log.h>
33

34
#define LOOP_MAGIC ISC_MAGIC('L','O','O','P') 
35

36
void
37
cfg_aclconfctx_init(cfg_aclconfctx_t *ctx) {
38
39
40
	ISC_LIST_INIT(ctx->named_acl_cache);
}

41
void
42
cfg_aclconfctx_destroy(cfg_aclconfctx_t *ctx) {
Mark Andrews's avatar
Mark Andrews committed
43
	dns_acl_t *dacl, *next;
44

45
46
47
48
49
50
51
52
53
	for (dacl = ISC_LIST_HEAD(ctx->named_acl_cache);
	     dacl != NULL;
	     dacl = next)
	{
		next = ISC_LIST_NEXT(dacl, nextincache);
		dns_acl_detach(&dacl);
	}
}

54
55
56
57
/*
 * Find the definition of the named acl whose name is "name".
 */
static isc_result_t
58
get_acl_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
59
	isc_result_t result;
60
61
	const cfg_obj_t *acls = NULL;
	const cfg_listelt_t *elt;
62
63
64
65
66
67
68
	
	result = cfg_map_get(cctx, "acl", &acls);
	if (result != ISC_R_SUCCESS)
		return (result);
	for (elt = cfg_list_first(acls);
	     elt != NULL;
	     elt = cfg_list_next(elt)) {
69
		const cfg_obj_t *acl = cfg_listelt_value(elt);
70
71
		const char *aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
		if (strcasecmp(aclname, name) == 0) {
72
73
74
			if (ret != NULL) {
				*ret = cfg_tuple_get(acl, "value");
			}
75
76
77
78
79
80
			return (ISC_R_SUCCESS);
		}
	}
	return (ISC_R_NOTFOUND);
}

81
static isc_result_t
82
convert_named_acl(const cfg_obj_t *nameobj, const cfg_obj_t *cctx,
83
		  isc_log_t *lctx, cfg_aclconfctx_t *ctx,
84
		  isc_mem_t *mctx, unsigned int nest_level,
Mark Andrews's avatar
Mark Andrews committed
85
		  dns_acl_t **target)
86
87
{
	isc_result_t result;
88
	const cfg_obj_t *cacl = NULL;
89
	dns_acl_t *dacl;
90
	dns_acl_t loop;
91
	const char *aclname = cfg_obj_asstring(nameobj);
92
93
94
95
96
97

	/* Look for an already-converted version. */
	for (dacl = ISC_LIST_HEAD(ctx->named_acl_cache);
	     dacl != NULL;
	     dacl = ISC_LIST_NEXT(dacl, nextincache))
	{
98
		if (strcasecmp(aclname, dacl->name) == 0) {
99
100
101
102
103
			if (ISC_MAGIC_VALID(dacl, LOOP_MAGIC)) {
				cfg_obj_log(nameobj, lctx, ISC_LOG_ERROR,
					    "acl loop detected: %s", aclname);
				return (ISC_R_FAILURE);
			}
104
			dns_acl_attach(dacl, target);
105
			return (ISC_R_SUCCESS);
106
107
108
		}
	}
	/* Not yet converted.  Convert now. */
109
	result = get_acl_def(cctx, aclname, &cacl);
110
	if (result != ISC_R_SUCCESS) {
111
		cfg_obj_log(nameobj, lctx, ISC_LOG_WARNING,
112
			    "undefined ACL '%s'", aclname);
113
114
		return (result);
	}
115
116
117
118
119
	/*
	 * Add a loop detection element.
	 */
	memset(&loop, 0, sizeof(loop));
	ISC_LINK_INIT(&loop, nextincache);
120
	DE_CONST(aclname, loop.name);
121
122
	loop.magic = LOOP_MAGIC;
	ISC_LIST_APPEND(ctx->named_acl_cache, &loop, nextincache);
123
	result = cfg_acl_fromconfig(cacl, cctx, lctx, ctx, mctx,
Mark Andrews's avatar
Mark Andrews committed
124
				    nest_level, &dacl);
125
126
127
	ISC_LIST_UNLINK(ctx->named_acl_cache, &loop, nextincache);
	loop.magic = 0;
	loop.name = NULL;
128
	if (result != ISC_R_SUCCESS)
129
		return (result);
130
	dacl->name = isc_mem_strdup(dacl->mctx, aclname);
131
132
	if (dacl->name == NULL)
		return (ISC_R_NOMEMORY);
133
134
135
136
137
138
	ISC_LIST_APPEND(ctx->named_acl_cache, dacl, nextincache);
	dns_acl_attach(dacl, target);
	return (ISC_R_SUCCESS);
}

static isc_result_t
139
convert_keyname(const cfg_obj_t *keyobj, isc_log_t *lctx, isc_mem_t *mctx,
140
141
		dns_name_t *dnsname)
{
142
143
144
145
	isc_result_t result;
	isc_buffer_t buf;
	dns_fixedname_t fixname;
	unsigned int keylen;
146
	const char *txtname = cfg_obj_asstring(keyobj);
147
148

	keylen = strlen(txtname);
149
	isc_buffer_init(&buf, txtname, keylen);
150
151
152
153
154
	isc_buffer_add(&buf, keylen);
	dns_fixedname_init(&fixname);
	result = dns_name_fromtext(dns_fixedname_name(&fixname), &buf,
				   dns_rootname, ISC_FALSE, NULL);
	if (result != ISC_R_SUCCESS) {
155
		cfg_obj_log(keyobj, lctx, ISC_LOG_WARNING,
156
157
			    "key name '%s' is not a valid domain name",
			    txtname);
158
159
160
161
		return (result);
	}
	return (dns_name_dup(dns_fixedname_name(&fixname), mctx, dnsname));
}
162

163
isc_result_t
164
165
cfg_acl_fromconfig(const cfg_obj_t *caml,
		   const cfg_obj_t *cctx,
Mark Andrews's avatar
Mark Andrews committed
166
		   isc_log_t *lctx,
167
168
		   cfg_aclconfctx_t *ctx,
		   isc_mem_t *mctx,
169
		   unsigned int nest_level,
170
		   dns_acl_t **target)
171
172
{
	isc_result_t result;
173
	dns_acl_t *dacl = NULL, *inneracl = NULL;
174
	dns_aclelement_t *de;
175
	const cfg_listelt_t *elt;
Mark Andrews's avatar
Mark Andrews committed
176
	dns_iptable_t *iptab;
177

178
	REQUIRE(target != NULL);
Mark Andrews's avatar
Mark Andrews committed
179
	REQUIRE(*target == NULL || DNS_ACL_VALID(*target));
180

Mark Andrews's avatar
Mark Andrews committed
181
182
183
184
185
186
187
	if (*target != NULL) {
		/*
		 * If target already points to an ACL, then we're being
		 * called recursively to configure a nested ACL.  The
		 * nested ACL's contents should just be absorbed into its
		 * parent ACL.
		 */
188
189
		dns_acl_attach(*target, &dacl);
		dns_acl_detach(target);
Mark Andrews's avatar
Mark Andrews committed
190
191
192
193
194
195
196
	} else {
		/*
		 * Need to allocate a new ACL structure.  Count the items
		 * in the ACL definition and allocate space for that many
		 * elements (even though some or all of them may end up in
		 * the iptable instead of the element array).
		 */
197
198
199
200
		isc_boolean_t recurse = ISC_TF(nest_level == 0);
		result = dns_acl_create(mctx,
					cfg_list_length(caml, recurse),
					&dacl);
Mark Andrews's avatar
Mark Andrews committed
201
202
203
		if (result != ISC_R_SUCCESS)
			return (result);
	}
204

205
	de = dacl->elements;
206
207
208
	for (elt = cfg_list_first(caml);
	     elt != NULL;
	     elt = cfg_list_next(elt))
209
	{
210
		const cfg_obj_t *ce = cfg_listelt_value(elt);
211
212
		isc_boolean_t	neg;

213
214
215
		if (cfg_obj_istuple(ce)) {
			/* This must be a negated element. */
			ce = cfg_tuple_get(ce, "value");
216
217
218
219
			neg = ISC_TRUE;
		} else
			neg = ISC_FALSE;

Mark Andrews's avatar
Mark Andrews committed
220
221
222
223
224
225
		/*
		 * If nest_level is nonzero, then every element is
		 * to be stored as a separate, nested ACL rather than
		 * merged into the main iptable.
		 */
		iptab = dacl->iptable;
226
227
228
229

		if (nest_level != 0) {
			result = dns_acl_create(mctx,
						cfg_list_length(ce, ISC_FALSE),
Mark Andrews's avatar
Mark Andrews committed
230
231
232
233
234
						&de->nestedacl);
			if (result != ISC_R_SUCCESS)
				goto cleanup;
			iptab = de->nestedacl->iptable;
		}
235
236
237

		if (cfg_obj_isnetprefix(ce)) {
			/* Network prefix */
Mark Andrews's avatar
Mark Andrews committed
238
239
			isc_netaddr_t	addr;
			unsigned int	bitlen;
240

Mark Andrews's avatar
Mark Andrews committed
241
			cfg_obj_asnetprefix(ce, &addr, &bitlen);
Mark Andrews's avatar
Mark Andrews committed
242
243
244
245
246

                        /*
                         * If nesting ACLs (nest_level != 0), we negate
                         * the nestedacl element, not the iptable entry
                         */
Mark Andrews's avatar
Mark Andrews committed
247
			result = dns_iptable_addprefix(iptab, &addr, bitlen,
Mark Andrews's avatar
Mark Andrews committed
248
				              ISC_TF(nest_level != 0 || !neg));
249
250
			if (result != ISC_R_SUCCESS)
				goto cleanup;
Mark Andrews's avatar
Mark Andrews committed
251

252
			if (nest_level != 0) {
Mark Andrews's avatar
Mark Andrews committed
253
254
255
256
				de->type = dns_aclelementtype_nestedacl;
				de->negative = neg;
			} else
				continue;
257
		} else if (cfg_obj_islist(ce)) {
Mark Andrews's avatar
Mark Andrews committed
258
259
260
261
262
263
			/*
			 * If we're nesting ACLs, put the nested
			 * ACL onto the elements list; otherwise
			 * merge it into *this* ACL.
			 */
			if (nest_level == 0) {
Evan Hunt's avatar
Evan Hunt committed
264
265
266
267
268
269
				if (inneracl != NULL)
					dns_acl_detach(&inneracl);

				result = cfg_acl_fromconfig(ce, cctx, lctx,
							    ctx, mctx, 0,
						 	    &inneracl);
Mark Andrews's avatar
Mark Andrews committed
270
271
				if (result != ISC_R_SUCCESS)
					goto cleanup;
Evan Hunt's avatar
Evan Hunt committed
272
273
274
275

				dns_acl_merge(dacl, inneracl,
					      ISC_TF(!neg));
				dns_acl_detach(&inneracl);
Mark Andrews's avatar
Mark Andrews committed
276
277
				continue;
			} else {
278
				de->type = dns_aclelementtype_nestedacl;
Mark Andrews's avatar
Mark Andrews committed
279
				de->negative = neg;
Evan Hunt's avatar
Evan Hunt committed
280
281
282
283
				result = cfg_acl_fromconfig(ce, cctx, lctx,
							    ctx, mctx,
						 	    nest_level - 1,
						 	    &de->nestedacl);
Mark Andrews's avatar
Mark Andrews committed
284
285
286
287
288
289
290
291
292
293
294
				if (result != ISC_R_SUCCESS)
					goto cleanup;
				/* Fall through */
			}
		} else if (cfg_obj_istype(ce, &cfg_type_keyref)) {
			/* Key name */
			de->type = dns_aclelementtype_keyname;
			de->negative = neg;
			dns_name_init(&de->keyname, NULL);
			result = convert_keyname(ce, lctx, mctx,
						 &de->keyname);
295
			if (result != ISC_R_SUCCESS)
Mark Andrews's avatar
Mark Andrews committed
296
				goto cleanup;
297
298
		} else if (cfg_obj_isstring(ce)) {
			/* ACL name */
299
			const char *name = cfg_obj_asstring(ce);
Mark Andrews's avatar
Mark Andrews committed
300
301
			if (strcasecmp(name, "any") == 0) {
				/* iptable entry with zero bit length */
302
303
304
305
306
307
308
309
310
311
				result = dns_iptable_addprefix(iptab, NULL, 0,
				              ISC_TF(nest_level != 0 || !neg));
                                if (result != ISC_R_SUCCESS)
                                        goto cleanup;

                                if (nest_level != 0) {
                                        de->type = dns_aclelementtype_nestedacl;
                                        de->negative = neg;
                                } else
                                        continue;
312
			} else if (strcasecmp(name, "none") == 0) {
Mark Andrews's avatar
Mark Andrews committed
313
				/* negated "any" */
314
315
316
317
318
319
320
321
322
323
				result = dns_iptable_addprefix(iptab, NULL, 0,
				              ISC_TF(nest_level != 0 || neg));
                                if (result != ISC_R_SUCCESS)
                                        goto cleanup;

                                if (nest_level != 0) {
                                        de->type = dns_aclelementtype_nestedacl;
                                        de->negative = !neg;
                                } else
                                        continue;
324
			} else if (strcasecmp(name, "localhost") == 0) {
325
				de->type = dns_aclelementtype_localhost;
Mark Andrews's avatar
Mark Andrews committed
326
				de->negative = neg;
327
328
			} else if (strcasecmp(name, "localnets") == 0) {
				de->type = dns_aclelementtype_localnets;
Mark Andrews's avatar
Mark Andrews committed
329
				de->negative = neg;
330
			} else {
331
332
333
				result = get_acl_def(cctx, name, NULL);
				if (result == ISC_R_SUCCESS) {
					/* found it in acl definitions */
334
335
					if (inneracl != NULL)
						dns_acl_detach(&inneracl);
336
337
					result = convert_named_acl(ce, cctx,
							lctx, ctx, mctx,
338
							(nest_level != 0)
Mark Andrews's avatar
Mark Andrews committed
339
340
341
							  ?  (nest_level - 1)
							  : 0,
							&inneracl);
342
				}
343
344
				if (result != ISC_R_SUCCESS)
					goto cleanup;
345

346
347
				if (nest_level != 0) {
					de->type = dns_aclelementtype_nestedacl;
Mark Andrews's avatar
Mark Andrews committed
348
					de->negative = neg;
349
350
351
352
353
354
					if(de->nestedacl != NULL)
						dns_acl_detach(&de->nestedacl);
					dns_acl_attach(inneracl,
						       &de->nestedacl);
					dns_acl_detach(&inneracl);
					/* Fall through */
Mark Andrews's avatar
Mark Andrews committed
355
356
357
358
				} else {
					dns_acl_merge(dacl, inneracl,
						      ISC_TF(!neg));
					dns_acl_detach(&inneracl);
359
					continue;
Mark Andrews's avatar
Mark Andrews committed
360
				}
361
362
			}
		} else {
363
			cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
364
365
				    "address match list contains "
				    "unsupported element type");
366
367
368
			result = ISC_R_FAILURE;
			goto cleanup;
		}
369

Mark Andrews's avatar
Mark Andrews committed
370
371
372
373
374
		/*
		 * This should only be reached for localhost, localnets
		 * and keyname elements, and nested ACLs if nest_level is
		 * nonzero (i.e., in sortlists). 
		 */
375
376
		if (de->nestedacl != NULL &&
		    de->type != dns_aclelementtype_nestedacl)
Mark Andrews's avatar
Mark Andrews committed
377
378
379
380
			dns_acl_detach(&de->nestedacl);

		dacl->node_count++;
		de->node_num = dacl->node_count;
381

382
383
		de++;
		dacl->length++;
Mark Andrews's avatar
Mark Andrews committed
384
		INSIST(dacl->length <= dacl->alloc);
385
386
	}

387
388
	dns_acl_attach(dacl, target);
	result = ISC_R_SUCCESS;
389

390
 cleanup:
391
392
	if (inneracl != NULL)
		dns_acl_detach(&inneracl);
393
394
395
	dns_acl_detach(&dacl);
	return (result);
}