Merge remote-tracking branch 'origin/v4-10-test' into HEAD
[samba.git] / source4 / dsdb / dns / dns_update.c
index ae5be44366fbba028c1d3b2b9effecc664aa5c77..0a1f0ac2330df77e73714694321ce0626e2a3d01 100644 (file)
@@ -1,5 +1,5 @@
 /*
-   Unix SMB/CIFS mplementation.
+   Unix SMB/CIFS Implementation.
 
    DNS update service
 
 #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(TALLOC_CTX *);
 
 struct dnsupdate_service {
        struct task_server *task;
@@ -77,7 +81,7 @@ static void dnsupdate_rndc_done(struct tevent_req *subreq)
        ret = samba_runcmd_recv(subreq, &sys_errno);
        TALLOC_FREE(subreq);
        if (ret != 0) {
-               service->confupdate.status = map_nt_error_from_unix(sys_errno);
+               service->confupdate.status = map_nt_error_from_unix_common(sys_errno);
        } else {
                service->confupdate.status = NT_STATUS_OK;
        }
@@ -97,40 +101,126 @@ static void dnsupdate_rebuild(struct dnsupdate_service *service)
 {
        int ret;
        size_t size;
-       struct ldb_result *res;
+       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 *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);
        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);
 
-       ret = ldb_search(service->samdb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
-                        attrs, "(&(primaryGroupID=%u)(objectClass=computer))",
-                        DOMAIN_RID_DCS);
+       /* 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)));
+               DBG_ERR("Unable to find DCs list - %s\n",
+                       ldb_errstring(service->samdb));
                talloc_free(tmp_ctx);
                return;
        }
 
+       dc_list = talloc_array(tmp_ctx, const char *, 0);
+       for (i=0; i<res1->count; 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");
+               if (path == NULL) {
+                       DBG_ERR("Out of memory!");
+                       talloc_free(tmp_ctx);
+                       return;
+               }
+
+               /*
+                * If the file doesn't exist, we provisioned in a the new
+                * bind-dns directory
+                */
+               if (!file_exist(path)) {
+                       path = talloc_asprintf(tmp_ctx,
+                                              "%s/named.conf.update",
+                                              lpcfg_binddns_dir(service->task->lp_ctx));
+                       if (path == NULL) {
+                               DBG_ERR("Out of memory!");
+                               talloc_free(tmp_ctx);
+                               return;
+                       }
+               }
        }
 
        path_static = lpcfg_parm_string(service->task->lp_ctx, NULL, "dnsupdate", "extra_static_grant_rules");
        if (path_static == NULL) {
-               path_static = private_path(tmp_ctx, service->task->lp_ctx, "named.conf.update.static");
+               path_static = lpcfg_private_path(tmp_ctx,
+                                                service->task->lp_ctx,
+                                                "named.conf.update.static");
+               if (path_static == NULL) {
+                       DBG_ERR("Out of memory!");
+                       talloc_free(tmp_ctx);
+                       return;
+               }
+
+               if (!file_exist(path_static)) {
+                       path_static = talloc_asprintf(tmp_ctx,
+                                                     "%s/named.conf.update.static",
+                                                     lpcfg_binddns_dir(service->task->lp_ctx));
+                       if (path_static == NULL) {
+                               DBG_ERR("Out of memory!");
+                               talloc_free(tmp_ctx);
+                               return;
+                       }
+               }
        }
 
        tmp_path = talloc_asprintf(tmp_ctx, "%s.tmp", path);
-       if (path == NULL || tmp_path == NULL || path_static == NULL ) {
+       if (tmp_path == NULL) {
                DEBUG(0,(__location__ ": Unable to get paths\n"));
                talloc_free(tmp_ctx);
                return;
@@ -154,15 +244,10 @@ static void dnsupdate_rebuild(struct dnsupdate_service *service)
                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; i<res->count; 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; i<dc_count; i++) {
+               dprintf(fd, "\tgrant %s wildcard * A AAAA SRV CNAME;\n", dc_list[i]);
        }
        dprintf(fd, "};\n");
        close(fd);
@@ -240,15 +325,10 @@ static void dnsupdate_nameupdate_done(struct tevent_req *subreq)
 
        ret = samba_runcmd_recv(subreq, &sys_errno);
        TALLOC_FREE(subreq);
-       if (ret != 0) {
-               service->nameupdate.status = map_nt_error_from_unix(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)));
+       if (ret != 0) {
+               DBG_ERR("Failed DNS update with exit code %d\n",
+                       sys_errno);
        } else {
                DEBUG(3,("Completed DNS update check OK\n"));
        }
@@ -270,14 +350,8 @@ static void dnsupdate_spnupdate_done(struct tevent_req *subreq)
        ret = samba_runcmd_recv(subreq, &sys_errno);
        TALLOC_FREE(subreq);
        if (ret != 0) {
-               service->nameupdate.status = map_nt_error_from_unix(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)));
+               DEBUG(0,(__location__ ": Failed SPN update - with error code %d\n",
+                        sys_errno));
        } else {
                DEBUG(3,("Completed SPN update check OK\n"));
        }
@@ -354,6 +428,7 @@ struct dnsupdate_RODC_state {
        struct irpc_message *msg;
        struct dnsupdate_RODC *r;
        char *tmp_path;
+       char *tmp_path2;
        int fd;
 };
 
@@ -363,6 +438,9 @@ static int dnsupdate_RODC_destructor(struct dnsupdate_RODC_state *st)
                close(st->fd);
        }
        unlink(st->tmp_path);
+       if (st->tmp_path2 != NULL) {
+               unlink(st->tmp_path2);
+       }
        return 0;
 }
 
