X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Fdsdb%2Fdns%2Fdns_update.c;h=292a825092349e433e42dfa486c755900a8720f4;hb=773cfba9af34e64b96e843b1b60afa5a0b0dec32;hp=4fefd656d71bbf584fee2f9d583a9841139c025e;hpb=b0b857d6ca842fb516b3330a11e0d6d78f35aded;p=obnox%2Fsamba%2Fsamba-obnox.git diff --git a/source4/dsdb/dns/dns_update.c b/source4/dsdb/dns/dns_update.c index 4fefd656d71..292a8250923 100644 --- a/source4/dsdb/dns/dns_update.c +++ b/source4/dsdb/dns/dns_update.c @@ -1,7 +1,7 @@ /* Unix SMB/CIFS mplementation. - DNS udpate service + DNS update service Copyright (C) Andrew Tridgell 2009 @@ -35,7 +35,13 @@ #include "lib/messaging/irpc.h" #include "param/param.h" #include "system/filesys.h" +#include "dsdb/common/util.h" #include "libcli/composite/composite.h" +#include "libcli/security/dom_sid.h" +#include "librpc/gen_ndr/ndr_irpc.h" +#include "libds/common/roles.h" + +NTSTATUS server_service_dnsupdate_init(void); struct dnsupdate_service { struct task_server *task; @@ -46,7 +52,7 @@ struct dnsupdate_service { struct { uint32_t interval; struct tevent_timer *te; - struct composite_context *c; + struct tevent_req *subreq; NTSTATUS status; } confupdate; @@ -54,7 +60,8 @@ struct dnsupdate_service { struct { uint32_t interval; struct tevent_timer *te; - struct composite_context *c; + struct tevent_req *subreq; + struct tevent_req *spnreq; NTSTATUS status; } nameupdate; }; @@ -62,13 +69,23 @@ struct dnsupdate_service { /* called when rndc reload has finished */ -static void dnsupdate_rndc_done(struct composite_context *c) +static void dnsupdate_rndc_done(struct tevent_req *subreq) { - struct dnsupdate_service *service = talloc_get_type_abort(c->async.private_data, - struct dnsupdate_service); - service->confupdate.status = composite_wait(c); - talloc_free(c); - service->confupdate.c = NULL; + struct dnsupdate_service *service = tevent_req_callback_data(subreq, + struct dnsupdate_service); + int ret; + int sys_errno; + + service->confupdate.subreq = NULL; + + ret = samba_runcmd_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (ret != 0) { + service->confupdate.status = map_nt_error_from_unix_common(sys_errno); + } else { + service->confupdate.status = NT_STATUS_OK; + } + if (!NT_STATUS_IS_OK(service->confupdate.status)) { DEBUG(0,(__location__ ": Failed rndc update - %s\n", nt_errstr(service->confupdate.status))); @@ -83,35 +100,93 @@ static void dnsupdate_rndc_done(struct composite_context *c) static void dnsupdate_rebuild(struct dnsupdate_service *service) { int ret; - struct ldb_result *res; - const char *tmp_path, *path; + size_t size; + struct ldb_result *res1, *res2; + const char *tmp_path, *path, *path_static; + char *static_policies; int fd; unsigned int i; - const char *attrs[] = { "sAMAccountName", NULL }; - const char *realm = lp_realm(service->task->lp_ctx); + const char *attrs1[] = { "msDS-HasDomainNCs", NULL }; + const char *attrs2[] = { "name", NULL }; + const char *realm = lpcfg_realm(service->task->lp_ctx); TALLOC_CTX *tmp_ctx = talloc_new(service); - - ret = ldb_search(service->samdb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, - attrs, "(&(primaryGroupID=%u)(objectClass=computer))", - DOMAIN_RID_DCS); + const char * const *rndc_command = lpcfg_rndc_command(service->task->lp_ctx); + const char **dc_list; + int dc_count=0; + + /* abort any pending script run */ + TALLOC_FREE(service->confupdate.subreq); + + /* find the DNs for all the non-RODC DCs in the forest */ + ret = dsdb_search(service->samdb, tmp_ctx, &res1, ldb_get_config_basedn(service->samdb), + LDB_SCOPE_SUBTREE, + attrs1, + 0, + "(&(objectclass=NTDSDSA)(!(msDS-isRODC=TRUE)))"); if (ret != LDB_SUCCESS) { DEBUG(0,(__location__ ": Unable to find DCs list - %s", ldb_errstring(service->samdb))); talloc_free(tmp_ctx); return; } - path = lp_parm_string(service->task->lp_ctx, NULL, "dnsupdate", "path"); + dc_list = talloc_array(tmp_ctx, const char *, 0); + for (i=0; icount; i++) { + struct ldb_dn *server_dn = res1->msgs[i]->dn; + struct ldb_dn *domain_dn; + const char *acct_name, *full_account, *dns_domain; + + /* this is a nasty hack to form the account name of + * this DC. We do it this way as we don't necessarily + * have access to the domain NC, so all we have to go + * on is what is in the configuration partition + */ + + domain_dn = ldb_msg_find_attr_as_dn(service->samdb, tmp_ctx, res1->msgs[i], "msDS-HasDomainNCs"); + if (domain_dn == NULL) continue; + + ldb_dn_remove_child_components(server_dn, 1); + ret = dsdb_search_dn(service->samdb, tmp_ctx, &res2, server_dn, attrs2, 0); + if (ret != LDB_SUCCESS) { + continue; + } + + acct_name = ldb_msg_find_attr_as_string(res2->msgs[0], "name", NULL); + if (acct_name == NULL) continue; + + dns_domain = samdb_dn_to_dns_domain(tmp_ctx, domain_dn); + if (dns_domain == NULL) { + continue; + } + + full_account = talloc_asprintf(tmp_ctx, "%s$@%s", acct_name, dns_domain); + if (full_account == NULL) continue; + + dc_list = talloc_realloc(tmp_ctx, dc_list, const char *, dc_count+1); + if (dc_list == NULL) { + continue; + } + dc_list[dc_count++] = full_account; + } + + path = lpcfg_parm_string(service->task->lp_ctx, NULL, "dnsupdate", "path"); if (path == NULL) { - path = private_path(tmp_ctx, service->task->lp_ctx, "named.conf.update"); + path = lpcfg_private_path(tmp_ctx, service->task->lp_ctx, "named.conf.update"); + } + + path_static = lpcfg_parm_string(service->task->lp_ctx, NULL, "dnsupdate", "extra_static_grant_rules"); + if (path_static == NULL) { + path_static = lpcfg_private_path(tmp_ctx, service->task->lp_ctx, "named.conf.update.static"); } tmp_path = talloc_asprintf(tmp_ctx, "%s.tmp", path); - if (path == NULL || tmp_path == NULL) { - DEBUG(0,(__location__ ": Unable to get paths")); + if (path == NULL || tmp_path == NULL || path_static == NULL ) { + DEBUG(0,(__location__ ": Unable to get paths\n")); talloc_free(tmp_ctx); return; } + static_policies = file_load(path_static, &size, 0, tmp_ctx); + unlink(tmp_path); fd = open(tmp_path, O_CREAT|O_TRUNC|O_WRONLY, 0444); if (fd == -1) { @@ -122,24 +197,20 @@ static void dnsupdate_rebuild(struct dnsupdate_service *service) dprintf(fd, "/* this file is auto-generated - do not edit */\n"); dprintf(fd, "update-policy {\n"); + if( static_policies != NULL ) { + dprintf(fd, "/* Start of static entries */\n"); + dprintf(fd, "%s\n",static_policies); + dprintf(fd, "/* End of static entries */\n"); + } dprintf(fd, "\tgrant %s ms-self * A AAAA;\n", realm); - dprintf(fd, "\tgrant administrator@%s wildcard * A AAAA SRV CNAME TXT;\n", realm); + dprintf(fd, "\tgrant Administrator@%s wildcard * A AAAA SRV CNAME;\n", realm); - for (i=0; icount; i++) { - const char *acctname; - acctname = ldb_msg_find_attr_as_string(res->msgs[i], - "sAMAccountName", NULL); - if (!acctname) continue; - dprintf(fd, "\tgrant %s@%s wildcard * A AAAA SRV CNAME;\n", - acctname, realm); + for (i=0; iconfupdate.c != NULL) { - talloc_free(service->confupdate.c); - service->confupdate.c = NULL; - } if (NT_STATUS_IS_OK(service->confupdate.status) && file_compare(tmp_path, path) == true) { @@ -156,13 +227,20 @@ static void dnsupdate_rebuild(struct dnsupdate_service *service) } DEBUG(2,("Loading new DNS update grant rules\n")); - service->confupdate.c = samba_runcmd(service->task->event_ctx, service, - timeval_current_ofs(10, 0), - 2, 0, - lp_rndc_command(service->task->lp_ctx), - "reload", NULL); - service->confupdate.c->async.fn = dnsupdate_rndc_done; - service->confupdate.c->async.private_data = service; + service->confupdate.subreq = samba_runcmd_send(service, + service->task->event_ctx, + timeval_current_ofs(10, 0), + 2, 0, + rndc_command, + "reload", NULL); + if (service->confupdate.subreq == NULL) { + DEBUG(0,(__location__ ": samba_runcmd_send() failed with no memory\n")); + talloc_free(tmp_ctx); + return; + } + tevent_req_set_callback(service->confupdate.subreq, + dnsupdate_rndc_done, + service); talloc_free(tmp_ctx); } @@ -195,13 +273,23 @@ static NTSTATUS dnsupdate_confupdate_schedule(struct dnsupdate_service *service) /* called when dns update script has finished */ -static void dnsupdate_nameupdate_done(struct composite_context *c) +static void dnsupdate_nameupdate_done(struct tevent_req *subreq) { - struct dnsupdate_service *service = talloc_get_type_abort(c->async.private_data, - struct dnsupdate_service); - service->nameupdate.status = composite_wait(c); - talloc_free(c); - service->nameupdate.c = NULL; + struct dnsupdate_service *service = tevent_req_callback_data(subreq, + struct dnsupdate_service); + int ret; + int sys_errno; + + service->nameupdate.subreq = NULL; + + ret = samba_runcmd_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (ret != 0) { + service->nameupdate.status = map_nt_error_from_unix_common(sys_errno); + } else { + service->nameupdate.status = NT_STATUS_OK; + } + if (!NT_STATUS_IS_OK(service->nameupdate.status)) { DEBUG(0,(__location__ ": Failed DNS update - %s\n", nt_errstr(service->nameupdate.status))); @@ -210,25 +298,75 @@ static void dnsupdate_nameupdate_done(struct composite_context *c) } } + +/* + called when spn update script has finished + */ +static void dnsupdate_spnupdate_done(struct tevent_req *subreq) +{ + struct dnsupdate_service *service = tevent_req_callback_data(subreq, + struct dnsupdate_service); + int ret; + int sys_errno; + + service->nameupdate.spnreq = NULL; + + ret = samba_runcmd_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (ret != 0) { + service->nameupdate.status = map_nt_error_from_unix_common(sys_errno); + } else { + service->nameupdate.status = NT_STATUS_OK; + } + + if (!NT_STATUS_IS_OK(service->nameupdate.status)) { + DEBUG(0,(__location__ ": Failed SPN update - %s\n", + nt_errstr(service->nameupdate.status))); + } else { + DEBUG(3,("Completed SPN update check OK\n")); + } +} + /* called every 'dnsupdate:name interval' seconds */ static void dnsupdate_check_names(struct dnsupdate_service *service) { + const char * const *dns_update_command = lpcfg_dns_update_command(service->task->lp_ctx); + const char * const *spn_update_command = lpcfg_spn_update_command(service->task->lp_ctx); + /* kill any existing child */ - if (service->nameupdate.c != NULL) { - talloc_free(service->nameupdate.c); - service->nameupdate.c = NULL; - } + TALLOC_FREE(service->nameupdate.subreq); DEBUG(3,("Calling DNS name update script\n")); - service->nameupdate.c = samba_runcmd(service->task->event_ctx, service, - timeval_current_ofs(10, 0), - 2, 0, - lp_dns_update_command(service->task->lp_ctx), - NULL); - service->nameupdate.c->async.fn = dnsupdate_nameupdate_done; - service->nameupdate.c->async.private_data = service; + service->nameupdate.subreq = samba_runcmd_send(service, + service->task->event_ctx, + timeval_current_ofs(20, 0), + 2, 0, + dns_update_command, + NULL); + if (service->nameupdate.subreq == NULL) { + DEBUG(0,(__location__ ": samba_runcmd_send() failed with no memory\n")); + return; + } + tevent_req_set_callback(service->nameupdate.subreq, + dnsupdate_nameupdate_done, + service); + + DEBUG(3,("Calling SPN name update script\n")); + service->nameupdate.spnreq = samba_runcmd_send(service, + service->task->event_ctx, + timeval_current_ofs(20, 0), + 2, 0, + spn_update_command, + NULL); + if (service->nameupdate.spnreq == NULL) { + DEBUG(0,(__location__ ": samba_runcmd_send() failed with no memory\n")); + return; + } + tevent_req_set_callback(service->nameupdate.spnreq, + dnsupdate_spnupdate_done, + service); } static NTSTATUS dnsupdate_nameupdate_schedule(struct dnsupdate_service *service); @@ -255,6 +393,214 @@ static NTSTATUS dnsupdate_nameupdate_schedule(struct dnsupdate_service *service) return NT_STATUS_OK; } + +struct dnsupdate_RODC_state { + struct irpc_message *msg; + struct dnsupdate_RODC *r; + char *tmp_path; + char *tmp_path2; + int fd; +}; + +static int dnsupdate_RODC_destructor(struct dnsupdate_RODC_state *st) +{ + if (st->fd != -1) { + close(st->fd); + } + unlink(st->tmp_path); + if (st->tmp_path2 != NULL) { + unlink(st->tmp_path2); + } + return 0; +} + +/* + called when the DNS update has completed + */ +static void dnsupdate_RODC_callback(struct tevent_req *req) +{ + struct dnsupdate_RODC_state *st = + tevent_req_callback_data(req, + struct dnsupdate_RODC_state); + int sys_errno; + int i, ret; + + ret = samba_runcmd_recv(req, &sys_errno); + talloc_free(req); + if (ret != 0) { + st->r->out.result = map_nt_error_from_unix_common(sys_errno); + DEBUG(2,(__location__ ": RODC DNS Update failed: %s\n", nt_errstr(st->r->out.result))); + } else { + st->r->out.result = NT_STATUS_OK; + DEBUG(3,(__location__ ": RODC DNS Update OK\n")); + } + + for (i=0; ir->in.dns_names->count; i++) { + st->r->out.dns_names->names[i].status = NT_STATUS_V(st->r->out.result); + } + + irpc_send_reply(st->msg, NT_STATUS_OK); +} + + +/** + * Called when we get a RODC DNS update request from the netlogon + * rpc server + */ +static NTSTATUS dnsupdate_dnsupdate_RODC(struct irpc_message *msg, + struct dnsupdate_RODC *r) +{ + struct dnsupdate_service *s = talloc_get_type(msg->private_data, + struct dnsupdate_service); + const char * const *dns_update_command = lpcfg_dns_update_command(s->task->lp_ctx); + struct dnsupdate_RODC_state *st; + struct tevent_req *req; + int i, ret; + struct GUID ntds_guid; + const char *site, *dnsdomain, *dnsforest, *ntdsguid; + const char *hostname = NULL; + struct ldb_dn *sid_dn; + const char *attrs[] = { "dNSHostName", NULL }; + struct ldb_result *res; + + st = talloc_zero(msg, struct dnsupdate_RODC_state); + if (!st) { + r->out.result = NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; + } + + st->r = r; + st->msg = msg; + + st->tmp_path = smbd_tmp_path(st, s->task->lp_ctx, "rodcdns.XXXXXX"); + if (!st->tmp_path) { + talloc_free(st); + r->out.result = NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; + } + + st->fd = mkstemp(st->tmp_path); + if (st->fd == -1) { + DEBUG(0,("Unable to create a temporary file for RODC dnsupdate\n")); + talloc_free(st); + r->out.result = NT_STATUS_INTERNAL_DB_CORRUPTION; + return NT_STATUS_OK; + } + + talloc_set_destructor(st, dnsupdate_RODC_destructor); + + st->tmp_path2 = talloc_asprintf(st, "%s.cache", st->tmp_path); + if (!st->tmp_path2) { + talloc_free(st); + r->out.result = NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; + } + + sid_dn = ldb_dn_new_fmt(st, s->samdb, "", dom_sid_string(st, r->in.dom_sid)); + if (!sid_dn) { + talloc_free(st); + r->out.result = NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; + } + + /* work out the site */ + ret = samdb_find_site_for_computer(s->samdb, st, sid_dn, &site); + if (ret != LDB_SUCCESS) { + DEBUG(2, (__location__ ": Unable to find site for computer %s\n", + ldb_dn_get_linearized(sid_dn))); + talloc_free(st); + r->out.result = NT_STATUS_NO_SUCH_USER; + return NT_STATUS_OK; + } + + /* work out the ntdsguid */ + ret = samdb_find_ntdsguid_for_computer(s->samdb, sid_dn, &ntds_guid); + ntdsguid = GUID_string(st, &ntds_guid); + if (ret != LDB_SUCCESS || !ntdsguid) { + DEBUG(2, (__location__ ": Unable to find NTDS GUID for computer %s\n", + ldb_dn_get_linearized(sid_dn))); + talloc_free(st); + r->out.result = NT_STATUS_NO_SUCH_USER; + return NT_STATUS_OK; + } + + + /* find dnsdomain and dnsforest */ + dnsdomain = lpcfg_realm(s->task->lp_ctx); + dnsforest = dnsdomain; + + /* find the hostname */ + ret = dsdb_search_dn(s->samdb, st, &res, sid_dn, attrs, 0); + if (ret == LDB_SUCCESS) { + hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL); + } + if (ret != LDB_SUCCESS || !hostname) { + DEBUG(2, (__location__ ": Unable to find NTDS GUID for computer %s\n", + ldb_dn_get_linearized(sid_dn))); + talloc_free(st); + r->out.result = NT_STATUS_NO_SUCH_USER; + return NT_STATUS_OK; + } + + + for (i=0; ir->in.dns_names->count; i++) { + struct NL_DNS_NAME_INFO *n = &r->in.dns_names->names[i]; + switch (n->type) { + case NlDnsLdapAtSite: + dprintf(st->fd, "SRV _ldap._tcp.%s._sites.%s. %s %u\n", + site, dnsdomain, hostname, n->port); + break; + case NlDnsGcAtSite: + dprintf(st->fd, "SRV _ldap._tcp.%s._sites.gc._msdcs.%s. %s %u\n", + site, dnsdomain, hostname, n->port); + break; + case NlDnsDsaCname: + dprintf(st->fd, "CNAME %s._msdcs.%s. %s\n", + ntdsguid, dnsforest, hostname); + break; + case NlDnsKdcAtSite: + dprintf(st->fd, "SRV _kerberos._tcp.%s._sites.dc._msdcs.%s. %s %u\n", + site, dnsdomain, hostname, n->port); + break; + case NlDnsDcAtSite: + dprintf(st->fd, "SRV _ldap._tcp.%s._sites.dc._msdcs.%s. %s %u\n", + site, dnsdomain, hostname, n->port); + break; + case NlDnsRfc1510KdcAtSite: + dprintf(st->fd, "SRV _kerberos._tcp.%s._sites.%s. %s %u\n", + site, dnsdomain, hostname, n->port); + break; + case NlDnsGenericGcAtSite: + dprintf(st->fd, "SRV _gc._tcp.%s._sites.%s. %s %u\n", + site, dnsforest, hostname, n->port); + break; + } + } + + close(st->fd); + st->fd = -1; + + DEBUG(3,("Calling RODC DNS name update script %s\n", st->tmp_path)); + req = samba_runcmd_send(st, + s->task->event_ctx, + timeval_current_ofs(20, 0), + 2, 0, + dns_update_command, + "--update-list", + st->tmp_path, + "--update-cache", + st->tmp_path2, + NULL); + NT_STATUS_HAVE_NO_MEMORY(req); + + /* setup the callback */ + tevent_req_set_callback(req, dnsupdate_RODC_callback, st); + + msg->defer_reply = true; + + return NT_STATUS_OK; +} + /* startup the dns update task */ @@ -263,7 +609,7 @@ static void dnsupdate_task_init(struct task_server *task) NTSTATUS status; struct dnsupdate_service *service; - if (lp_server_role(task->lp_ctx) != ROLE_DOMAIN_CONTROLLER) { + if (lpcfg_server_role(task->lp_ctx) != ROLE_ACTIVE_DIRECTORY_DC) { /* not useful for non-DC */ return; } @@ -287,17 +633,17 @@ static void dnsupdate_task_init(struct task_server *task) } service->samdb = samdb_connect(service, service->task->event_ctx, task->lp_ctx, - service->system_session_info); + service->system_session_info, 0); if (!service->samdb) { task_server_terminate(task, "dnsupdate: Failed to connect to local samdb\n", true); return; } - service->confupdate.interval = lp_parm_int(task->lp_ctx, NULL, + service->confupdate.interval = lpcfg_parm_int(task->lp_ctx, NULL, "dnsupdate", "config interval", 60); /* in seconds */ - service->nameupdate.interval = lp_parm_int(task->lp_ctx, NULL, + service->nameupdate.interval = lpcfg_parm_int(task->lp_ctx, NULL, "dnsupdate", "name interval", 600); /* in seconds */ dnsupdate_rebuild(service); @@ -320,6 +666,9 @@ static void dnsupdate_task_init(struct task_server *task) irpc_add_name(task->msg_ctx, "dnsupdate"); + IRPC_REGISTER(task->msg_ctx, irpc, DNSUPDATE_RODC, + dnsupdate_dnsupdate_RODC, service); + /* create the intial file */ dnsupdate_rebuild(service);