1 /* $NetBSD: aclconf.c,v 1.1 2024/02/18 20:57:58 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16 #include <inttypes.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19
20 #include <isc/mem.h>
21 #include <isc/print.h>
22 #include <isc/string.h> /* Required for HP/UX (and others?) */
23 #include <isc/util.h>
24
25 #include <dns/acl.h>
26 #include <dns/fixedname.h>
27 #include <dns/iptable.h>
28 #include <dns/log.h>
29
30 #include <isccfg/aclconf.h>
31 #include <isccfg/namedconf.h>
32
33 #define LOOP_MAGIC ISC_MAGIC('L', 'O', 'O', 'P')
34
35 #if defined(HAVE_GEOIP2)
36 static const char *geoip_dbnames[] = {
37 "country", "city", "asnum", "isp", "domain", NULL,
38 };
39 #endif /* if defined(HAVE_GEOIP2) */
40
41 isc_result_t
cfg_aclconfctx_create(isc_mem_t * mctx,cfg_aclconfctx_t ** ret)42 cfg_aclconfctx_create(isc_mem_t *mctx, cfg_aclconfctx_t **ret) {
43 cfg_aclconfctx_t *actx;
44
45 REQUIRE(mctx != NULL);
46 REQUIRE(ret != NULL && *ret == NULL);
47
48 actx = isc_mem_get(mctx, sizeof(*actx));
49
50 isc_refcount_init(&actx->references, 1);
51
52 actx->mctx = NULL;
53 isc_mem_attach(mctx, &actx->mctx);
54 ISC_LIST_INIT(actx->named_acl_cache);
55
56 #if defined(HAVE_GEOIP2)
57 actx->geoip = NULL;
58 #endif /* if defined(HAVE_GEOIP2) */
59
60 *ret = actx;
61 return (ISC_R_SUCCESS);
62 }
63
64 void
cfg_aclconfctx_attach(cfg_aclconfctx_t * src,cfg_aclconfctx_t ** dest)65 cfg_aclconfctx_attach(cfg_aclconfctx_t *src, cfg_aclconfctx_t **dest) {
66 REQUIRE(src != NULL);
67 REQUIRE(dest != NULL && *dest == NULL);
68
69 isc_refcount_increment(&src->references);
70 *dest = src;
71 }
72
73 void
cfg_aclconfctx_detach(cfg_aclconfctx_t ** actxp)74 cfg_aclconfctx_detach(cfg_aclconfctx_t **actxp) {
75 REQUIRE(actxp != NULL && *actxp != NULL);
76 cfg_aclconfctx_t *actx = *actxp;
77 *actxp = NULL;
78
79 if (isc_refcount_decrement(&actx->references) == 1) {
80 dns_acl_t *dacl, *next;
81 isc_refcount_destroy(&actx->references);
82 for (dacl = ISC_LIST_HEAD(actx->named_acl_cache); dacl != NULL;
83 dacl = next)
84 {
85 next = ISC_LIST_NEXT(dacl, nextincache);
86 ISC_LIST_UNLINK(actx->named_acl_cache, dacl,
87 nextincache);
88 dns_acl_detach(&dacl);
89 }
90 isc_mem_putanddetach(&actx->mctx, actx, sizeof(*actx));
91 }
92 }
93
94 /*
95 * Find the definition of the named acl whose name is "name".
96 */
97 static isc_result_t
get_acl_def(const cfg_obj_t * cctx,const char * name,const cfg_obj_t ** ret)98 get_acl_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
99 isc_result_t result;
100 const cfg_obj_t *acls = NULL;
101 const cfg_listelt_t *elt;
102
103 result = cfg_map_get(cctx, "acl", &acls);
104 if (result != ISC_R_SUCCESS) {
105 return (result);
106 }
107 for (elt = cfg_list_first(acls); elt != NULL; elt = cfg_list_next(elt))
108 {
109 const cfg_obj_t *acl = cfg_listelt_value(elt);
110 const char *aclname =
111 cfg_obj_asstring(cfg_tuple_get(acl, "name"));
112 if (strcasecmp(aclname, name) == 0) {
113 if (ret != NULL) {
114 *ret = cfg_tuple_get(acl, "value");
115 }
116 return (ISC_R_SUCCESS);
117 }
118 }
119 return (ISC_R_NOTFOUND);
120 }
121
122 static isc_result_t
convert_named_acl(const cfg_obj_t * nameobj,const cfg_obj_t * cctx,isc_log_t * lctx,cfg_aclconfctx_t * ctx,isc_mem_t * mctx,unsigned int nest_level,dns_acl_t ** target)123 convert_named_acl(const cfg_obj_t *nameobj, const cfg_obj_t *cctx,
124 isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
125 unsigned int nest_level, dns_acl_t **target) {
126 isc_result_t result;
127 const cfg_obj_t *cacl = NULL;
128 dns_acl_t *dacl;
129 dns_acl_t loop;
130 const char *aclname = cfg_obj_asstring(nameobj);
131
132 /* Look for an already-converted version. */
133 for (dacl = ISC_LIST_HEAD(ctx->named_acl_cache); dacl != NULL;
134 dacl = ISC_LIST_NEXT(dacl, nextincache))
135 {
136 /* cppcheck-suppress nullPointerRedundantCheck symbolName=dacl
137 */
138 if (strcasecmp(aclname, dacl->name) == 0) {
139 if (ISC_MAGIC_VALID(dacl, LOOP_MAGIC)) {
140 cfg_obj_log(nameobj, lctx, ISC_LOG_ERROR,
141 "acl loop detected: %s", aclname);
142 return (ISC_R_FAILURE);
143 }
144 dns_acl_attach(dacl, target);
145 return (ISC_R_SUCCESS);
146 }
147 }
148 /* Not yet converted. Convert now. */
149 result = get_acl_def(cctx, aclname, &cacl);
150 if (result != ISC_R_SUCCESS) {
151 cfg_obj_log(nameobj, lctx, ISC_LOG_WARNING,
152 "undefined ACL '%s'", aclname);
153 return (result);
154 }
155 /*
156 * Add a loop detection element.
157 */
158 memset(&loop, 0, sizeof(loop));
159 ISC_LINK_INIT(&loop, nextincache);
160 DE_CONST(aclname, loop.name);
161 loop.magic = LOOP_MAGIC;
162 ISC_LIST_APPEND(ctx->named_acl_cache, &loop, nextincache);
163 result = cfg_acl_fromconfig(cacl, cctx, lctx, ctx, mctx, nest_level,
164 &dacl);
165 ISC_LIST_UNLINK(ctx->named_acl_cache, &loop, nextincache);
166 loop.magic = 0;
167 loop.name = NULL;
168 if (result != ISC_R_SUCCESS) {
169 return (result);
170 }
171 dacl->name = isc_mem_strdup(dacl->mctx, aclname);
172 ISC_LIST_APPEND(ctx->named_acl_cache, dacl, nextincache);
173 dns_acl_attach(dacl, target);
174 return (ISC_R_SUCCESS);
175 }
176
177 static isc_result_t
convert_keyname(const cfg_obj_t * keyobj,isc_log_t * lctx,isc_mem_t * mctx,dns_name_t * dnsname)178 convert_keyname(const cfg_obj_t *keyobj, isc_log_t *lctx, isc_mem_t *mctx,
179 dns_name_t *dnsname) {
180 isc_result_t result;
181 isc_buffer_t buf;
182 dns_fixedname_t fixname;
183 unsigned int keylen;
184 const char *txtname = cfg_obj_asstring(keyobj);
185
186 keylen = strlen(txtname);
187 isc_buffer_constinit(&buf, txtname, keylen);
188 isc_buffer_add(&buf, keylen);
189 dns_fixedname_init(&fixname);
190 result = dns_name_fromtext(dns_fixedname_name(&fixname), &buf,
191 dns_rootname, 0, NULL);
192 if (result != ISC_R_SUCCESS) {
193 cfg_obj_log(keyobj, lctx, ISC_LOG_WARNING,
194 "key name '%s' is not a valid domain name",
195 txtname);
196 return (result);
197 }
198 dns_name_dup(dns_fixedname_name(&fixname), mctx, dnsname);
199 return (ISC_R_SUCCESS);
200 }
201
202 /*
203 * Recursively pre-parse an ACL definition to find the total number
204 * of non-IP-prefix elements (localhost, localnets, key) in all nested
205 * ACLs, so that the parent will have enough space allocated for the
206 * elements table after all the nested ACLs have been merged in to the
207 * parent.
208 */
209 static isc_result_t
count_acl_elements(const cfg_obj_t * caml,const cfg_obj_t * cctx,isc_log_t * lctx,cfg_aclconfctx_t * ctx,isc_mem_t * mctx,uint32_t * count,bool * has_negative)210 count_acl_elements(const cfg_obj_t *caml, const cfg_obj_t *cctx,
211 isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
212 uint32_t *count, bool *has_negative) {
213 const cfg_listelt_t *elt;
214 isc_result_t result;
215 uint32_t n = 0;
216
217 REQUIRE(count != NULL);
218
219 if (has_negative != NULL) {
220 *has_negative = false;
221 }
222
223 for (elt = cfg_list_first(caml); elt != NULL; elt = cfg_list_next(elt))
224 {
225 const cfg_obj_t *ce = cfg_listelt_value(elt);
226
227 /* might be a negated element, in which case get the value. */
228 if (cfg_obj_istuple(ce)) {
229 const cfg_obj_t *negated = cfg_tuple_get(ce, "negated");
230 if (!cfg_obj_isvoid(negated)) {
231 ce = negated;
232 if (has_negative != NULL) {
233 *has_negative = true;
234 }
235 }
236 }
237
238 if (cfg_obj_istype(ce, &cfg_type_keyref)) {
239 n++;
240 } else if (cfg_obj_islist(ce)) {
241 bool negative;
242 uint32_t sub;
243 result = count_acl_elements(ce, cctx, lctx, ctx, mctx,
244 &sub, &negative);
245 if (result != ISC_R_SUCCESS) {
246 return (result);
247 }
248 n += sub;
249 if (negative) {
250 n++;
251 }
252 #if defined(HAVE_GEOIP2)
253 } else if (cfg_obj_istuple(ce) &&
254 cfg_obj_isvoid(cfg_tuple_get(ce, "negated")))
255 {
256 n++;
257 #endif /* HAVE_GEOIP2 */
258 } else if (cfg_obj_isstring(ce)) {
259 const char *name = cfg_obj_asstring(ce);
260 if (strcasecmp(name, "localhost") == 0 ||
261 strcasecmp(name, "localnets") == 0 ||
262 strcasecmp(name, "none") == 0)
263 {
264 n++;
265 } else if (strcasecmp(name, "any") != 0) {
266 dns_acl_t *inneracl = NULL;
267 /*
268 * Convert any named acls we reference now if
269 * they have not already been converted.
270 */
271 result = convert_named_acl(ce, cctx, lctx, ctx,
272 mctx, 0, &inneracl);
273 if (result == ISC_R_SUCCESS) {
274 if (inneracl->has_negatives) {
275 n++;
276 } else {
277 n += inneracl->length;
278 }
279 dns_acl_detach(&inneracl);
280 } else {
281 return (result);
282 }
283 }
284 }
285 }
286
287 *count = n;
288 return (ISC_R_SUCCESS);
289 }
290
291 #if defined(HAVE_GEOIP2)
292 static dns_geoip_subtype_t
get_subtype(const cfg_obj_t * obj,isc_log_t * lctx,dns_geoip_subtype_t subtype,const char * dbname)293 get_subtype(const cfg_obj_t *obj, isc_log_t *lctx, dns_geoip_subtype_t subtype,
294 const char *dbname) {
295 if (dbname == NULL) {
296 return (subtype);
297 }
298
299 switch (subtype) {
300 case dns_geoip_countrycode:
301 if (strcasecmp(dbname, "city") == 0) {
302 return (dns_geoip_city_countrycode);
303 } else if (strcasecmp(dbname, "country") == 0) {
304 return (dns_geoip_country_code);
305 }
306 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
307 "invalid database specified for "
308 "country search: ignored");
309 return (subtype);
310 case dns_geoip_countryname:
311 if (strcasecmp(dbname, "city") == 0) {
312 return (dns_geoip_city_countryname);
313 } else if (strcasecmp(dbname, "country") == 0) {
314 return (dns_geoip_country_name);
315 }
316 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
317 "invalid database specified for "
318 "country search: ignored");
319 return (subtype);
320 case dns_geoip_continentcode:
321 if (strcasecmp(dbname, "city") == 0) {
322 return (dns_geoip_city_continentcode);
323 } else if (strcasecmp(dbname, "country") == 0) {
324 return (dns_geoip_country_continentcode);
325 }
326 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
327 "invalid database specified for "
328 "continent search: ignored");
329 return (subtype);
330 case dns_geoip_continent:
331 if (strcasecmp(dbname, "city") == 0) {
332 return (dns_geoip_city_continent);
333 } else if (strcasecmp(dbname, "country") == 0) {
334 return (dns_geoip_country_continent);
335 }
336 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
337 "invalid database specified for "
338 "continent search: ignored");
339 return (subtype);
340 case dns_geoip_region:
341 if (strcasecmp(dbname, "city") == 0) {
342 return (dns_geoip_city_region);
343 }
344 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
345 "invalid database specified for "
346 "region/subdivision search: ignored");
347 return (subtype);
348 case dns_geoip_regionname:
349 if (strcasecmp(dbname, "city") == 0) {
350 return (dns_geoip_city_regionname);
351 }
352 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
353 "invalid database specified for "
354 "region/subdivision search: ignored");
355 return (subtype);
356
357 /*
358 * Log a warning if the wrong database was specified
359 * on an unambiguous query
360 */
361 case dns_geoip_city_name:
362 case dns_geoip_city_postalcode:
363 case dns_geoip_city_metrocode:
364 case dns_geoip_city_areacode:
365 case dns_geoip_city_timezonecode:
366 if (strcasecmp(dbname, "city") != 0) {
367 cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
368 "invalid database specified for "
369 "a 'city'-only search type: ignoring");
370 }
371 return (subtype);
372 case dns_geoip_isp_name:
373 if (strcasecmp(dbname, "isp") != 0) {
374 cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
375 "invalid database specified for "
376 "an 'isp' search: ignoring");
377 }
378 return (subtype);
379 case dns_geoip_org_name:
380 if (strcasecmp(dbname, "org") != 0) {
381 cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
382 "invalid database specified for "
383 "an 'org' search: ignoring");
384 }
385 return (subtype);
386 case dns_geoip_as_asnum:
387 if (strcasecmp(dbname, "asnum") != 0) {
388 cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
389 "invalid database specified for "
390 "an 'asnum' search: ignoring");
391 }
392 return (subtype);
393 case dns_geoip_domain_name:
394 if (strcasecmp(dbname, "domain") != 0) {
395 cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
396 "invalid database specified for "
397 "a 'domain' search: ignoring");
398 }
399 return (subtype);
400 case dns_geoip_netspeed_id:
401 if (strcasecmp(dbname, "netspeed") != 0) {
402 cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
403 "invalid database specified for "
404 "a 'netspeed' search: ignoring");
405 }
406 return (subtype);
407 default:
408 UNREACHABLE();
409 }
410 }
411
412 static bool
geoip_can_answer(dns_aclelement_t * elt,cfg_aclconfctx_t * ctx)413 geoip_can_answer(dns_aclelement_t *elt, cfg_aclconfctx_t *ctx) {
414 if (ctx->geoip == NULL) {
415 return (true);
416 }
417
418 switch (elt->geoip_elem.subtype) {
419 case dns_geoip_countrycode:
420 case dns_geoip_countryname:
421 case dns_geoip_continentcode:
422 case dns_geoip_continent:
423 if (ctx->geoip->country != NULL || ctx->geoip->city != NULL) {
424 return (true);
425 }
426 break;
427 case dns_geoip_country_code:
428 case dns_geoip_country_name:
429 case dns_geoip_country_continentcode:
430 case dns_geoip_country_continent:
431 if (ctx->geoip->country != NULL) {
432 return (true);
433 }
434 /* city db can answer these too, so: */
435 FALLTHROUGH;
436 case dns_geoip_region:
437 case dns_geoip_regionname:
438 case dns_geoip_city_countrycode:
439 case dns_geoip_city_countryname:
440 case dns_geoip_city_region:
441 case dns_geoip_city_regionname:
442 case dns_geoip_city_name:
443 case dns_geoip_city_postalcode:
444 case dns_geoip_city_metrocode:
445 case dns_geoip_city_areacode:
446 case dns_geoip_city_continentcode:
447 case dns_geoip_city_continent:
448 case dns_geoip_city_timezonecode:
449 if (ctx->geoip->city != NULL) {
450 return (true);
451 }
452 break;
453 case dns_geoip_isp_name:
454 if (ctx->geoip->isp != NULL) {
455 return (true);
456 }
457 break;
458 case dns_geoip_as_asnum:
459 case dns_geoip_org_name:
460 if (ctx->geoip->as != NULL) {
461 return (true);
462 }
463 break;
464 case dns_geoip_domain_name:
465 if (ctx->geoip->domain != NULL) {
466 return (true);
467 }
468 break;
469 default:
470 break;
471 }
472
473 return (false);
474 }
475
476 static isc_result_t
parse_geoip_element(const cfg_obj_t * obj,isc_log_t * lctx,cfg_aclconfctx_t * ctx,dns_aclelement_t * dep)477 parse_geoip_element(const cfg_obj_t *obj, isc_log_t *lctx,
478 cfg_aclconfctx_t *ctx, dns_aclelement_t *dep) {
479 const cfg_obj_t *ge;
480 const char *dbname = NULL;
481 const char *stype = NULL, *search = NULL;
482 dns_geoip_subtype_t subtype;
483 dns_aclelement_t de;
484 size_t len;
485
486 REQUIRE(dep != NULL);
487
488 de = *dep;
489
490 ge = cfg_tuple_get(obj, "db");
491 if (!cfg_obj_isvoid(ge)) {
492 int i;
493
494 dbname = cfg_obj_asstring(ge);
495
496 for (i = 0; geoip_dbnames[i] != NULL; i++) {
497 if (strcasecmp(dbname, geoip_dbnames[i]) == 0) {
498 break;
499 }
500 }
501 if (geoip_dbnames[i] == NULL) {
502 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
503 "database '%s' is not defined for GeoIP2",
504 dbname);
505 return (ISC_R_UNEXPECTED);
506 }
507 }
508
509 stype = cfg_obj_asstring(cfg_tuple_get(obj, "subtype"));
510 search = cfg_obj_asstring(cfg_tuple_get(obj, "search"));
511 len = strlen(search);
512
513 if (len == 0) {
514 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
515 "zero-length geoip search field");
516 return (ISC_R_FAILURE);
517 }
518
519 if (strcasecmp(stype, "country") == 0 && len == 2) {
520 /* Two-letter country code */
521 subtype = dns_geoip_countrycode;
522 strlcpy(de.geoip_elem.as_string, search,
523 sizeof(de.geoip_elem.as_string));
524 } else if (strcasecmp(stype, "country") == 0 && len == 3) {
525 /* Three-letter country code */
526 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
527 "three-letter country codes are unavailable "
528 "in GeoIP2 databases");
529 return (ISC_R_FAILURE);
530 } else if (strcasecmp(stype, "country") == 0) {
531 /* Country name */
532 subtype = dns_geoip_countryname;
533 strlcpy(de.geoip_elem.as_string, search,
534 sizeof(de.geoip_elem.as_string));
535 } else if (strcasecmp(stype, "continent") == 0 && len == 2) {
536 /* Two-letter continent code */
537 subtype = dns_geoip_continentcode;
538 strlcpy(de.geoip_elem.as_string, search,
539 sizeof(de.geoip_elem.as_string));
540 } else if (strcasecmp(stype, "continent") == 0) {
541 subtype = dns_geoip_continent;
542 strlcpy(de.geoip_elem.as_string, search,
543 sizeof(de.geoip_elem.as_string));
544 } else if ((strcasecmp(stype, "region") == 0 ||
545 strcasecmp(stype, "subdivision") == 0) &&
546 len == 2)
547 {
548 /* Two-letter region code */
549 subtype = dns_geoip_region;
550 strlcpy(de.geoip_elem.as_string, search,
551 sizeof(de.geoip_elem.as_string));
552 } else if (strcasecmp(stype, "region") == 0 ||
553 strcasecmp(stype, "subdivision") == 0)
554 {
555 /* Region name */
556 subtype = dns_geoip_regionname;
557 strlcpy(de.geoip_elem.as_string, search,
558 sizeof(de.geoip_elem.as_string));
559 } else if (strcasecmp(stype, "city") == 0) {
560 /* City name */
561 subtype = dns_geoip_city_name;
562 strlcpy(de.geoip_elem.as_string, search,
563 sizeof(de.geoip_elem.as_string));
564 } else if (strcasecmp(stype, "postal") == 0 ||
565 strcasecmp(stype, "postalcode") == 0)
566 {
567 if (len < 7) {
568 subtype = dns_geoip_city_postalcode;
569 strlcpy(de.geoip_elem.as_string, search,
570 sizeof(de.geoip_elem.as_string));
571 } else {
572 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
573 "geoiop postal code (%s) too long", search);
574 return (ISC_R_FAILURE);
575 }
576 } else if (strcasecmp(stype, "metro") == 0 ||
577 strcasecmp(stype, "metrocode") == 0)
578 {
579 subtype = dns_geoip_city_metrocode;
580 de.geoip_elem.as_int = atoi(search);
581 } else if (strcasecmp(stype, "tz") == 0 ||
582 strcasecmp(stype, "timezone") == 0)
583 {
584 subtype = dns_geoip_city_timezonecode;
585 strlcpy(de.geoip_elem.as_string, search,
586 sizeof(de.geoip_elem.as_string));
587 } else if (strcasecmp(stype, "isp") == 0) {
588 subtype = dns_geoip_isp_name;
589 strlcpy(de.geoip_elem.as_string, search,
590 sizeof(de.geoip_elem.as_string));
591 } else if (strcasecmp(stype, "asnum") == 0) {
592 subtype = dns_geoip_as_asnum;
593 strlcpy(de.geoip_elem.as_string, search,
594 sizeof(de.geoip_elem.as_string));
595 } else if (strcasecmp(stype, "org") == 0) {
596 subtype = dns_geoip_org_name;
597 strlcpy(de.geoip_elem.as_string, search,
598 sizeof(de.geoip_elem.as_string));
599 } else if (strcasecmp(stype, "domain") == 0) {
600 subtype = dns_geoip_domain_name;
601 strlcpy(de.geoip_elem.as_string, search,
602 sizeof(de.geoip_elem.as_string));
603 } else {
604 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
605 "type '%s' is unavailable "
606 "in GeoIP2 databases",
607 stype);
608 return (ISC_R_FAILURE);
609 }
610
611 de.geoip_elem.subtype = get_subtype(obj, lctx, subtype, dbname);
612
613 if (!geoip_can_answer(&de, ctx)) {
614 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
615 "no GeoIP2 database installed which can answer "
616 "queries of type '%s'",
617 stype);
618 return (ISC_R_FAILURE);
619 }
620
621 *dep = de;
622
623 return (ISC_R_SUCCESS);
624 }
625 #endif /* HAVE_GEOIP2 */
626
627 isc_result_t
cfg_acl_fromconfig(const cfg_obj_t * caml,const cfg_obj_t * cctx,isc_log_t * lctx,cfg_aclconfctx_t * ctx,isc_mem_t * mctx,unsigned int nest_level,dns_acl_t ** target)628 cfg_acl_fromconfig(const cfg_obj_t *caml, const cfg_obj_t *cctx,
629 isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
630 unsigned int nest_level, dns_acl_t **target) {
631 return (cfg_acl_fromconfig2(caml, cctx, lctx, ctx, mctx, nest_level, 0,
632 target));
633 }
634
635 isc_result_t
cfg_acl_fromconfig2(const cfg_obj_t * caml,const cfg_obj_t * cctx,isc_log_t * lctx,cfg_aclconfctx_t * ctx,isc_mem_t * mctx,unsigned int nest_level,uint16_t family,dns_acl_t ** target)636 cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx,
637 isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
638 unsigned int nest_level, uint16_t family,
639 dns_acl_t **target) {
640 isc_result_t result;
641 dns_acl_t *dacl = NULL, *inneracl = NULL;
642 dns_aclelement_t *de;
643 const cfg_listelt_t *elt;
644 dns_iptable_t *iptab;
645 int new_nest_level = 0;
646 bool setpos;
647
648 if (nest_level != 0) {
649 new_nest_level = nest_level - 1;
650 }
651
652 REQUIRE(ctx != NULL);
653 REQUIRE(target != NULL);
654 REQUIRE(*target == NULL || DNS_ACL_VALID(*target));
655
656 if (*target != NULL) {
657 /*
658 * If target already points to an ACL, then we're being
659 * called recursively to configure a nested ACL. The
660 * nested ACL's contents should just be absorbed into its
661 * parent ACL.
662 */
663 dns_acl_attach(*target, &dacl);
664 dns_acl_detach(target);
665 } else {
666 /*
667 * Need to allocate a new ACL structure. Count the items
668 * in the ACL definition that will require space in the
669 * elements table. (Note that if nest_level is nonzero,
670 * *everything* goes in the elements table.)
671 */
672 uint32_t nelem;
673
674 if (nest_level == 0) {
675 result = count_acl_elements(caml, cctx, lctx, ctx, mctx,
676 &nelem, NULL);
677 if (result != ISC_R_SUCCESS) {
678 return (result);
679 }
680 } else {
681 nelem = cfg_list_length(caml, false);
682 }
683
684 result = dns_acl_create(mctx, nelem, &dacl);
685 if (result != ISC_R_SUCCESS) {
686 return (result);
687 }
688 }
689
690 de = dacl->elements;
691 for (elt = cfg_list_first(caml); elt != NULL; elt = cfg_list_next(elt))
692 {
693 const cfg_obj_t *ce = cfg_listelt_value(elt);
694 bool neg = false;
695
696 INSIST(dacl->length <= dacl->alloc);
697
698 if (cfg_obj_istuple(ce)) {
699 /* Might be a negated element */
700 const cfg_obj_t *negated = cfg_tuple_get(ce, "negated");
701 if (!cfg_obj_isvoid(negated)) {
702 neg = true;
703 dacl->has_negatives = true;
704 ce = negated;
705 }
706 }
707
708 /*
709 * If nest_level is nonzero, then every element is
710 * to be stored as a separate, nested ACL rather than
711 * merged into the main iptable.
712 */
713 iptab = dacl->iptable;
714
715 if (nest_level != 0) {
716 result = dns_acl_create(mctx,
717 cfg_list_length(ce, false),
718 &de->nestedacl);
719 if (result != ISC_R_SUCCESS) {
720 goto cleanup;
721 }
722 iptab = de->nestedacl->iptable;
723 }
724
725 if (cfg_obj_isnetprefix(ce)) {
726 /* Network prefix */
727 isc_netaddr_t addr;
728 unsigned int bitlen;
729
730 cfg_obj_asnetprefix(ce, &addr, &bitlen);
731 if (family != 0 && family != addr.family) {
732 char buf[ISC_NETADDR_FORMATSIZE + 1];
733 isc_netaddr_format(&addr, buf, sizeof(buf));
734 cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
735 "'%s': incorrect address family; "
736 "ignoring",
737 buf);
738 if (nest_level != 0) {
739 dns_acl_detach(&de->nestedacl);
740 }
741 continue;
742 }
743 result = isc_netaddr_prefixok(&addr, bitlen);
744 if (result != ISC_R_SUCCESS) {
745 char buf[ISC_NETADDR_FORMATSIZE + 1];
746 isc_netaddr_format(&addr, buf, sizeof(buf));
747 cfg_obj_log(ce, lctx, ISC_LOG_ERROR,
748 "'%s/%u': address/prefix length "
749 "mismatch",
750 buf, bitlen);
751 goto cleanup;
752 }
753
754 /*
755 * If nesting ACLs (nest_level != 0), we negate
756 * the nestedacl element, not the iptable entry.
757 */
758 setpos = (nest_level != 0 || !neg);
759 result = dns_iptable_addprefix(iptab, &addr, bitlen,
760 setpos);
761 if (result != ISC_R_SUCCESS) {
762 goto cleanup;
763 }
764
765 if (nest_level > 0) {
766 INSIST(dacl->length < dacl->alloc);
767 de->type = dns_aclelementtype_nestedacl;
768 de->negative = neg;
769 } else {
770 continue;
771 }
772 } else if (cfg_obj_islist(ce)) {
773 /*
774 * If we're nesting ACLs, put the nested
775 * ACL onto the elements list; otherwise
776 * merge it into *this* ACL. We nest ACLs
777 * in two cases: 1) sortlist, 2) if the
778 * nested ACL contains negated members.
779 */
780 if (inneracl != NULL) {
781 dns_acl_detach(&inneracl);
782 }
783 result = cfg_acl_fromconfig(ce, cctx, lctx, ctx, mctx,
784 new_nest_level, &inneracl);
785 if (result != ISC_R_SUCCESS) {
786 goto cleanup;
787 }
788 nested_acl:
789 if (nest_level > 0 || inneracl->has_negatives) {
790 INSIST(dacl->length < dacl->alloc);
791 de->type = dns_aclelementtype_nestedacl;
792 de->negative = neg;
793 if (de->nestedacl != NULL) {
794 dns_acl_detach(&de->nestedacl);
795 }
796 dns_acl_attach(inneracl, &de->nestedacl);
797 dns_acl_detach(&inneracl);
798 /* Fall through. */
799 } else {
800 INSIST(dacl->length + inneracl->length <=
801 dacl->alloc);
802 dns_acl_merge(dacl, inneracl, !neg);
803 de += inneracl->length; /* elements added */
804 dns_acl_detach(&inneracl);
805 INSIST(dacl->length <= dacl->alloc);
806 continue;
807 }
808 } else if (cfg_obj_istype(ce, &cfg_type_keyref)) {
809 /* Key name. */
810 INSIST(dacl->length < dacl->alloc);
811 de->type = dns_aclelementtype_keyname;
812 de->negative = neg;
813 dns_name_init(&de->keyname, NULL);
814 result = convert_keyname(ce, lctx, mctx, &de->keyname);
815 if (result != ISC_R_SUCCESS) {
816 goto cleanup;
817 }
818 #if defined(HAVE_GEOIP2)
819 } else if (cfg_obj_istuple(ce) &&
820 cfg_obj_isvoid(cfg_tuple_get(ce, "negated")))
821 {
822 INSIST(dacl->length < dacl->alloc);
823 result = parse_geoip_element(ce, lctx, ctx, de);
824 if (result != ISC_R_SUCCESS) {
825 goto cleanup;
826 }
827 de->type = dns_aclelementtype_geoip;
828 de->negative = neg;
829 #endif /* HAVE_GEOIP2 */
830 } else if (cfg_obj_isstring(ce)) {
831 /* ACL name. */
832 const char *name = cfg_obj_asstring(ce);
833 if (strcasecmp(name, "any") == 0) {
834 /* Iptable entry with zero bit length. */
835 setpos = (nest_level != 0 || !neg);
836 result = dns_iptable_addprefix(iptab, NULL, 0,
837 setpos);
838 if (result != ISC_R_SUCCESS) {
839 goto cleanup;
840 }
841
842 if (nest_level != 0) {
843 INSIST(dacl->length < dacl->alloc);
844 de->type = dns_aclelementtype_nestedacl;
845 de->negative = neg;
846 } else {
847 continue;
848 }
849 } else if (strcasecmp(name, "none") == 0) {
850 /* none == !any */
851 /*
852 * We don't unconditional set
853 * dacl->has_negatives and
854 * de->negative to true so we can handle
855 * "!none;".
856 */
857 setpos = (nest_level != 0 || neg);
858 result = dns_iptable_addprefix(iptab, NULL, 0,
859 setpos);
860 if (result != ISC_R_SUCCESS) {
861 goto cleanup;
862 }
863
864 if (!neg) {
865 dacl->has_negatives = !neg;
866 }
867
868 if (nest_level != 0) {
869 INSIST(dacl->length < dacl->alloc);
870 de->type = dns_aclelementtype_nestedacl;
871 de->negative = !neg;
872 } else {
873 continue;
874 }
875 } else if (strcasecmp(name, "localhost") == 0) {
876 INSIST(dacl->length < dacl->alloc);
877 de->type = dns_aclelementtype_localhost;
878 de->negative = neg;
879 } else if (strcasecmp(name, "localnets") == 0) {
880 INSIST(dacl->length < dacl->alloc);
881 de->type = dns_aclelementtype_localnets;
882 de->negative = neg;
883 } else {
884 if (inneracl != NULL) {
885 dns_acl_detach(&inneracl);
886 }
887 /*
888 * This call should just find the cached
889 * of the named acl.
890 */
891 result = convert_named_acl(ce, cctx, lctx, ctx,
892 mctx, new_nest_level,
893 &inneracl);
894 if (result != ISC_R_SUCCESS) {
895 goto cleanup;
896 }
897
898 goto nested_acl;
899 }
900 } else {
901 cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
902 "address match list contains "
903 "unsupported element type");
904 result = ISC_R_FAILURE;
905 goto cleanup;
906 }
907
908 /*
909 * This should only be reached for localhost, localnets
910 * and keyname elements, and nested ACLs if nest_level is
911 * nonzero (i.e., in sortlists).
912 */
913 if (de->nestedacl != NULL &&
914 de->type != dns_aclelementtype_nestedacl)
915 {
916 dns_acl_detach(&de->nestedacl);
917 }
918
919 dns_acl_node_count(dacl)++;
920 de->node_num = dns_acl_node_count(dacl);
921
922 dacl->length++;
923 de++;
924 INSIST(dacl->length <= dacl->alloc);
925 }
926
927 dns_acl_attach(dacl, target);
928 result = ISC_R_SUCCESS;
929
930 cleanup:
931 if (inneracl != NULL) {
932 dns_acl_detach(&inneracl);
933 }
934 dns_acl_detach(&dacl);
935 return (result);
936 }
937