@@ -380,7 +458,7 @@ static void dnsupdate_RODC_callback(struct tevent_req *req)
        ret = samba_runcmd_recv(req, &sys_errno);
        talloc_free(req);
        if (ret != 0) {
-               st->r->out.result = map_nt_error_from_unix(sys_errno);
+               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;
@@ -409,7 +487,8 @@ static NTSTATUS dnsupdate_dnsupdate_RODC(struct irpc_message *msg,
        struct tevent_req *req;
        int i, ret;
        struct GUID ntds_guid;
-       const char *site, *dnsdomain, *dnsforest, *ntdsguid, *hostname;
+       const char *site, *dnsdomain, *dnsforest, *ntdsguid;
+       const char *hostname = NULL;
        struct ldb_dn *sid_dn;
        const char *attrs[] = { "dNSHostName", NULL };
        struct ldb_result *res;
@@ -440,6 +519,13 @@ static NTSTATUS dnsupdate_dnsupdate_RODC(struct irpc_message *msg,
 
        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, "<SID=%s>", dom_sid_string(st, r->in.dom_sid));
        if (!sid_dn) {
                talloc_free(st);
@@ -470,7 +556,7 @@ static NTSTATUS dnsupdate_dnsupdate_RODC(struct irpc_message *msg,
 
 
        /* find dnsdomain and dnsforest */
-       dnsdomain = lpcfg_realm(s->task->lp_ctx);
+       dnsdomain = lpcfg_dnsdomain(s->task->lp_ctx);
        dnsforest = dnsdomain;
 
        /* find the hostname */
@@ -486,36 +572,35 @@ static NTSTATUS dnsupdate_dnsupdate_RODC(struct irpc_message *msg,
                return NT_STATUS_OK;
        }
 
-
        for (i=0; i<st->r->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",
+                       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",
+                       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",
+                       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",
+                       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",
+                       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",
+                       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",
+                       dprintf(st->fd, "SRV _gc._tcp.%s._sites.%s %s %u\n",
                                site, dnsforest, hostname, n->port);
                        break;
                }
@@ -532,6 +617,8 @@ static NTSTATUS dnsupdate_dnsupdate_RODC(struct irpc_message *msg,
                                dns_update_command,
                                "--update-list",
                                st->tmp_path,
+                               "--update-cache",
+                               st->tmp_path2,
                                NULL);
        NT_STATUS_HAVE_NO_MEMORY(req);
 
@@ -546,14 +633,14 @@ static NTSTATUS dnsupdate_dnsupdate_RODC(struct irpc_message *msg,
 /*
   startup the dns update task
 */
-static void dnsupdate_task_init(struct task_server *task)
+static NTSTATUS dnsupdate_task_init(struct task_server *task)
 {
        NTSTATUS status;
        struct dnsupdate_service *service;
 
-       if (lpcfg_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;
+               return NT_STATUS_INVALID_DOMAIN_ROLE;
        }
 
        task_server_set_title(task, "task[dnsupdate]");
@@ -561,7 +648,7 @@ static void dnsupdate_task_init(struct task_server *task)
        service = talloc_zero(task, struct dnsupdate_service);
        if (!service) {
                task_server_terminate(task, "dnsupdate_task_init: out of memory", true);
-               return;
+               return NT_STATUS_NO_MEMORY;
        }
        service->task           = task;
        task->private_data      = service;
@@ -571,15 +658,19 @@ static void dnsupdate_task_init(struct task_server *task)
                task_server_terminate(task,
                                      "dnsupdate: Failed to obtain server credentials\n",
                                      true);
-               return;
+               return NT_STATUS_UNSUCCESSFUL;
        }
 
-       service->samdb = samdb_connect(service, service->task->event_ctx, task->lp_ctx,
-                                      service->system_session_info, 0);
+       service->samdb = samdb_connect(service,
+                                      service->task->event_ctx,
+                                      task->lp_ctx,
+                                      service->system_session_info,
+                                      NULL,
+                                      0);
        if (!service->samdb) {
                task_server_terminate(task, "dnsupdate: Failed to connect to local samdb\n",
                                      true);
-               return;
+               return NT_STATUS_UNSUCCESSFUL;
        }
 
        service->confupdate.interval    = lpcfg_parm_int(task->lp_ctx, NULL,
@@ -594,7 +685,7 @@ static void dnsupdate_task_init(struct task_server *task)
                task_server_terminate(task, talloc_asprintf(task,
                                      "dnsupdate: Failed to confupdate schedule: %s\n",
                                                            nt_errstr(status)), true);
-               return;
+               return status;
        }
 
        dnsupdate_check_names(service);
@@ -603,7 +694,7 @@ static void dnsupdate_task_init(struct task_server *task)
                task_server_terminate(task, talloc_asprintf(task,
                                      "dnsupdate: Failed to nameupdate schedule: %s\n",
                                                            nt_errstr(status)), true);
-               return;
+               return status;
        }
 
        irpc_add_name(task->msg_ctx, "dnsupdate");
@@ -613,13 +704,20 @@ static void dnsupdate_task_init(struct task_server *task)
 
        /* create the intial file */
        dnsupdate_rebuild(service);
+       return NT_STATUS_OK;
 
 }
 
 /*
   register ourselves as a available server
 */
-NTSTATUS server_service_dnsupdate_init(void)
+NTSTATUS server_service_dnsupdate_init(TALLOC_CTX *ctx)
 {
-       return register_server_service("dnsupdate", dnsupdate_task_init);
+       static const struct service_details details = {
+               .inhibit_fork_on_accept = true,
+               .inhibit_pre_fork = true,
+               .task_init = dnsupdate_task_init,
+               .post_fork = NULL
+       };
+       return register_server_service(ctx, "dnsupdate", &details);
 }