diff --git a/bin/named/server.c b/bin/named/server.c index 8441148606..f7955a421a 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -4859,33 +4859,16 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, INSIST(result == ISC_R_SUCCESS); view->root_key_sentinel = cfg_obj_asboolean(obj); - /* - * Set the "allow-query", "allow-query-cache", "allow-recursion", - * "allow-recursion-on" and "allow-query-cache-on" ACLs if - * configured in named.conf, but NOT from the global defaults. - * This is done by leaving the third argument to configure_view_acl() - * NULL. - * - * We ignore the global defaults here because these ACLs - * can inherit from each other. If any are still unset after - * applying the inheritance rules, we'll look up the defaults at - * that time. - */ - - /* named.conf only */ CHECK(configure_view_acl(vconfig, config, "allow-query", NULL, aclctx, isc_g_mctx, &view->queryacl)); - - /* named.conf only */ - CHECK(configure_view_acl(vconfig, config, "allow-query-cache", NULL, - aclctx, isc_g_mctx, &view->cacheacl)); - /* named.conf only */ - CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on", NULL, - aclctx, isc_g_mctx, &view->cacheonacl)); - CHECK(configure_view_acl(vconfig, config, "allow-query-on", NULL, aclctx, isc_g_mctx, &view->queryonacl)); + CHECK(configure_view_acl(vconfig, config, "allow-query-cache", NULL, + aclctx, isc_g_mctx, &view->cacheacl)); + CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on", NULL, + aclctx, isc_g_mctx, &view->cacheonacl)); + CHECK(configure_view_acl(vconfig, config, "allow-proxy", NULL, aclctx, isc_g_mctx, &view->proxyacl)); @@ -4895,39 +4878,14 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, if (strcmp(view->name, "_bind") != 0 && view->rdclass != dns_rdataclass_chaos) { - /* named.conf only */ CHECK(configure_view_acl(vconfig, config, "allow-recursion", NULL, aclctx, isc_g_mctx, &view->recursionacl)); - /* named.conf only */ CHECK(configure_view_acl(vconfig, config, "allow-recursion-on", NULL, aclctx, isc_g_mctx, &view->recursiononacl)); } - if (view->recursion == false) { - /* - * We're not recursive; if the query-cache ACLs haven't - * been set at the options/view level, set them to none. - */ - if (view->cacheacl == NULL) { - CHECK(dns_acl_none(mctx, &view->cacheacl)); - } - if (view->cacheonacl == NULL) { - CHECK(dns_acl_none(mctx, &view->cacheonacl)); - } - } - - /* - * Finished setting recursion and query-cache ACLs, so now we - * can get the allow-query default if it wasn't set in named.conf - */ - if (view->queryacl == NULL) { - /* global default only */ - CHECK(configure_view_acl(NULL, NULL, "allow-query", NULL, - aclctx, isc_g_mctx, &view->queryacl)); - } - /* * Ignore case when compressing responses to the specified * clients. This causes case not always to be preserved, diff --git a/bin/tests/system/allow-query/ns3/named5.conf.in b/bin/tests/system/allow-query/ns3/named5.conf.in new file mode 100644 index 0000000000..81edf10e34 --- /dev/null +++ b/bin/tests/system/allow-query/ns3/named5.conf.in @@ -0,0 +1,42 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.3; 10.53.1.2; }; + listen-on-v6 { none; }; + recursion no; + allow-recursion { none; }; + dnssec-validation no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +view internal { + match-destinations { 10.53.0.3; }; + zone "." { + type hint; + file "../../_common/root.hint"; + }; + + recursion yes; + allow-recursion { any; }; +}; diff --git a/bin/tests/system/allow-query/ns3/named6.conf.in b/bin/tests/system/allow-query/ns3/named6.conf.in new file mode 100644 index 0000000000..94e796ba24 --- /dev/null +++ b/bin/tests/system/allow-query/ns3/named6.conf.in @@ -0,0 +1,41 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + recursion no; + allow-query-cache { none; }; + dnssec-validation no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +view internal { + match-destinations { 10.53.0.3; }; + zone "." { + type hint; + file "../../_common/root.hint"; + }; + recursion yes; + allow-recursion{ any; }; +}; diff --git a/bin/tests/system/allow-query/ns3/named7.conf.in b/bin/tests/system/allow-query/ns3/named7.conf.in new file mode 100644 index 0000000000..28a05bf171 --- /dev/null +++ b/bin/tests/system/allow-query/ns3/named7.conf.in @@ -0,0 +1,41 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + recursion no; + allow-recursion { none; }; + dnssec-validation no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +view internal { + match-destinations { 10.53.0.3; }; + zone "." { + type hint; + file "../../_common/root.hint"; + }; + recursion yes; + allow-query{ any; }; +}; diff --git a/bin/tests/system/allow-query/ns3/named8.conf.in b/bin/tests/system/allow-query/ns3/named8.conf.in new file mode 100644 index 0000000000..a248f55ac4 --- /dev/null +++ b/bin/tests/system/allow-query/ns3/named8.conf.in @@ -0,0 +1,42 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.3; 10.53.0.4; 10.53.1.2; }; + listen-on-v6 { none; }; + recursion no; + allow-query-cache { none; }; + dnssec-validation no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +view internal { + zone "." { + type hint; + file "../../_common/root.hint"; + }; + + recursion yes; + allow-query-cache { 10.53.0.3; 10.53.0.4; }; + allow-query { 10.53.0.4; }; +}; diff --git a/bin/tests/system/allow-query/tests.sh b/bin/tests/system/allow-query/tests.sh index bfacf76508..f332ce15e1 100644 --- a/bin/tests/system/allow-query/tests.sh +++ b/bin/tests/system/allow-query/tests.sh @@ -734,5 +734,76 @@ nextpart ns3/named.run | grep 'allow-recursion-on did not match' >/dev/null || r if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) +# Test 63 - allow-query-cache inheritance from allow-recursion +n=$((n + 1)) +copy_setports ns3/named5.conf.in ns3/named.conf +rndc_reload ns3 10.53.0.3 + +echo_i "test $n: inheritance of allow-query-cache from allow-recursion" +ret=0 +# this should be allowed +$DIG -p ${PORT} @10.53.0.3 e.normal.example a >dig.out.ns3.1.$n || ret=1 +grep 'ANSWER: 1' dig.out.ns3.1.$n >/dev/null || ret=1 +# this should be prohibited +$DIG -p ${PORT} @10.53.1.2 f.normal.example a >dig.out.ns3.2.$n || ret=1 +grep 'recursion requested but not available' dig.out.ns3.2.$n >/dev/null || ret=1 +grep 'status: REFUSED' dig.out.ns3.2.$n >/dev/null || ret=1 +grep 'EDE: 18 (Prohibited)' dig.out.ns3.2.$n >/dev/null || ret=1 +grep 'EDE: 20' dig.out.ns3.2.$n >/dev/null && ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# Test 64 - allow-query-cache no inheritance from allow-recursion as it is +# defined in the options +n=$((n + 1)) +copy_setports ns3/named6.conf.in ns3/named.conf +rndc_reload ns3 10.53.0.3 + +echo_i "test $n: allow-query-cache defined in options, so it does not inherit from allow-recursion" +ret=0 +$DIG -p ${PORT} @10.53.0.3 f.normal.example a >dig.out.ns3.1.$n || ret=1 +grep 'recursion requested but not available' dig.out.ns3.1.$n >/dev/null || ret=1 +grep 'status: REFUSED' dig.out.ns3.1.$n >/dev/null || ret=1 +grep 'EDE: 18 (Prohibited)' dig.out.ns3.1.$n >/dev/null || ret=1 +grep 'EDE: 20' dig.out.ns3.1.$n >/dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# Test 65 - allow-query-cache inherits from allow-recursion before allow-query +n=$((n + 1)) +copy_setports ns3/named7.conf.in ns3/named.conf +rndc_reload ns3 10.53.0.3 + +echo_i "test $n: allow-query-cache inherits from allow-recursion before allow-query" +ret=0 +$DIG -p ${PORT} -b 10.53.0.3 @10.53.0.3 f.normal.example a >dig.out.ns3.1.$n || ret=1 +grep 'recursion requested but not available' dig.out.ns3.1.$n >/dev/null || ret=1 +grep 'status: REFUSED' dig.out.ns3.1.$n >/dev/null || ret=1 +grep 'EDE: 18 (Prohibited)' dig.out.ns3.1.$n >/dev/null || ret=1 +grep 'EDE: 20' dig.out.ns3.1.$n >/dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# Test 66 - allow-recursion inheritance from allow-query +n=$((n + 1)) +copy_setports ns3/named8.conf.in ns3/named.conf +rndc_reload ns3 10.53.0.3 + +echo_i "test $n: inheritance of allow-query-cache from allow-recursion" +ret=0 +# this should be prohibited (10.53.1.2 does not have recursion allowed) +$DIG -p ${PORT} -b 10.53.1.2 @10.53.1.2 f.normal.example a >dig.out.ns3.1.$n || ret=1 +grep 'recursion requested but not available' dig.out.ns3.1.$n >/dev/null || ret=1 +grep 'status: REFUSED' dig.out.ns3.1.$n >/dev/null || ret=1 +grep 'EDE: 18 (Prohibited)' dig.out.ns3.1.$n >/dev/null || ret=1 +grep 'EDE: 20' dig.out.ns3.1.$n >/dev/null || ret=1 +# this should be allowed +$DIG -p ${PORT} -b 10.53.0.4 @10.53.0.4 f.normal.example a >dig.out.ns3.2.$n || ret=1 +grep 'ANSWER: 1' dig.out.ns3.2.$n >/dev/null || ret=1 +# this should be allowed +$DIG -p ${PORT} -b 10.53.0.4 @10.53.0.4 e.normal.example a >dig.out.ns3.3.$n || ret=1 +grep 'ANSWER: 1' dig.out.ns3.3.$n >/dev/null || ret=1 +status=$((status + ret)) +if [ $ret != 0 ]; then echo_i "failed"; fi echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/lib/isccfg/include/isccfg/cfg.h b/lib/isccfg/include/isccfg/cfg.h index ec3076f3ce..882ac2cca1 100644 --- a/lib/isccfg/include/isccfg/cfg.h +++ b/lib/isccfg/include/isccfg/cfg.h @@ -578,6 +578,9 @@ const cfg_clausedef_t * cfg_map_nextclause(const cfg_type_t *map, const void **clauses, unsigned int *idx); +const cfg_clausedef_t * +cfg_map_findclause(const cfg_type_t *map, const char *name); + typedef isc_result_t(pluginlist_cb_t)( const cfg_obj_t *config, const cfg_obj_t *obj, cfg_aclconfctx_t *aclctx, const char *plugin_path, const char *parameters, void *callback_data); diff --git a/lib/isccfg/include/isccfg/grammar.h b/lib/isccfg/include/isccfg/grammar.h index e0839e9e64..460afb1087 100644 --- a/lib/isccfg/include/isccfg/grammar.h +++ b/lib/isccfg/include/isccfg/grammar.h @@ -133,7 +133,8 @@ struct cfg_printer { * cfg_rep_map, because the allow-query and allow-recursion ACLs have * complex semantics that need to be preserved. */ -typedef void (*cfg_mergefunc_t)(cfg_obj_t *effectiveobj, +typedef void (*cfg_mergefunc_t)(const cfg_obj_t *config, + cfg_obj_t *effectiveobj, const cfg_obj_t *defaultobj); struct cfg_clausedef { @@ -201,6 +202,13 @@ struct cfg_obj { isc_mem_t *mctx; isc_refcount_t references; + /*% + * Indicates that an object was cloned from the defaults + * or otherwise generated during the configuration merge + * process: + */ + bool cloned; + const cfg_type_t *type; union { uint32_t uint32; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index e2d5796ed9..e4421c50c8 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1148,7 +1148,8 @@ static cfg_type_t cfg_type_fetchesper = { "fetchesper", cfg_parse_tuple, &cfg_rep_tuple, fetchesper_fields }; static void -map_merge(cfg_obj_t *effectivemap, const cfg_obj_t *defaultmap) { +map_merge(const cfg_obj_t *config ISC_ATTR_UNUSED, cfg_obj_t *effectivemap, + const cfg_obj_t *defaultmap) { const void *clauses = NULL; const cfg_clausedef_t *clause = NULL; unsigned int i = 0; @@ -1178,7 +1179,7 @@ map_merge(cfg_obj_t *effectivemap, const cfg_obj_t *defaultmap) { if (effectiveobj != NULL && defaultobj != NULL && clause->merge != NULL) { - clause->merge(effectiveobj, defaultobj); + clause->merge(effectivemap, effectiveobj, defaultobj); continue; } @@ -1202,153 +1203,180 @@ map_merge(cfg_obj_t *effectivemap, const cfg_obj_t *defaultmap) { } /* - * These are used when merging clauses with CFG_CLAUSEFLAG_MULTI, where - * the entries from the user configuration and the default configuration - * are added together, rather than the user configuration overriding the - * default. merge_prepend() puts the default configuration at the - * beginning of the cloned object (for example, for dnssec-policy), and - * merge_append() puts it at the end (for example, for views). + * "dnssec-policy" has CFG_CLAUSEFLAG_MULTI, but unlike most such + * clauses, the entries in the user configuration are appended to the + * default configuration instead of overriding the list. */ static void -merge_prepend(cfg_obj_t *effectiveobj, const cfg_obj_t *defaultobj) { - cfg_list_addclone(effectiveobj, defaultobj, true); +policy_merge(const cfg_obj_t *config ISC_ATTR_UNUSED, cfg_obj_t *eff, + const cfg_obj_t *def) { + cfg_list_addclone(eff, def, true); } static void -merge_append(cfg_obj_t *effectiveobj, const cfg_obj_t *defaultobj) { - cfg_list_addclone(effectiveobj, defaultobj, false); +cloneto(cfg_obj_t *options, const cfg_obj_t *obj, const char *clausename) { + isc_result_t result; + const cfg_clausedef_t *clause = cfg_map_findclause(options->type, + clausename); + + result = cfg_map_addclone(options, obj, clause); + INSIST(result == ISC_R_SUCCESS); } static void -options_merge_defaultacl(cfg_obj_t *effectiveoptions, - const cfg_obj_t *defaultoptions, const char *aclname, - bool needsdefault) { +setdefaultacl(cfg_obj_t *options, const cfg_obj_t *defaultoptions, + const char *aclname) { const cfg_obj_t *obj = NULL; isc_result_t result; - if (needsdefault == false) { + result = cfg_map_get(options, aclname, &obj); + if (result == ISC_R_SUCCESS) { return; } + obj = NULL; result = cfg_map_get(defaultoptions, aclname, &obj); INSIST(result == ISC_R_SUCCESS); - cfg_obj_ref(UNCONST(obj)); - result = cfg_map_add(effectiveoptions, UNCONST(obj), aclname); - INSIST(result == ISC_R_SUCCESS); + cloneto(options, obj, aclname); +} + +static const cfg_obj_t * +aclobj(const cfg_obj_t *o, const cfg_obj_t *v, const char *name) { + const cfg_obj_t *obj = NULL; + + cfg_map_get(v, name, &obj); + if (obj == NULL) { + cfg_map_get(o, name, &obj); + } + return obj; } static void -options_merge(cfg_obj_t *effectiveoptions, const cfg_obj_t *defaultoptions) { - const cfg_obj_t *obj = NULL; - isc_result_t result; - bool noquerycacheacl = false; - bool norecursionacl = false; - bool noquerycacheonacl = false; - bool norecursiononacl = false; +setacls(const cfg_obj_t *config, cfg_obj_t *voptions, + const cfg_obj_t *defaultoptions) { + const cfg_obj_t *options = NULL; + const cfg_obj_t *query = NULL, *cache = NULL, *cacheon = NULL; + const cfg_obj_t *recursion = NULL, *recursionon = NULL; + + cfg_map_get(config, "options", &options); + INSIST(options != NULL); /* - * ACLs allow-query-cache, allow-recursion, allow-query-cache-on and - * allow-recursion-on need to be "merged" at once because there - * are implicit dependency rules between them. After all those - * dependency rules have been applied, the default values are used - * _only_ if they are still undefined in the user configuration. - * - * This need to be done only for the global options, because the views - * and zone ACL initialization code will look in the global options - * as fallback, and they'll be defined there. - * - * This is useless (and shouldn't have any effect) for views with - * recursion=false, but needed for those with recursion=true + * This can be called in two different contexts: from the top-level + * option clause, or from the user-defined views. */ - result = cfg_map_get(effectiveoptions, "allow-query-cache", &obj); - if (result != ISC_R_SUCCESS) { - result = cfg_map_get(effectiveoptions, "allow-recursion", &obj); - if (result == ISC_R_SUCCESS) { - cfg_obj_ref(UNCONST(obj)); - result = cfg_map_add(effectiveoptions, UNCONST(obj), - "allow-query-cache"); - INSIST(result == ISC_R_SUCCESS); - } else { - result = cfg_map_get(effectiveoptions, "allow-query", - &obj); - if (result == ISC_R_SUCCESS) { - cfg_obj_ref(UNCONST(obj)); - result = cfg_map_add(effectiveoptions, - UNCONST(obj), - "allow-query-cache"); - INSIST(result == ISC_R_SUCCESS); - } else { - noquerycacheacl = true; - } + INSIST((options == voptions && defaultoptions != NULL) || + (options != voptions && defaultoptions == NULL)); + + query = aclobj(options, voptions, "allow-query"); + recursion = aclobj(options, voptions, "allow-recursion"); + cache = aclobj(options, voptions, "allow-query-cache"); + + cacheon = aclobj(options, voptions, "allow-query-cache-on"); + recursionon = aclobj(options, voptions, "allow-recursion-on"); + + bool aq = query != NULL && !query->cloned; + bool aqc = cache != NULL && !cache->cloned; + bool ar = recursion != NULL && !recursion->cloned; + + bool aqco = cacheon != NULL && !cacheon->cloned; + bool aro = recursionon != NULL && !recursionon->cloned; + + /* + * "allow-query-cache" inherits from "allow-recursion" if set, + * otherwise from "allow-query" if set. + */ + if (!aqc) { + if (ar) { + cloneto(voptions, recursion, "allow-query-cache"); + } else if (aq) { + cloneto(voptions, query, "allow-query-cache"); } } - obj = NULL; - result = cfg_map_get(effectiveoptions, "allow-recursion", &obj); - if (result != ISC_R_SUCCESS) { - result = cfg_map_get(effectiveoptions, "allow-query-cache", - &obj); - if (result == ISC_R_SUCCESS) { - cfg_obj_ref(UNCONST(obj)); - result = cfg_map_add(effectiveoptions, UNCONST(obj), - "allow-recursion"); - INSIST(result == ISC_R_SUCCESS); - } else { - result = cfg_map_get(effectiveoptions, "allow-query", - &obj); - if (result == ISC_R_SUCCESS) { - cfg_obj_ref(UNCONST(obj)); - result = cfg_map_add(effectiveoptions, - UNCONST(obj), - "allow-recursion"); - INSIST(result == ISC_R_SUCCESS); - } else { - norecursionacl = true; - } + /* + * "allow-recursion" inherits from "allow-query-cache" if set, + * otherwise from "allow-query" if set. + */ + if (!ar) { + if (aqc) { + cloneto(voptions, cache, "allow-recursion"); + } else if (aq) { + cloneto(voptions, query, "allow-recursion"); } } - obj = NULL; - result = cfg_map_get(effectiveoptions, "allow-query-cache-on", &obj); - if (result != ISC_R_SUCCESS) { - result = cfg_map_get(effectiveoptions, "allow-recursion-on", - &obj); - if (result == ISC_R_SUCCESS) { - cfg_obj_ref(UNCONST(obj)); - result = cfg_map_add(effectiveoptions, UNCONST(obj), - "allow-query-cache-on"); - INSIST(result == ISC_R_SUCCESS); - } else { - noquerycacheonacl = true; - } + /* + * "allow-query-cache-on" inherits from "allow-recursion-on" + * if set, and vice versa. + */ + if (!aqco && aro) { + cloneto(voptions, recursionon, "allow-query-cache-on"); + } else if (!aro && aqco) { + cloneto(voptions, cacheon, "allow-recursion-on"); } - obj = NULL; - result = cfg_map_get(effectiveoptions, "allow-recursion-on", &obj); - if (result != ISC_R_SUCCESS) { - result = cfg_map_get(effectiveoptions, "allow-query-cache-on", - &obj); - if (result == ISC_R_SUCCESS) { - cfg_obj_ref(UNCONST(obj)); - result = cfg_map_add(effectiveoptions, UNCONST(obj), - "allow-recursion-on"); - INSIST(result == ISC_R_SUCCESS); - } else { - norecursiononacl = true; - } + if (options == voptions) { + /* + * This is the top-level options clause. This clause gets copies + * of the default ACL if they are not defined. Those will be + * used for user views ACLs too. + */ + setdefaultacl(voptions, defaultoptions, "allow-query-cache"); + setdefaultacl(voptions, defaultoptions, "allow-recursion"); + setdefaultacl(voptions, defaultoptions, "allow-query-cache-on"); + setdefaultacl(voptions, defaultoptions, "allow-recursion-on"); } +} - options_merge_defaultacl(effectiveoptions, defaultoptions, - "allow-query-cache", noquerycacheacl); - options_merge_defaultacl(effectiveoptions, defaultoptions, - "allow-recursion", norecursionacl); - options_merge_defaultacl(effectiveoptions, defaultoptions, - "allow-query-cache-on", noquerycacheonacl); - options_merge_defaultacl(effectiveoptions, defaultoptions, - "allow-recursion-on", norecursiononacl); +static void +options_merge(const cfg_obj_t *config, cfg_obj_t *effectiveoptions, + const cfg_obj_t *defaultoptions) { + /* + * ACLs allow-query-cache, allow-recursion, allow-query-cache-on + * and allow-recursion-on need to be merged with the defaults + * carefully, because there are implicit dependency rules + * between them. + * + * Note: this is similar to the code in view_merge() + * below, but that's only called when views are explicitly + * configured in named.conf, so we need to do this at the + * options level too. + */ + setacls(config, effectiveoptions, defaultoptions); - map_merge(effectiveoptions, defaultoptions); + map_merge(config, effectiveoptions, defaultoptions); +} + +/* + * "view" has CFG_CLAUSEFLAG_MULTI, but unlike most such clauses, the + * entries in the user configuration are *prepended* to the default + * configuration instead of overriding the list. + * + * After all views have been cloned into the effective configuration, + * we correct their ACL settings to take into account the mutual iheritance + * of allow-recursion, allow-query-cache, and allow-query. + */ +static void +view_merge(const cfg_obj_t *config, cfg_obj_t *eff, const cfg_obj_t *def) { + REQUIRE(cfg_obj_islist(eff)); + REQUIRE(cfg_obj_islist(def)); + + cfg_list_addclone(eff, def, false); + CFG_LIST_FOREACH(eff, elt) { + const cfg_obj_t *name = cfg_tuple_get(elt->obj, "name"); + cfg_obj_t *voptions = NULL; + + if (name != NULL && + strcmp(cfg_obj_asstring(name), "_bind") == 0) + { + continue; + } + + voptions = UNCONST(cfg_tuple_get(elt->obj, "options")); + setacls(config, voptions, NULL); + } } /*% @@ -1359,7 +1387,7 @@ static cfg_clausedef_t namedconf_clauses[] = { { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI }, { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI }, { "dnssec-policy", &cfg_type_dnssecpolicy, CFG_CLAUSEFLAG_MULTI, - merge_prepend }, + policy_merge }, #if HAVE_LIBNGHTTP2 { "http", &cfg_type_http_description, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OPTIONAL }, @@ -1390,7 +1418,7 @@ static cfg_clausedef_t namedconf_clauses[] = { CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_BUILTINONLY | CFG_CLAUSEFLAG_NODOC }, { "tls", &cfg_type_tlsconf, CFG_CLAUSEFLAG_MULTI }, - { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI, merge_append }, + { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI, view_merge }, { NULL, NULL, 0 } }; @@ -2231,7 +2259,8 @@ static cfg_type_t cfg_type_staleanswerclienttimeout = { }; static void -prefetch_merge(cfg_obj_t *effectiveobj, const cfg_obj_t *defaultobj) { +prefetch_merge(const cfg_obj_t *config ISC_ATTR_UNUSED, cfg_obj_t *effectiveobj, + const cfg_obj_t *defaultobj) { cfg_obj_t *trigger = NULL; cfg_obj_t *eligible = NULL; @@ -2253,7 +2282,8 @@ prefetch_merge(cfg_obj_t *effectiveobj, const cfg_obj_t *defaultobj) { } static void -checknames_merge(cfg_obj_t *effectiveobj, const cfg_obj_t *defaultobj) { +checknames_merge(const cfg_obj_t *config ISC_ATTR_UNUSED, + cfg_obj_t *effectiveobj, const cfg_obj_t *defaultobj) { /* * Applies only to the top-level option `check-names` statement. * The view and zone-level versions aren't merged into the defaults @@ -4356,7 +4386,7 @@ cfg_effective_config(const cfg_obj_t *userconfig, REQUIRE(userconfig != NULL && userconfig->type == &cfg_type_namedconf); cfg_obj_clone(userconfig, &effective); - map_merge(effective, defaultconfig); + map_merge(effective, effective, defaultconfig); return effective; } diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c index 15c2c53e46..199e9aa9fb 100644 --- a/lib/isccfg/parser.c +++ b/lib/isccfg/parser.c @@ -175,6 +175,7 @@ cfg_obj_clone(const cfg_obj_t *source, cfg_obj_t **target) { cfg_obj_create(source->mctx, source->file, source->line, source->type, target); + (*target)->cloned = source->cloned; source->type->rep->copy(*target, source); } @@ -2989,6 +2990,23 @@ cfg_map_nextclause(const cfg_type_t *map, const void **clauses, return &(*clauseset)[*idx]; } +const cfg_clausedef_t * +cfg_map_findclause(const cfg_type_t *map, const char *name) { + const cfg_clausedef_t *found = NULL; + const void *clauses = NULL; + unsigned int idx; + + REQUIRE(map != NULL && map->rep == &cfg_rep_map); + REQUIRE(name != NULL); + + found = cfg_map_firstclause(map, &clauses, &idx); + while (name != NULL && strcasecmp(name, found->name)) { + found = cfg_map_nextclause(map, &clauses, &idx); + } + + return ((cfg_clausedef_t *)clauses) + idx; +} + /* Parse an arbitrary token, storing its raw text representation. */ static isc_result_t parse_token(cfg_parser_t *pctx, const cfg_type_t *type ISC_ATTR_UNUSED, @@ -4037,24 +4055,6 @@ cfg_print_grammar(const cfg_type_t *type, unsigned int flags, cfg_doc_obj(&pctx, type); } -static const cfg_clausedef_t * -map_lookup_clause(const cfg_obj_t *mapobj, const char *clausename) { - const cfg_map_t *map; - const cfg_clausedef_t *const *clauseset = NULL; - const cfg_clausedef_t *clause = NULL; - - map = &mapobj->value.map; - for (clauseset = map->clausesets; *clauseset != NULL; clauseset++) { - for (clause = *clauseset; clause->name != NULL; clause++) { - if (strcasecmp(clause->name, clausename) == 0) { - return clause; - } - } - } - - return NULL; -} - static isc_result_t map_define(cfg_obj_t *mapobj, cfg_obj_t *obj, const cfg_clausedef_t *clause) { isc_result_t result; @@ -4110,7 +4110,7 @@ cfg_map_add(cfg_obj_t *mapobj, cfg_obj_t *obj, const char *clausename) { REQUIRE(mapobj->type->rep == &cfg_rep_map); REQUIRE(clausename != NULL); - clause = map_lookup_clause(mapobj, clausename); + clause = cfg_map_findclause(mapobj->type, clausename); if (clause == NULL || clause->name == NULL) { return ISC_R_FAILURE; } @@ -4142,6 +4142,8 @@ cfg_map_addclone(cfg_obj_t *map, const cfg_obj_t *obj, elt = cfg_list_first(obj); while (elt != NULL && result == ISC_R_SUCCESS) { cfg_obj_clone(elt->obj, &clone); + clone->cloned = true; + result = map_define(map, clone, clause); elt = cfg_list_next(elt); @@ -4153,6 +4155,7 @@ cfg_map_addclone(cfg_obj_t *map, const cfg_obj_t *obj, } } else { cfg_obj_clone(obj, &clone); + clone->cloned = true; result = map_define(map, clone, clause); } diff --git a/tests/isccfg/grammar_test.c b/tests/isccfg/grammar_test.c index 15610a6d11..59ea824112 100644 --- a/tests/isccfg/grammar_test.c +++ b/tests/isccfg/grammar_test.c @@ -72,29 +72,15 @@ assert_text(const char *text) { memset(gtext, 0, sizeof(gtext)); } -static const cfg_clausedef_t * -find_clause(const cfg_type_t *map, const char *name) { - const cfg_clausedef_t *found = NULL; - const void *clauses = NULL; - unsigned int idx; - - found = cfg_map_firstclause(map, &clauses, &idx); - while (name != NULL && strcasecmp(name, found->name)) { - found = cfg_map_nextclause(map, &clauses, &idx); - } - - return ((cfg_clausedef_t *)clauses) + idx; -} - static void test__querysource(const char *clause_name, const char *name, const char *expected) { const cfg_clausedef_t *options_clause = NULL; - options_clause = find_clause(&cfg_type_namedconf, clause_name); + options_clause = cfg_map_findclause(&cfg_type_namedconf, clause_name); assert_non_null(options_clause); const cfg_clausedef_t *querysource_clause = NULL; - querysource_clause = find_clause(options_clause->type, name); + querysource_clause = cfg_map_findclause(options_clause->type, name); assert_non_null(querysource_clause); querysource_clause->type->doc(&gprinter, querysource_clause->type); assert_text(expected);