r20315: Implement the server side of DsGetDomainControllerInfo. This is a
authorAndrew Bartlett <abartlet@samba.org>
Fri, 22 Dec 2006 07:04:06 +0000 (07:04 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 19:29:38 +0000 (14:29 -0500)
supprisingly complex call...

It turns out that the in/out parameter 'level' is not in/out, but set
seperatly by the server-side code from r->req.req1.level.

This commit also breaks out some common code from samldb into samdb.

Andrew Bartlett

source/dsdb/samdb/ldb_modules/samldb.c
source/dsdb/samdb/samdb.c
source/librpc/idl/drsuapi.idl
source/rpc_server/drsuapi/dcesrv_drsuapi.c
source/torture/rpc/drsuapi.c

index 26560c361eb26e7fa4b502051ddedfe71fd12229..1c1ff0ea6e2ce5cc0327cda69f6855d0243d8968 100644 (file)
@@ -184,41 +184,6 @@ static int samldb_allocate_next_rid(struct ldb_module *module, TALLOC_CTX *mem_c
        return ret;
 }
 
-/* Find a domain object in the parents of a particular DN.  */
-static struct ldb_dn *samldb_search_domain(struct ldb_module *module, TALLOC_CTX *mem_ctx, struct ldb_dn *dn)
-{
-       TALLOC_CTX *local_ctx;
-       struct ldb_dn *sdn;
-       struct ldb_result *res = NULL;
-       int ret = 0;
-       const char *attrs[] = { NULL };
-
-       local_ctx = talloc_new(mem_ctx);
-       if (local_ctx == NULL) return NULL;
-
-       sdn = ldb_dn_copy(local_ctx, dn);
-       do {
-               ret = ldb_search(module->ldb, sdn, LDB_SCOPE_BASE, 
-                                "(|(objectClass=domain)(objectClass=builtinDomain))", attrs, &res);
-               if (ret == LDB_SUCCESS) {
-                       talloc_steal(local_ctx, res);
-                       if (res->count == 1) {
-                               break;
-                       }
-               }
-       } while ((sdn = ldb_dn_get_parent(local_ctx, sdn)));
-
-       if (ret != LDB_SUCCESS || res->count != 1) {
-               talloc_free(local_ctx);
-               return NULL;
-       }
-
-       talloc_steal(mem_ctx, sdn);
-       talloc_free(local_ctx);
-
-       return sdn;
-}
-
 /* search the domain related to the provided dn
    allocate a new RID for the domain
    return the new sid string
@@ -235,7 +200,7 @@ static int samldb_get_new_sid(struct ldb_module *module,
 
        /* get the domain component part of the provided dn */
 
-       dom_dn = samldb_search_domain(module, mem_ctx, obj_dn);
+       dom_dn = samdb_search_for_parent_domain(module->ldb, mem_ctx, obj_dn);
        if (dom_dn == NULL) {
                ldb_asprintf_errstring(module->ldb,
                                        "Invalid dn (%s) not child of a domain object!\n",
index 9296352b0bc09487e33133632095a8f9e190a2b2..2d811094ff92fd2bbee72092f3ad76e8ee79aae0 100644 (file)
@@ -353,15 +353,11 @@ const char *samdb_result_string(const struct ldb_message *msg, const char *attr,
 struct ldb_dn *samdb_result_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
                               const char *attr, struct ldb_dn *default_value)
 {
-       struct ldb_dn *res_dn;
-       const char *string = samdb_result_string(msg, attr, NULL);
-       if (string == NULL) return default_value;
-       res_dn = ldb_dn_new(mem_ctx, ldb, string);
-       if ( ! ldb_dn_validate(res_dn)) {
-               talloc_free(res_dn);
-               return NULL;
+       struct ldb_dn *ret_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, msg, attr);
+       if (!ret_dn) {
+               return default_value;
        }
-       return res_dn;
+       return ret_dn;
 }
 
 /*
@@ -1182,7 +1178,7 @@ struct ldb_dn *samdb_server_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
 }
 
 /*
-  work out the domain sid for the current open ldb
+  work out if we are the PDC for the domain of the current open ldb
 */
 BOOL samdb_is_pdc(struct ldb_context *ldb)
 {
@@ -1225,6 +1221,41 @@ failed:
        return False;
 }
 
+
+/* Find a domain object in the parents of a particular DN.  */
+struct ldb_dn *samdb_search_for_parent_domain(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn)
+{
+       TALLOC_CTX *local_ctx;
+       struct ldb_dn *sdn = dn;
+       struct ldb_result *res = NULL;
+       int ret = 0;
+       const char *attrs[] = { NULL };
+
+       local_ctx = talloc_new(mem_ctx);
+       if (local_ctx == NULL) return NULL;
+       
+       while ((sdn = ldb_dn_get_parent(local_ctx, sdn))) {
+               ret = ldb_search(ldb, sdn, LDB_SCOPE_BASE, 
+                                "(|(objectClass=domain)(objectClass=builtinDomain))", attrs, &res);
+               if (ret == LDB_SUCCESS) {
+                       talloc_steal(local_ctx, res);
+                       if (res->count == 1) {
+                               break;
+                       }
+               }
+       }
+
+       if (ret != LDB_SUCCESS || res->count != 1) {
+               talloc_free(local_ctx);
+               return NULL;
+       }
+
+       talloc_steal(mem_ctx, sdn);
+       talloc_free(local_ctx);
+
+       return sdn;
+}
+
 /*
   check that a password is sufficiently complex
 */
@@ -1681,3 +1712,43 @@ NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TA
        *ret_dn = msg->dn;
        return NT_STATUS_OK;
 }
+
+/*
+  Find the DN of a domain, be it the netbios or DNS name 
+*/
+
+struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, 
+                                 const char *domain_name) 
+{
+       const char * const domain_ref_attrs[] = {
+               "ncName", NULL
+       };
+       struct ldb_result *res_domain_ref;
+       char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name);
+       /* find the domain's DN */
+       int ret_domain = ldb_search_exp_fmt(ldb, mem_ctx, 
+                                           &res_domain_ref, 
+                                           samdb_partitions_dn(ldb, mem_ctx), 
+                                           LDB_SCOPE_ONELEVEL, 
+                                           domain_ref_attrs,
+                                           "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))", 
+                                           escaped_domain, escaped_domain);
+       if (ret_domain != 0) {
+               return NULL;
+       }
+       
+       if (res_domain_ref->count == 0) {
+               DEBUG(3,("sam_search_user: Couldn't find domain [%s] in samdb.\n", 
+                        domain_name));
+               return NULL;
+       }
+       
+       if (res_domain_ref->count > 1) {
+               DEBUG(0,("Found %d records matching domain [%s]\n", 
+                        ret_domain, domain_name));
+               return NULL;
+       }
+       
+       return samdb_result_dn(ldb, mem_ctx, res_domain_ref->msgs[0], "nCName", NULL);
+
+}
index 119e0b1b3ea0a8a2b9d7e881a963cc5d9f4f942b..fe0e98fe0ddba7b699b9ebd60349dcdbeebb82a6 100644 (file)
@@ -1093,9 +1093,10 @@ interface drsuapi
 
        WERROR drsuapi_DsGetDomainControllerInfo(
                [in] policy_handle *bind_handle,
-               [in, out] int32 level,
+               [in] int32 level,
                [in,switch_is(level)] drsuapi_DsGetDCInfoRequest req,
-               [out,switch_is(level)] drsuapi_DsGetDCInfoCtr ctr
+               [out] int32 level_out,
+               [out,switch_is(level_out)] drsuapi_DsGetDCInfoCtr ctr
                );
 
        /*****************/
