diff options
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/samldb.c | 37 | ||||
-rw-r--r-- | source4/dsdb/samdb/samdb.c | 89 | ||||
-rw-r--r-- | source4/librpc/idl/drsuapi.idl | 5 | ||||
-rw-r--r-- | source4/rpc_server/drsuapi/dcesrv_drsuapi.c | 274 | ||||
-rw-r--r-- | source4/torture/rpc/drsuapi.c | 5 |
5 files changed, 357 insertions, 53 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 26560c361e..1c1ff0ea6e 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -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", diff --git a/source4/dsdb/samdb/samdb.c b/source4/dsdb/samdb/samdb.c index 9296352b0b..2d811094ff 100644 --- a/source4/dsdb/samdb/samdb.c +++ b/source4/dsdb/samdb/samdb.c @@ -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); + +} diff --git a/source4/librpc/idl/drsuapi.idl b/source4/librpc/idl/drsuapi.idl index 119e0b1b3e..fe0e98fe0d 100644 --- a/source4/librpc/idl/drsuapi.idl +++ b/source4/librpc/idl/drsuapi.idl @@ -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 ); /*****************/ diff --git a/source4/rpc_server/drsuapi/dcesrv_drsuapi.c b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c index 1773c61629..dd26145522 100644 --- a/source4/rpc_server/drsuapi/dcesrv_drsuapi.c +++ b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c @@ -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; } diff --git a/source4/torture/rpc/drsuapi.c b/source4/torture/rpc/drsuapi.c index ef49b0443e..d876b079dd 100644 --- a/source4/torture/rpc/drsuapi.c +++ b/source4/torture/rpc/drsuapi.c @@ -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; |