From: Julien ROPÉ Date: Fri, 23 Nov 2018 14:56:59 +0000 (+0100) Subject: Fix for https://bugzilla.samba.org/show_bug.cgi?id=9634 X-Git-Tag: tevent-0.11.0~383 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0ec865d979540a63362a2014358c8bb27efc0081;p=thirdparty%2Fsamba.git Fix for https://bugzilla.samba.org/show_bug.cgi?id=9634 Add an option to smb.conf to list authorized zone transfer clients. Implement restriction in dlz_bind9 module to allow transfers only to selected IPs. Deny zone transfer by default in dlz_bind9. Adds test for the restriction in DNZ zone transfer clients. BUG: https://bugzilla.samba.org/show_bug.cgi?id=9634 Signed-off-by: Julien ROPÉ Reviewed-by: Jeremy Allison Reviewed-by: Andrew Bartlett Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Fri Jun 11 19:28:10 UTC 2021 on sn-devel-184 --- diff --git a/WHATSNEW.txt b/WHATSNEW.txt index 1e407da422e..b28722c6f92 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -36,6 +36,15 @@ See also GPG_AA99442FB680B620_replaces_6F33915B6568B7EA.txt NEW FEATURES/CHANGES ==================== +- bind DLZ: Added the ability to set allow/deny lists for zone + transfer clients. + Up to now, any client could use a DNS zone transfer request + to the bind server, and get an answer from Samba. + Now the default behaviour will be to deny those request. + Two new options have been added to manage the list of + authorized/denied clients for zone transfer requests. + In order to be accepted, the request must be issued by a client + that is in the allow list and NOT in the deny list. diff --git a/docs-xml/smbdotconf/domain/dnszonetransferclientsallow.xml b/docs-xml/smbdotconf/domain/dnszonetransferclientsallow.xml new file mode 100644 index 00000000000..902b6082379 --- /dev/null +++ b/docs-xml/smbdotconf/domain/dnszonetransferclientsallow.xml @@ -0,0 +1,26 @@ + + + This option specifies the list IPs authorized to ask for dns zone + transfer from bind DLZ module. + + + The IP list is comma and space separated and specified in the same + syntax as used in , specifically + including IP address, IP prefixes and IP address masks. + + + As this is a DNS server option, hostnames are naturally not permitted. + + + The default behaviour is to deny any request. + A request will be authorized only if the emitting client is identified + in this list, and not in + + + + +192.168.0.1 + diff --git a/docs-xml/smbdotconf/domain/dnszonetransferclientsdeny.xml b/docs-xml/smbdotconf/domain/dnszonetransferclientsdeny.xml new file mode 100644 index 00000000000..f88b15bf1ca --- /dev/null +++ b/docs-xml/smbdotconf/domain/dnszonetransferclientsdeny.xml @@ -0,0 +1,26 @@ + + + This option specifies the list IPs denied to ask for dns zone + transfer from bind DLZ module. + + + The IP list is comma and space separated and specified in the same + syntax as used in , specifically + including IP address, IP prefixes and IP address masks. + + + As this is a DNS server option, hostnames are naturally not permitted. + + + If a client identified in this list sends a zone transfer request, it will always + be denied, even if they are in . + This allows the definition of sepcific denied clients within an authorized subnet. + + + + +192.168.0.1 + diff --git a/source4/dns_server/dlz_bind9.c b/source4/dns_server/dlz_bind9.c index 78f69a4d635..a9946041206 100644 --- a/source4/dns_server/dlz_bind9.c +++ b/source4/dns_server/dlz_bind9.c @@ -40,6 +40,7 @@ #include "dlz_minimal.h" #include "dnsserver_common.h" #include "lib/util/smb_strtox.h" +#include "lib/util/access.h" #undef strcasecmp @@ -1085,10 +1086,60 @@ _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name, */ _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client) { - /* just say yes for all our zones for now */ struct dlz_bind9_data *state = talloc_get_type( dbdata, struct dlz_bind9_data); - return b9_find_zone_dn(state, name, NULL, NULL); + isc_result_t ret; + const char **authorized_clients, **denied_clients; + const char *cname=""; + + /* check that the zone is known */ + ret = b9_find_zone_dn(state, name, NULL, NULL); + if (ret != ISC_R_SUCCESS) { + return ret; + } + + /* default is to deny all transfers */ + + authorized_clients = lpcfg_dns_zone_transfer_clients_allow(state->lp); + denied_clients = lpcfg_dns_zone_transfer_clients_deny(state->lp); + + /* The logic of allow_access() when both allow and deny lists are given + * does not match our expectation here: it would allow clients thar are + * neither allowed nor denied. + * Here, we want to deny clients by default. + * Using the allow_access() function is still useful as it takes care of + * parsing IP adresses and subnets in a consistent way with other options + * from smb.conf. + * + * We will then check the deny list first, then the allow list, so that + * we accept only clients that are explicitely allowed AND not explicitely + * denied. + */ + if ((authorized_clients == NULL) && (denied_clients == NULL)) { + /* No "allow" or "deny" lists given. Deny by default. */ + return ISC_R_NOPERM; + } + + if (denied_clients != NULL) { + bool ok = allow_access(denied_clients, NULL, cname, client); + if (!ok) { + /* client on deny list. Deny. */ + return ISC_R_NOPERM; + } + } + + if (authorized_clients != NULL) { + bool ok = allow_access(NULL, authorized_clients, cname, client); + if (ok) { + /* + * client is not on deny list and is on allow list. + * This is the only place we should return "allow". + */ + return ISC_R_SUCCESS; + } + } + /* We shouldn't get here, but deny by default. */ + return ISC_R_NOPERM; } /* diff --git a/source4/torture/dns/dlz_bind9.c b/source4/torture/dns/dlz_bind9.c index 1e1f1e8bef9..9ec2702dc1d 100644 --- a/source4/torture/dns/dlz_bind9.c +++ b/source4/torture/dns/dlz_bind9.c @@ -1200,6 +1200,76 @@ cancel_version: return ret; } +/* + * Test zone transfer requests restrictions + * + * 1: test that zone transfer is denied by default + * 2: with an authorized list of IPs set in smb.conf, test that zone transfer + * is accepted only for selected IPs. + */ +static bool test_dlz_bind9_allowzonexfr(struct torture_context *tctx) +{ + void *dbdata; + const char *argv[] = { + "samba_dlz", + "-H", + test_dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"), + NULL + }; + isc_result_t ret; + dns_dlzdb_t *dlzdb = NULL; + bool ok; + + tctx_static = tctx; + torture_assert_int_equal(tctx, dlz_create("samba_dlz", 3, argv, &dbdata, + "log", dlz_bind9_log_wrapper, + "writeable_zone", dlz_bind9_writeable_zone_hook, + "putrr", dlz_bind9_putrr_hook, + "putnamedrr", dlz_bind9_putnamedrr_hook, + NULL), + ISC_R_SUCCESS, + "Failed to create samba_dlz"); + + torture_assert_int_equal(tctx, dlz_configure((void*)tctx, dlzdb, dbdata), + ISC_R_SUCCESS, + "Failed to configure samba_dlz"); + + /* Ask for zone transfer with no specific config => expect denied */ + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "127.0.0.1"); + torture_assert_int_equal(tctx, ret, ISC_R_NOPERM, + "Zone transfer accepted with default settings"); + + /* Ask for zone transfer with authorizations set */ + ok = lpcfg_set_option(tctx->lp_ctx, "dns zone transfer clients allow=127.0.0.1,1234:5678::1,192.168.0."); + torture_assert(tctx, ok, "Failed to set dns zone transfer clients allow option."); + + ok = lpcfg_set_option(tctx->lp_ctx, "dns zone transfer clients deny=192.168.0.2"); + torture_assert(tctx, ok, "Failed to set dns zone transfer clients deny option."); + + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "127.0.0.1"); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Zone transfer refused for authorized IPv4 address"); + + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "1234:5678::1"); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Zone transfer refused for authorized IPv6 address."); + + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "10.0.0.1"); + torture_assert_int_equal(tctx, ret, ISC_R_NOPERM, + "Zone transfer accepted for unauthorized IP"); + + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "192.168.0.1"); + torture_assert_int_equal(tctx, ret, ISC_R_SUCCESS, + "Zone transfer refused for address in authorized IPv4 subnet."); + + ret = dlz_allowzonexfr(dbdata, lpcfg_dnsdomain(tctx->lp_ctx), "192.168.0.2"); + torture_assert_int_equal(tctx, ret, ISC_R_NOPERM, + "Zone transfer allowed for denied client."); + + dlz_destroy(dbdata); + return true; +} + static struct torture_suite *dlz_bind9_suite(TALLOC_CTX *ctx) { struct torture_suite *suite = torture_suite_create(ctx, "dlz_bind9"); @@ -1221,6 +1291,7 @@ static struct torture_suite *dlz_bind9_suite(TALLOC_CTX *ctx) torture_suite_add_simple_test(suite, "lookup", test_dlz_bind9_lookup); torture_suite_add_simple_test(suite, "zonedump", test_dlz_bind9_zonedump); torture_suite_add_simple_test(suite, "update01", test_dlz_bind9_update01); + torture_suite_add_simple_test(suite, "allowzonexfr", test_dlz_bind9_allowzonexfr); return suite; }