index 1773c6162937cdbd9e8f9a17a65a57dc01f9c435..dd26145522dddf16ab41718bfab9890e538c030f 100644 (file)
@@ -345,6 +345,268 @@ static WERROR DRSUAPI_REMOVE_DS_DOMAIN(struct dcesrv_call_state *dce_call, TALLO
        DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
 }
 
+/* Obtain the site name from a server DN */
+const char *result_site_name(struct ldb_dn *site_dn)
+{
+       /* Format is cn=<NETBIOS name>,cn=Servers,cn=<site>,cn=sites.... */
+       const struct ldb_val *val = ldb_dn_get_component_val(site_dn, 2);
+       const char *name = ldb_dn_get_component_name(site_dn, 2);
+
+       if (!name || (ldb_attr_cmp(name, "cn") != 0)) {
+               /* Ensure this matches the format.  This gives us a
+                * bit more confidence that a 'cn' value will be a
+                * ascii string */
+               return NULL;
+       }
+       if (val) {
+               return (char *)val->data;
+       }
+       return NULL;
+}
+
+/* 
+  drsuapi_DsGetDomainControllerInfo 
+*/
+static WERROR drsuapi_DsGetDomainControllerInfo_1(struct drsuapi_bind_state *b_state, 
+                                               TALLOC_CTX *mem_ctx,
+                                               struct drsuapi_DsGetDomainControllerInfo *r)
+{
+       struct ldb_dn *sites_dn;
+       struct ldb_result *res;
+
+       const char *attrs_account_01[] = { "samAccountName", NULL };
+       const char *attrs_account_1[] = { "cn", "dnsHostName", NULL };
+       const char *attrs_account_2[] = { "cn", "dnsHostName", "objectGUID", NULL };
+
+       const char *attrs_none[] = { NULL };
+
+       const char *attrs_site[] = { "objectGUID", NULL };
+
+       const char *attrs_ntds[] = { "options", "objectGUID", NULL };
+
+       const char *attrs_01[] = { "serverReference", NULL };
+       const char *attrs_1[] = { "serverReference", "cn", "dnsHostName", NULL };
+       const char *attrs_2[] = { "serverReference", "cn", "dnsHostName", "objectGUID", NULL };
+       const char **attrs;
+
+       struct drsuapi_DsGetDCInfoCtr01 *ctr01;
+       struct drsuapi_DsGetDCInfoCtr1 *ctr1;
+       struct drsuapi_DsGetDCInfoCtr2 *ctr2;
+
+       int ret, i;
+
+       r->out.level_out = r->in.req.req1.level;
+
+       sites_dn = samdb_domain_to_dn(b_state->sam_ctx, mem_ctx, r->in.req.req1.domain_name);
+       if (!sites_dn) {
+               return WERR_DS_OBJ_NOT_FOUND;
+       }
+
+       if (!ldb_dn_add_child_fmt(sites_dn, "CN=Sites,CN=Configuration")) {
+               return WERR_NOMEM;
+       }
+
+       switch (r->out.level_out) {
+       case -1:
+               attrs = attrs_01;
+               break;
+       case 1:
+               attrs = attrs_1;
+               break;
+       case 2:
+               attrs = attrs_2;
+               break;
+       default:
+               return WERR_UNKNOWN_LEVEL;
+       }
+
+       ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res, sites_dn, LDB_SCOPE_SUBTREE, attrs, 
+                                "objectClass=server");
+       
+       if (ret) {
+               return WERR_GENERAL_FAILURE;
+       }
+
+       switch (r->out.level_out) {
+       case -1:
+               ctr01 = &r->out.ctr.ctr01;
+               ctr01->count = res->count;
+               ctr01->array = talloc_zero_array(mem_ctx, 
+                                                struct drsuapi_DsGetDCInfo01, 
+                                                res->count);
+               for (i=0; i < res->count; i++) {
+                       struct ldb_result *res_account;
+                       struct ldb_dn *ref_dn
+                               = ldb_msg_find_attr_as_dn(b_state->sam_ctx, 
+                                                         mem_ctx, res->msgs[i], 
+                                                         "serverReference");
+                       ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_account, ref_dn, 
+                                                LDB_SCOPE_BASE, attrs_account_01, "objectClass=computer");
+                       if (ret) {
+                               return WERR_GENERAL_FAILURE;
+                       }
+                       if (res_account->count == 1) {
+                               ctr01->array[i].server_nt4_account
+                                       = ldb_msg_find_attr_as_string(res_account->msgs[0], "samAccountName", NULL);
+                       }
+               }
+               break;
+       case 1:
+               ctr1 = &r->out.ctr.ctr1;
+               ctr1->count = res->count;
+               ctr1->array = talloc_zero_array(mem_ctx, 
+                                               struct drsuapi_DsGetDCInfo1, 
+                                               res->count);
+               for (i=0; i < res->count; i++) {
+                       struct ldb_dn *domain_dn;
+                       struct ldb_result *res_domain;
+                       struct ldb_result *res_account;
+                       struct ldb_dn *ntds_dn = ldb_dn_copy(b_state->sam_ctx, res->msgs[i]->dn);
+                       
+                       struct ldb_dn *ref_dn
+                               = ldb_msg_find_attr_as_dn(b_state->sam_ctx, 
+                                                         mem_ctx, res->msgs[i], 
+                                                         "serverReference");
+
+                       if (!ntds_dn || !ldb_dn_add_child_fmt(ntds_dn, "CN=NTDS Settings")) {
+                               return WERR_NOMEM;
+                       }
+
+                       ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_account, ref_dn, 
+                                                LDB_SCOPE_BASE, attrs_account_1, "objectClass=computer");
+                       if (ret) {
+                               return WERR_GENERAL_FAILURE;
+                       }
+                       if (res_account->count == 1) {
+                               ctr1->array[i].dns_name
+                                       = ldb_msg_find_attr_as_string(res_account->msgs[0], "dNSHostName", NULL);
+                               ctr1->array[i].netbios_name
+                                       = ldb_msg_find_attr_as_string(res_account->msgs[0], "cn", NULL);
+                               ctr1->array[i].computer_dn
+                                       = ldb_dn_get_linearized(res_account->msgs[0]->dn);
+
+                               /* Determine if this is the PDC */
+                               domain_dn = samdb_search_for_parent_domain(b_state->sam_ctx, 
+                                                                          mem_ctx, res_account->msgs[0]->dn);
+                               
+                               if (domain_dn) {
+                                       ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_domain, domain_dn, 
+                                                                LDB_SCOPE_BASE, attrs_none, "fSMORoleOwner=%s",
+                                                                ldb_dn_get_linearized(ntds_dn));
+                                       if (ret) {
+                                               return WERR_GENERAL_FAILURE;
+                                       }
+                                       if (res_domain->count == 1) {
+                                               ctr1->array[i].is_pdc = True;
+                                       }
+                               }
+                       }
+
+                       /* Look at server DN and extract site component */
+                       ctr1->array[i].site_name = result_site_name(res->msgs[i]->dn);
+                       ctr1->array[i].server_dn = ldb_dn_get_linearized(res->msgs[i]->dn);
+
+
+                       ctr1->array[i].is_enabled = True;
+
+               }
+               break;
+       case 2:
+               ctr2 = &r->out.ctr.ctr2;
+               ctr2->count = res->count;
+               ctr2->array = talloc_zero_array(mem_ctx, 
+                                                struct drsuapi_DsGetDCInfo2, 
+                                                res->count);
+               for (i=0; i < res->count; i++) {
+                       struct ldb_dn *domain_dn;
+                       struct ldb_result *res_domain;
+                       struct ldb_result *res_account;
+                       struct ldb_dn *ntds_dn = ldb_dn_copy(b_state->sam_ctx, res->msgs[i]->dn);
+                       struct ldb_result *res_ntds;
+                       struct ldb_dn *site_dn = ldb_dn_copy(b_state->sam_ctx, res->msgs[i]->dn);
+                       struct ldb_result *res_site;
+                       struct ldb_dn *ref_dn
+                               = ldb_msg_find_attr_as_dn(b_state->sam_ctx, 
+                                                         mem_ctx, res->msgs[i], 
+                                                         "serverReference");
+
+                       if (!ntds_dn || !ldb_dn_add_child_fmt(ntds_dn, "CN=NTDS Settings")) {
+                               return WERR_NOMEM;
+                       }
+
+                       /* Format is cn=<NETBIOS name>,cn=Servers,cn=<site>,cn=sites.... */
+                       if (!site_dn || !ldb_dn_remove_child_components(site_dn, 2)) {
+                               return WERR_NOMEM;
+                       }
+
+                       ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_ntds, ntds_dn, 
+                                                LDB_SCOPE_BASE, attrs_ntds, "objectClass=nTDSDSA");
+                       if (ret) {
+                               return WERR_GENERAL_FAILURE;
+                       }
+                       if (res_ntds->count == 1) {
+                               ctr2->array[i].is_gc
+                                       = (ldb_msg_find_attr_as_int(res_ntds->msgs[0], "options", 0) == 1);
+                               ctr2->array[i].ntds_guid 
+                                       = samdb_result_guid(res_ntds->msgs[0], "objectGUID");
+                               ctr2->array[i].ntds_dn = ldb_dn_get_linearized(res_ntds->msgs[0]->dn);
+                       }
+
+                       ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_site, site_dn, 
+                                                LDB_SCOPE_BASE, attrs_site, "objectClass=site");
+                       if (ret) {
+                               return WERR_GENERAL_FAILURE;
+                       }
+                       if (res_site->count == 1) {
+                               ctr2->array[i].site_guid 
+                                       = samdb_result_guid(res_site->msgs[0], "objectGUID");
+                               ctr2->array[i].site_dn = ldb_dn_get_linearized(res_site->msgs[0]->dn);
+                       }
+
+                       ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_account, ref_dn, 
+                                                LDB_SCOPE_BASE, attrs_account_2, "objectClass=computer");
+                       if (ret) {
+                               return WERR_GENERAL_FAILURE;
+                       }
+                       if (res_account->count == 1) {
+                               ctr2->array[i].dns_name
+                                       = ldb_msg_find_attr_as_string(res_account->msgs[0], "dNSHostName", NULL);
+                               ctr2->array[i].netbios_name
+                                       = ldb_msg_find_attr_as_string(res_account->msgs[0], "cn", NULL);
+                               ctr2->array[i].computer_dn = ldb_dn_get_linearized(res_account->msgs[0]->dn);
+                               ctr2->array[i].computer_guid 
+                                       = samdb_result_guid(res_account->msgs[0], "objectGUID");
+
+                               /* Determine if this is the PDC */
+                               domain_dn = samdb_search_for_parent_domain(b_state->sam_ctx, 
+                                                                          mem_ctx, res_account->msgs[0]->dn);
+                               
+                               if (domain_dn) {
+                                       ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_domain, domain_dn, 
+                                                                LDB_SCOPE_BASE, attrs_none, "fSMORoleOwner=%s",
+                                                                ldb_dn_get_linearized(ntds_dn));
+                                       if (ret) {
+                                               return WERR_GENERAL_FAILURE;
+                                       }
+                                       if (res_domain->count == 1) {
+                                               ctr2->array[i].is_pdc = True;
+                                       }
+                               }
+                       }
+
+                       /* Look at server DN and extract site component */
+                       ctr2->array[i].site_name = result_site_name(res->msgs[i]->dn);
+                       ctr2->array[i].server_dn = ldb_dn_get_linearized(res->msgs[i]->dn);
+                       ctr2->array[i].server_guid 
+                               = samdb_result_guid(res->msgs[i], "objectGUID");
+
+                       ctr2->array[i].is_enabled = True;
+
+               }
+               break;
+       }
+       return WERR_OK;
+}
 
 /* 
   drsuapi_DsGetDomainControllerInfo 
@@ -352,7 +614,17 @@ static WERROR DRSUAPI_REMOVE_DS_DOMAIN(struct dcesrv_call_state *dce_call, TALLO
 static WERROR drsuapi_DsGetDomainControllerInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
                                                struct drsuapi_DsGetDomainControllerInfo *r)
 {
-       DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+       struct dcesrv_handle *h;
+       struct drsuapi_bind_state *b_state;     
+       DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+       b_state = h->data;
+
+       switch (r->in.level) {
+       case 1:
+               return drsuapi_DsGetDomainControllerInfo_1(b_state, mem_ctx, r);
+       }
+
+       return WERR_UNKNOWN_LEVEL;
 }
 
 
index ef49b0443ee2e214a2c631d7c0a2becaf9900e14..d876b079dd379d71872d8097baf85d5b0f5d2f4a 100644 (file)
@@ -67,11 +67,6 @@ static BOOL test_DsGetDomainControllerInfo(struct dcerpc_pipe *p, TALLOC_CTX *me
        struct drsuapi_DsGetDomainControllerInfo r;
        BOOL ret = True;
 
-       if (lp_parm_bool(-1, "torture", "samba4", False)) {
-               printf("skipping DsGetDCInfo test against Samba4\n");
-               return True;
-       }
-
        r.in.bind_handle = &priv->bind_handle;
        r.in.level = 1;