From e7abed8389e4bfaaf1a1c861db9f6a22582568a6 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 19 Dec 2014 14:58:01 +1300 Subject: [PATCH 1/7] selftest: Run samba.tests.dns in :local environment so it can access credentials This allows it to access the machine account, and use that to modify the DNS zones Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit ee4324ddc24dc6d92b2fdcf87e8dc63c631e2444) BUG: https://bugzilla.samba.org/show_bug.cgi?id=9404 --- source4/selftest/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 8d03b40..4826191 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -278,7 +278,7 @@ for f in sorted(os.listdir(os.path.join(samba4srcdir, "../pidl/tests"))): planperltestsuite("pidl.%s" % f[:-3], os.path.normpath(os.path.join(samba4srcdir, "../pidl/tests", f))) # DNS tests -planpythontestsuite("fl2003dc", "samba.tests.dns") +planpythontestsuite("fl2003dc:local", "samba.tests.dns") for t in smbtorture4_testsuites("dns_internal."): plansmbtorture4testsuite(t, "dc:local", '//$SERVER/whavever') -- 2.1.4 From 4b7af45e4681f8d236f0a8abf55b7dffde215010 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Tue, 16 Dec 2014 10:58:50 +0100 Subject: [PATCH 2/7] s4-dns: Reload DNS zones from dsdb when zones are modified through RPC or DRS Setup a RPC management call on the internal DNS server triggered a new LDB module which sniffs dnsZone object add, delete and modify operations. This way the notification is triggered when zones are modified either from RPC or replicated by inbound DRS. Signed-off-by: Samuel Cabrero (shadowed variable error corrected by abartlet) Signed-off-by: Andrew Bartlett Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 4fb29e9347271acd66833d471a84e39a525f4f18) BUG: https://bugzilla.samba.org/show_bug.cgi?id=9404 --- source4/dns_server/dns_server.c | 133 ++++-- source4/dsdb/samdb/ldb_modules/dns_notify.c | 448 +++++++++++++++++++++ source4/dsdb/samdb/ldb_modules/samba_dsdb.c | 3 +- .../dsdb/samdb/ldb_modules/wscript_build_server | 9 + source4/librpc/idl/irpc.idl | 11 + 5 files changed, 565 insertions(+), 39 deletions(-) create mode 100644 source4/dsdb/samdb/ldb_modules/dns_notify.c diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c index 7ea70db..c432236 100644 --- a/source4/dns_server/dns_server.c +++ b/source4/dns_server/dns_server.c @@ -45,6 +45,8 @@ #include "lib/util/tevent_werror.h" #include "auth/auth.h" #include "auth/credentials/credentials.h" +#include "librpc/gen_ndr/ndr_irpc.h" +#include "lib/messaging/irpc.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_DNS @@ -760,16 +762,90 @@ static struct dns_server_tkey_store *tkey_store_init(TALLOC_CTX *mem_ctx, return buffer; } +static NTSTATUS dns_server_reload_zones(struct dns_server *dns) +{ + int ret; + static const char * const attrs[] = { "name", NULL}; + struct ldb_result *res; + int i; + struct dns_server_zone *new_list = NULL; + struct dns_server_zone *old_list = NULL; + struct dns_server_zone *old_zone; + + // TODO: this search does not work against windows + ret = dsdb_search(dns->samdb, dns, &res, NULL, LDB_SCOPE_SUBTREE, + attrs, DSDB_SEARCH_SEARCH_ALL_PARTITIONS, "(objectClass=dnsZone)"); + if (ret != LDB_SUCCESS) { + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + TYPESAFE_QSORT(res->msgs, res->count, dns_server_sort_zones); + + for (i=0; i < res->count; i++) { + struct dns_server_zone *z; + + z = talloc_zero(dns, struct dns_server_zone); + if (z == NULL) { + return NT_STATUS_NO_MEMORY; + } + + z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL); + z->dn = talloc_move(z, &res->msgs[i]->dn); + /* + * Ignore the RootDNSServers zone and zones that we don't support yet + * RootDNSServers should never be returned (Windows DNS server don't) + * ..TrustAnchors should never be returned as is, (Windows returns + * TrustAnchors) and for the moment we don't support DNSSEC so we'd better + * not return this zone. + */ + if ((strcmp(z->name, "RootDNSServers") == 0) || + (strcmp(z->name, "..TrustAnchors") == 0)) + { + DEBUG(10, ("Ignoring zone %s\n", z->name)); + talloc_free(z); + continue; + } + DLIST_ADD_END(new_list, z, NULL); + } + + old_list = dns->zones; + dns->zones = new_list; + while ((old_zone = DLIST_TAIL(old_list)) != NULL) { + DLIST_REMOVE(old_list, old_zone); + talloc_free(old_zone); + } + + return NT_STATUS_OK; +} + +/** + * Called when the internal DNS server should reload the zones from DB, for + * example, when zones are added or deleted through RPC or replicated by + * inbound DRS. + */ +static NTSTATUS dns_reload_zones(struct irpc_message *msg, + struct dnssrv_reload_dns_zones *r) +{ + struct dns_server *dns; + + dns = talloc_get_type(msg->private_data, struct dns_server); + if (dns == NULL) { + r->out.result = NT_STATUS_INTERNAL_ERROR; + return NT_STATUS_INTERNAL_ERROR; + } + + r->out.result = dns_server_reload_zones(dns); + + return NT_STATUS_OK; +} + static void dns_task_init(struct task_server *task) { struct dns_server *dns; NTSTATUS status; struct interface *ifaces = NULL; int ret; - struct ldb_result *res; - static const char * const attrs[] = { "name", NULL}; static const char * const attrs_none[] = { NULL}; - unsigned int i; struct ldb_message *dns_acc; char *hostname_lower; char *dns_spn; @@ -865,48 +941,29 @@ static void dns_task_init(struct task_server *task) return; } - // TODO: this search does not work against windows - ret = dsdb_search(dns->samdb, dns, &res, NULL, LDB_SCOPE_SUBTREE, - attrs, DSDB_SEARCH_SEARCH_ALL_PARTITIONS, "(objectClass=dnsZone)"); - if (ret != LDB_SUCCESS) { - task_server_terminate(task, - "dns: failed to look up root DNS zones", - true); + status = dns_server_reload_zones(dns); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, "dns: failed to load DNS zones", true); return; } - TYPESAFE_QSORT(res->msgs, res->count, dns_server_sort_zones); - - for (i=0; i < res->count; i++) { - struct dns_server_zone *z; - - z = talloc_zero(dns, struct dns_server_zone); - if (z == NULL) { - task_server_terminate(task, "dns failed to allocate memory", true); - } + status = dns_startup_interfaces(dns, ifaces); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, "dns failed to setup interfaces", true); + return; + } - z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL); - z->dn = talloc_move(z, &res->msgs[i]->dn); - /* - * Ignore the RootDNSServers zone and zones that we don't support yet - * RootDNSServers should never be returned (Windows DNS server don't) - * ..TrustAnchors should never be returned as is, (Windows returns - * TrustAnchors) and for the moment we don't support DNSSEC so we'd better - * not return this zone. - */ - if ((strcmp(z->name, "RootDNSServers") == 0) || - (strcmp(z->name, "..TrustAnchors") == 0)) - { - DEBUG(10, ("Ignoring zone %s\n", z->name)); - talloc_free(z); - continue; - } - DLIST_ADD_END(dns->zones, z, NULL); + /* Setup the IRPC interface and register handlers */ + status = irpc_add_name(task->msg_ctx, "dnssrv"); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, "dns: failed to register IRPC name", true); + return; } - status = dns_startup_interfaces(dns, ifaces); + status = IRPC_REGISTER(task->msg_ctx, irpc, DNSSRV_RELOAD_DNS_ZONES, + dns_reload_zones, dns); if (!NT_STATUS_IS_OK(status)) { - task_server_terminate(task, "dns failed to setup interfaces", true); + task_server_terminate(task, "dns: failed to setup reload handler", true); return; } } diff --git a/source4/dsdb/samdb/ldb_modules/dns_notify.c b/source4/dsdb/samdb/ldb_modules/dns_notify.c new file mode 100644 index 0000000..28a1be9 --- /dev/null +++ b/source4/dsdb/samdb/ldb_modules/dns_notify.c @@ -0,0 +1,448 @@ +/* + ldb database library + + Copyright (C) Samuel Cabrero 2014 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + * Name: ldb + * + * Component: ldb dns_notify module + * + * Description: Notify the DNS server when zones are changed, either by direct + * RPC management calls or DRS inbound replication. + * + * Author: Samuel Cabrero + */ + +#include "includes.h" +#include "ldb_module.h" +#include "dsdb/samdb/ldb_modules/util.h" +#include "dsdb/samdb/samdb.h" +#include "dsdb/common/proto.h" +#include "librpc/gen_ndr/ndr_irpc.h" +#include "lib/messaging/irpc.h" +#include "librpc/gen_ndr/ndr_irpc_c.h" +#include "param/param.h" +#include "dlinklist.h" + +struct dns_notify_watched_dn { + struct dns_notify_watched_dn *next, *prev; + struct ldb_dn *dn; +}; + +struct dns_notify_private { + struct dns_notify_watched_dn *watched; + bool reload_zones; +}; + +struct dns_notify_dnssrv_state { + struct imessaging_context *msg_ctx; + struct dnssrv_reload_dns_zones r; +}; + +static void dns_notify_dnssrv_done(struct tevent_req *req) +{ + NTSTATUS status; + struct dns_notify_dnssrv_state *state; + + state = tevent_req_callback_data(req, struct dns_notify_dnssrv_state); + + status = dcerpc_dnssrv_reload_dns_zones_r_recv(req, state); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("%s: Error notifiying dns server: %s\n", + __func__, nt_errstr(status))); + } + imessaging_cleanup(state->msg_ctx); + + talloc_free(req); + talloc_free(state); +} + +static void dns_notify_dnssrv_send(struct ldb_module *module) +{ + struct ldb_context *ldb; + struct loadparm_context *lp_ctx; + struct dns_notify_dnssrv_state *state; + struct dcerpc_binding_handle *handle; + struct tevent_req *req; + + ldb = ldb_module_get_ctx(module); + + lp_ctx = ldb_get_opaque(ldb, "loadparm"); + if (lp_ctx == NULL) { + return; + } + + state = talloc_zero(module, struct dns_notify_dnssrv_state); + if (state == NULL) { + return; + } + + /* Initialize messaging client */ + state->msg_ctx = imessaging_client_init(state, lp_ctx, + ldb_get_event_context(ldb)); + if (state->msg_ctx == NULL) { + ldb_asprintf_errstring(ldb, "Failed to generate client messaging context in %s", + lpcfg_imessaging_path(state, lp_ctx)); + talloc_free(state); + return; + } + + /* Get a handle to notify the DNS server */ + handle = irpc_binding_handle_by_name(state, state->msg_ctx, + "dnssrv", + &ndr_table_irpc); + if (handle == NULL) { + imessaging_cleanup(state->msg_ctx); + talloc_free(state); + return; + } + + /* Send the notifications */ + req = dcerpc_dnssrv_reload_dns_zones_r_send(state, + ldb_get_event_context(ldb), + handle, + &state->r); + if (req == NULL) { + imessaging_cleanup(state->msg_ctx); + talloc_free(state); + return; + } + tevent_req_set_callback(req, dns_notify_dnssrv_done, state); +} + +static int dns_notify_add(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_context *ldb; + struct dns_notify_private *data; + struct dns_notify_watched_dn *w; + struct dsdb_schema *schema; + const struct dsdb_class *objectclass; + + if (ldb_dn_is_special(req->op.add.message->dn)) { + return ldb_next_request(module, req); + } + + if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) { + return ldb_next_request(module, req); + } + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), + struct dns_notify_private); + if (data == NULL) { + return ldb_operr(ldb); + } + + for (w = data->watched; w; w = w->next) { + if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) { + schema = dsdb_get_schema(ldb, req); + if (schema == NULL) { + return ldb_operr(ldb); + } + + objectclass = dsdb_get_structural_oc_from_msg(schema, req->op.add.message); + if (objectclass == NULL) { + return ldb_operr(ldb); + } + + if (strcasecmp(objectclass->lDAPDisplayName, "dnsZone") == 0) { + data->reload_zones = true; + break; + } + } + } + + return ldb_next_request(module, req); +} + +static int dns_notify_modify(struct ldb_module *module, struct ldb_request *req) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_context *ldb; + struct dns_notify_private *data; + struct dns_notify_watched_dn *w; + struct ldb_dn *dn; + struct ldb_result *res; + struct dsdb_schema *schema; + const struct dsdb_class *objectclass; + int ret; + + if (ldb_dn_is_special(req->op.mod.message->dn)) { + return ldb_next_request(module, req); + } + + if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) { + return ldb_next_request(module, req); + } + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), + struct dns_notify_private); + if (data == NULL) { + return ldb_operr(ldb); + } + + tmp_ctx = talloc_new(module); + if (tmp_ctx == NULL) { + return ldb_oom(ldb); + } + + for (w = data->watched; w; w = w->next) { + if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) { + dn = ldb_dn_copy(tmp_ctx, req->op.mod.message->dn); + + ret = dsdb_module_search_dn(module, tmp_ctx, &res, dn, NULL, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_RECYCLED | + DSDB_SEARCH_REVEAL_INTERNALS | + DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + "%s: Failed to modify %s, because we failed to find it: %s\n", + __func__, + ldb_dn_get_linearized(dn), + ldb_errstring(ldb_module_get_ctx(module))); + talloc_free(tmp_ctx); + return ret; + } + + schema = dsdb_get_schema(ldb, req); + if (schema == NULL) { + talloc_free(tmp_ctx); + return ldb_operr(ldb); + } + + objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]); + if (objectclass == NULL) { + talloc_free(tmp_ctx); + return ldb_operr(ldb); + } + + if (strcasecmp(objectclass->lDAPDisplayName, "dnsZone") == 0) { + data->reload_zones = true; + break; + } + } + } + + talloc_free(tmp_ctx); + return ldb_next_request(module, req); +} + +static int dns_notify_delete(struct ldb_module *module, struct ldb_request *req) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_context *ldb; + struct dns_notify_private *data; + struct dns_notify_watched_dn *w; + struct ldb_dn *old_dn; + struct ldb_result *res; + struct dsdb_schema *schema; + const struct dsdb_class *objectclass; + int ret; + + if (ldb_dn_is_special(req->op.del.dn)) { + return ldb_next_request(module, req); + } + + if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) { + return ldb_next_request(module, req); + } + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), + struct dns_notify_private); + if (data == NULL) { + return ldb_operr(ldb); + } + + tmp_ctx = talloc_new(module); + if (tmp_ctx == NULL) { + return ldb_oom(ldb); + } + + for (w = data->watched; w; w = w->next) { + if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) { + old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn); + ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_RECYCLED | + DSDB_SEARCH_REVEAL_INTERNALS | + DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + "%s: Failed to delete %s, because we failed to find it: %s\n", + __func__, + ldb_dn_get_linearized(old_dn), + ldb_errstring(ldb_module_get_ctx(module))); + talloc_free(tmp_ctx); + return ret; + } + + schema = dsdb_get_schema(ldb, req); + if (schema == NULL) { + talloc_free(tmp_ctx); + return ldb_operr(ldb); + } + + objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]); + if (objectclass == NULL) { + talloc_free(tmp_ctx); + return ldb_operr(ldb); + } + + if (strcasecmp(objectclass->lDAPDisplayName, "dnsZone") == 0) { + data->reload_zones = true; + break; + } + } + } + + talloc_free(tmp_ctx); + return ldb_next_request(module, req); +} + +static int dns_notify_start_trans(struct ldb_module *module) +{ + struct ldb_context *ldb; + struct dns_notify_private *data; + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), + struct dns_notify_private); + if (data == NULL) { + return ldb_operr(ldb); + } + + data->reload_zones = false; + + return ldb_next_start_trans(module); +} + +static int dns_notify_end_trans(struct ldb_module *module) +{ + struct ldb_context *ldb; + struct dns_notify_private *data; + int ret; + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), + struct dns_notify_private); + if (data == NULL) { + return ldb_operr(ldb); + } + + ret = ldb_next_end_trans(module); + if (ret == LDB_SUCCESS) { + if (data->reload_zones) { + dns_notify_dnssrv_send(module); + } + } + + return ret; +} + +static int dns_notify_del_trans(struct ldb_module *module) +{ + struct ldb_context *ldb; + struct dns_notify_private *data; + + ldb = ldb_module_get_ctx(module); + data = talloc_get_type(ldb_module_get_private(module), + struct dns_notify_private); + if (data == NULL) { + return ldb_operr(ldb); + } + + data->reload_zones = false; + + return ldb_next_del_trans(module); +} + +static int dns_notify_init(struct ldb_module *module) +{ + struct ldb_context *ldb; + struct dns_notify_private *data; + struct dns_notify_watched_dn *watched; + struct ldb_dn *domain_dn; + struct ldb_dn *forest_dn; + + ldb = ldb_module_get_ctx(module); + + data = talloc_zero(module, struct dns_notify_private); + if (data == NULL) { + return ldb_oom(ldb); + } + + domain_dn = ldb_get_default_basedn(ldb); + forest_dn = ldb_get_root_basedn(ldb); + + /* Register hook on domain partition */ + watched = talloc_zero(data, struct dns_notify_watched_dn); + if (watched == NULL) { + talloc_free(data); + return ldb_oom(ldb); + } + watched->dn = ldb_dn_new_fmt(watched, ldb, + "CN=MicrosoftDNS,CN=System,%s", + ldb_dn_get_linearized(domain_dn)); + if (watched->dn == NULL) { + talloc_free(data); + return ldb_oom(ldb); + } + DLIST_ADD(data->watched, watched); + + /* Check for DomainDnsZones partition and register hook */ + watched = talloc_zero(data, struct dns_notify_watched_dn); + if (watched == NULL) { + talloc_free(data); + return ldb_oom(ldb); + } + watched->dn = ldb_dn_new_fmt(watched, ldb, "CN=MicrosoftDNS,DC=DomainDnsZones,%s", ldb_dn_get_linearized(forest_dn)); + DLIST_ADD(data->watched, watched); + + /* Check for ForestDnsZones partition and register hook */ + watched = talloc_zero(data, struct dns_notify_watched_dn); + if (watched == NULL) { + talloc_free(data); + return ldb_oom(ldb); + } + watched->dn = ldb_dn_new_fmt(watched, ldb, "CN=MicrosoftDNS,DC=ForestDnsZones,%s", ldb_dn_get_linearized(forest_dn)); + DLIST_ADD(data->watched, watched); + + ldb_module_set_private(module, data); + + return ldb_next_init(module); +} + +static const struct ldb_module_ops ldb_dns_notify_module_ops = { + .name = "dns_notify", + .init_context = dns_notify_init, + .add = dns_notify_add, + .modify = dns_notify_modify, + .del = dns_notify_delete, + .start_transaction = dns_notify_start_trans, + .end_transaction = dns_notify_end_trans, + .del_transaction = dns_notify_del_trans, +}; + +int ldb_dns_notify_module_init(const char *version) +{ + LDB_MODULE_CHECK_VERSION(version); + return ldb_register_module(&ldb_dns_notify_module_ops); +} diff --git a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c index 086b11f..26c583e 100644 --- a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c +++ b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c @@ -302,7 +302,8 @@ static int samba_dsdb_init(struct ldb_module *module) const char *extended_dn_module_openldap = "extended_dn_out_openldap"; const char *extended_dn_in_module = "extended_dn_in"; - static const char *modules_list2[] = {"show_deleted", + static const char *modules_list2[] = {"dns_notify", + "show_deleted", "new_partition", "partition", NULL }; diff --git a/source4/dsdb/samdb/ldb_modules/wscript_build_server b/source4/dsdb/samdb/ldb_modules/wscript_build_server index 0307aea..8848fd2 100755 --- a/source4/dsdb/samdb/ldb_modules/wscript_build_server +++ b/source4/dsdb/samdb/ldb_modules/wscript_build_server @@ -364,3 +364,12 @@ bld.SAMBA_MODULE('ldb_dirsync', internal_module=False, deps='talloc samba-security samdb DSDB_MODULE_HELPERS' ) + +bld.SAMBA_MODULE('ldb_dns_notify', + source='dns_notify.c', + subsystem='ldb', + init_function='ldb_dns_notify_module_init', + module_init_name='ldb_init_module', + internal_module=False, + deps='talloc samdb DSDB_MODULE_HELPERS MESSAGING RPC_NDR_IRPC' + ) \ No newline at end of file diff --git a/source4/librpc/idl/irpc.idl b/source4/librpc/idl/irpc.idl index 6a55eef..65ae4b6 100644 --- a/source4/librpc/idl/irpc.idl +++ b/source4/librpc/idl/irpc.idl @@ -207,4 +207,15 @@ import "misc.idl", "security.idl", "nbt.idl", "netlogon.idl", "server_id.idl"; [in] uint32 dns_ttl, [in,out,ref] NL_DNS_NAME_INFO_ARRAY *dns_names ); + + /****************************************************** + * Management calls for the dns server + ******************************************************/ + /** + * Force internal DNS server to reload the DNS zones. + * + * Called when zones are added or deleted through RPC + * or replicated by DRS. + */ + NTSTATUS dnssrv_reload_dns_zones(); } -- 2.1.4 From a1d4a7c1907bd5f08eee56bbc1b06218509369f9 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Tue, 16 Dec 2014 18:04:13 +0100 Subject: [PATCH 3/7] dns.py: Test dns server reload zones from DSDB when are created or deleted Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 336ffb29b50298a0597c15b9f60416adb745bc3d) BUG: https://bugzilla.samba.org/show_bug.cgi?id=9404 --- python/samba/tests/dns.py | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/python/samba/tests/dns.py b/python/samba/tests/dns.py index f93e13f..b8ce32a 100644 --- a/python/samba/tests/dns.py +++ b/python/samba/tests/dns.py @@ -21,7 +21,9 @@ import random import socket import samba.ndr as ndr import samba.dcerpc.dns as dns +from samba import credentials, param from samba.tests import TestCase +from samba.dcerpc import dnsp, dnsserver FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)]) @@ -862,6 +864,82 @@ class TestInvalidQueries(DNSTest): if s is not None: s.close() +class TestZones(DNSTest): + def get_loadparm(self): + lp = param.LoadParm() + lp.load(os.getenv("SMB_CONF_PATH")) + return lp + + def get_credentials(self, lp): + creds = credentials.Credentials() + creds.guess(lp) + creds.set_machine_account(lp) + creds.set_krb_forwardable(credentials.NO_KRB_FORWARDABLE) + return creds + + def setUp(self): + super(TestZones, self).setUp() + self.lp = self.get_loadparm() + self.creds = self.get_credentials(self.lp) + self.server = os.getenv("SERVER_IP") + self.zone = "test.lan" + self.rpc_conn = dnsserver.dnsserver("ncacn_ip_tcp:%s" % (self.server), + self.lp, self.creds) + + def create_zone(self, zone): + zone_create = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN() + zone_create.pszZoneName = zone + zone_create.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY + zone_create.fAllowUpdate = dnsp.DNS_ZONE_UPDATE_SECURE + zone_create.fAging = 0 + zone_create.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT + self.rpc_conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + None, + 0, + 'ZoneCreate', + dnsserver.DNSSRV_TYPEID_ZONE_CREATE, + zone_create) + + def delete_zone(self, zone): + self.rpc_conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + zone, + 0, + 'DeleteZoneFromDs', + dnsserver.DNSSRV_TYPEID_NULL, + None) + + def test_soa_query(self): + zone = "test.lan" + p = self.make_name_packet(dns.DNS_OPCODE_QUERY) + questions = [] + + q = self.make_name_question(zone, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN) + questions.append(q) + self.finish_name_packet(p, questions) + + response = self.dns_transaction_udp(p) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NXDOMAIN) + self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY) + self.assertEquals(response.ancount, 0) + + self.create_zone(zone) + response = self.dns_transaction_udp(p) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY) + self.assertEquals(response.ancount, 1) + self.assertEquals(response.answers[0].rr_type, dns.DNS_QTYPE_SOA) + + self.delete_zone(zone) + response = self.dns_transaction_udp(p) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NXDOMAIN) + self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY) + self.assertEquals(response.ancount, 0) + + if __name__ == "__main__": import unittest -- 2.1.4 From e6353e12698d0e19cef05f7065c9f31ce81f1971 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 19 Dec 2014 14:40:28 +1300 Subject: [PATCH 4/7] dsdb: Use ldb_attr_cmp() for comparing objectclass names This is the same as strcasecmp, but it is best to remain consistent. Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit e9f6dc730df7c496b743f258b03d6fbcef76211e) BUG: https://bugzilla.samba.org/show_bug.cgi?id=9404 --- source4/dsdb/samdb/ldb_modules/dns_notify.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/dns_notify.c b/source4/dsdb/samdb/ldb_modules/dns_notify.c index 28a1be9..2d3ed86 100644 --- a/source4/dsdb/samdb/ldb_modules/dns_notify.c +++ b/source4/dsdb/samdb/ldb_modules/dns_notify.c @@ -160,7 +160,7 @@ static int dns_notify_add(struct ldb_module *module, struct ldb_request *req) return ldb_operr(ldb); } - if (strcasecmp(objectclass->lDAPDisplayName, "dnsZone") == 0) { + if (ldb_attr_cmp(objectclass->lDAPDisplayName, "dnsZone") == 0) { data->reload_zones = true; break; } @@ -233,7 +233,7 @@ static int dns_notify_modify(struct ldb_module *module, struct ldb_request *req) return ldb_operr(ldb); } - if (strcasecmp(objectclass->lDAPDisplayName, "dnsZone") == 0) { + if (ldb_attr_cmp(objectclass->lDAPDisplayName, "dnsZone") == 0) { data->reload_zones = true; break; } @@ -306,7 +306,7 @@ static int dns_notify_delete(struct ldb_module *module, struct ldb_request *req) return ldb_operr(ldb); } - if (strcasecmp(objectclass->lDAPDisplayName, "dnsZone") == 0) { + if (ldb_attr_cmp(objectclass->lDAPDisplayName, "dnsZone") == 0) { data->reload_zones = true; break; } -- 2.1.4 From 204854507cfc3f484e10a30e2e8e428e1ed278ea Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 19 Dec 2014 14:41:40 +1300 Subject: [PATCH 5/7] dsdb: Use a fixed set of attributes in search in dns_notify module Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit bb886401e8b1c991684c0b9d855b976728e44f13) BUG: https://bugzilla.samba.org/show_bug.cgi?id=9404 --- source4/dsdb/samdb/ldb_modules/dns_notify.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/dns_notify.c b/source4/dsdb/samdb/ldb_modules/dns_notify.c index 2d3ed86..bee6a6e 100644 --- a/source4/dsdb/samdb/ldb_modules/dns_notify.c +++ b/source4/dsdb/samdb/ldb_modules/dns_notify.c @@ -180,6 +180,7 @@ static int dns_notify_modify(struct ldb_module *module, struct ldb_request *req) struct ldb_result *res; struct dsdb_schema *schema; const struct dsdb_class *objectclass; + const char * const attrs[] = { "objectClass", NULL }; int ret; if (ldb_dn_is_special(req->op.mod.message->dn)) { @@ -206,7 +207,7 @@ static int dns_notify_modify(struct ldb_module *module, struct ldb_request *req) if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) { dn = ldb_dn_copy(tmp_ctx, req->op.mod.message->dn); - ret = dsdb_module_search_dn(module, tmp_ctx, &res, dn, NULL, + ret = dsdb_module_search_dn(module, tmp_ctx, &res, dn, attrs, DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_RECYCLED | DSDB_SEARCH_REVEAL_INTERNALS | @@ -254,6 +255,7 @@ static int dns_notify_delete(struct ldb_module *module, struct ldb_request *req) struct ldb_result *res; struct dsdb_schema *schema; const struct dsdb_class *objectclass; + const char * const attrs[] = { "objectClass", NULL }; int ret; if (ldb_dn_is_special(req->op.del.dn)) { @@ -279,7 +281,7 @@ static int dns_notify_delete(struct ldb_module *module, struct ldb_request *req) for (w = data->watched; w; w = w->next) { if (ldb_dn_compare_base(w->dn, req->op.add.message->dn) == 0) { old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn); - ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL, + ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, attrs, DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_RECYCLED | DSDB_SEARCH_REVEAL_INTERNALS | -- 2.1.4 From a957f66483b913c1fdf6ac0313cd7610385e904c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 19 Dec 2014 14:42:08 +1300 Subject: [PATCH 6/7] dsdb: Ignore errors from search in dns_notify module This ensures the error messages are unchanged Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit eabc177bf674a43f6b9442c8a89af0203befb3f1) BUG: https://bugzilla.samba.org/show_bug.cgi?id=9404 --- source4/dsdb/samdb/ldb_modules/dns_notify.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/dns_notify.c b/source4/dsdb/samdb/ldb_modules/dns_notify.c index bee6a6e..2481a41 100644 --- a/source4/dsdb/samdb/ldb_modules/dns_notify.c +++ b/source4/dsdb/samdb/ldb_modules/dns_notify.c @@ -213,13 +213,12 @@ static int dns_notify_modify(struct ldb_module *module, struct ldb_request *req) DSDB_SEARCH_REVEAL_INTERNALS | DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req); if (ret != LDB_SUCCESS) { - ldb_asprintf_errstring(ldb_module_get_ctx(module), - "%s: Failed to modify %s, because we failed to find it: %s\n", - __func__, - ldb_dn_get_linearized(dn), - ldb_errstring(ldb_module_get_ctx(module))); - talloc_free(tmp_ctx); - return ret; + /* + * We want the give the caller the + * error from trying the actual + * request, below + */ + break; } schema = dsdb_get_schema(ldb, req); @@ -287,13 +286,12 @@ static int dns_notify_delete(struct ldb_module *module, struct ldb_request *req) DSDB_SEARCH_REVEAL_INTERNALS | DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, req); if (ret != LDB_SUCCESS) { - ldb_asprintf_errstring(ldb_module_get_ctx(module), - "%s: Failed to delete %s, because we failed to find it: %s\n", - __func__, - ldb_dn_get_linearized(old_dn), - ldb_errstring(ldb_module_get_ctx(module))); - talloc_free(tmp_ctx); - return ret; + /* + * We want the give the caller the + * error from trying the actual + * request, below + */ + break; } schema = dsdb_get_schema(ldb, req); -- 2.1.4 From e91a9daa4ce3085dcb603c3aa2bf70deb7422399 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 19 Dec 2014 15:14:22 +1300 Subject: [PATCH 7/7] dns.py: Always remove the test zone in tearDown() Change-Id: Ic6d6c51579f8859b4e396179123974382c253bf7 Signed-off-by: Garming Sam Pair-programmed-with: Garming Sam Signed-off-by: Andrew Bartlett Autobuild-User(master): Garming Sam Autobuild-Date(master): Mon Dec 22 08:21:22 CET 2014 on sn-devel-104 (cherry picked from commit ad074795e33b676595aa7fb1b0129a0b5b8cbf24) BUG: https://bugzilla.samba.org/show_bug.cgi?id=9404 --- python/samba/tests/dns.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/samba/tests/dns.py b/python/samba/tests/dns.py index b8ce32a..92b94a6 100644 --- a/python/samba/tests/dns.py +++ b/python/samba/tests/dns.py @@ -886,6 +886,14 @@ class TestZones(DNSTest): self.rpc_conn = dnsserver.dnsserver("ncacn_ip_tcp:%s" % (self.server), self.lp, self.creds) + def tearDown(self): + super(TestZones, self).tearDown() + try: + self.delete_zone(self.zone) + except RuntimeError, (num, string): + if num != 9601: #WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST + raise + def create_zone(self, zone): zone_create = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN() zone_create.pszZoneName = zone -- 2.1.4