/* work out the principal to use for DRS replication connections */ NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s, TALLOC_CTX *mem_ctx, const struct repsFromTo1 *rft, const char **target_principal) { TALLOC_CTX *tmp_ctx; struct ldb_result *res; const char *attrs[] = { "dNSHostName", NULL }; int ret; const char *hostname; struct ldb_dn *dn; *target_principal = NULL; tmp_ctx = talloc_new(mem_ctx); /* we need to find their hostname */ ret = dsdb_find_dn_by_guid(s->samdb, tmp_ctx, &rft->source_dsa_obj_guid, &dn); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); /* its OK for their NTDSDSA DN not to be in our database */ return NT_STATUS_OK; } /* strip off the NTDS Settings */ if (!ldb_dn_remove_child_components(dn, 1)) { talloc_free(tmp_ctx); return NT_STATUS_OK; } ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, dn, attrs, 0); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); /* its OK for their account DN not to be in our database */ return NT_STATUS_OK; } hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL); if (hostname == NULL) { talloc_free(tmp_ctx); /* its OK to not have a dnshostname */ return NT_STATUS_OK; } /* All DCs have the GC/hostname/realm name, but if some of the * preconditions are not satisfied, then we will fall back to * the * E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN} * name. This means that if a AD server has a dnsHostName set * on it's record, it must also have GC/hostname/realm * servicePrincipalName */ *target_principal = talloc_asprintf(mem_ctx, "GC/%s/%s", hostname, lpcfg_dnsdomain(s->task->lp_ctx)); talloc_free(tmp_ctx); return NT_STATUS_OK; }
/* get the stamp values for the linked attribute 'linked_attr_name' of the object 'dn' */ static WERROR get_linked_attribute_value_stamp(TALLOC_CTX *mem_ctx, struct ldb_context *samdb, struct ldb_dn *dn, const char *linked_attr_name, uint32_t *attr_version, NTTIME *attr_change_time, uint32_t *attr_orig_usn) { struct ldb_result *res; int ret; const char *attrs[2]; struct ldb_dn *attr_ext_dn; NTSTATUS ntstatus; attrs[0] = linked_attr_name; attrs[1] = NULL; ret = dsdb_search_dn(samdb, mem_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_REVEAL_INTERNALS); if (ret != LDB_SUCCESS) { DEBUG(0, (__location__ ": Failed search for attribute %s on %s", linked_attr_name, ldb_dn_get_linearized(dn))); return WERR_INTERNAL_ERROR; } attr_ext_dn = ldb_msg_find_attr_as_dn(samdb, mem_ctx, res->msgs[0], linked_attr_name); if (!attr_ext_dn) { DEBUG(0, (__location__ ": Failed search for attribute %s on %s", linked_attr_name, ldb_dn_get_linearized(dn))); return WERR_INTERNAL_ERROR; } DEBUG(0, ("linked_attr_name = %s, attr_ext_dn = %s", linked_attr_name, ldb_dn_get_extended_linearized(mem_ctx, attr_ext_dn, 1))); ntstatus = dsdb_get_extended_dn_uint32(attr_ext_dn, attr_version, "RMD_VERSION"); if (!NT_STATUS_IS_OK(ntstatus)) { DEBUG(0, (__location__ ": Could not extract component %s from dn \"%s\"", "RMD_VERSION", ldb_dn_get_extended_linearized(mem_ctx, attr_ext_dn, 1))); return WERR_INTERNAL_ERROR; } ntstatus = dsdb_get_extended_dn_nttime(attr_ext_dn, attr_change_time, "RMD_CHANGETIME"); if (!NT_STATUS_IS_OK(ntstatus)) { DEBUG(0, (__location__ ": Could not extract component %s from dn \"%s\"", "RMD_CHANGETIME", ldb_dn_get_extended_linearized(mem_ctx, attr_ext_dn, 1))); return WERR_INTERNAL_ERROR; } ntstatus = dsdb_get_extended_dn_uint32(attr_ext_dn, attr_version, "RMD_ORIGINATING_USN"); if (!NT_STATUS_IS_OK(ntstatus)) { DEBUG(0, (__location__ ": Could not extract component %s from dn \"%s\"", "RMD_ORIGINATING_USN", ldb_dn_get_extended_linearized(mem_ctx, attr_ext_dn, 1))); return WERR_INTERNAL_ERROR; } return WERR_OK; }
/* return the default DN for a ldap server given a connected RPC pipe to the server */ static const char *torture_get_ldap_base_dn(struct torture_context *tctx, struct dcerpc_pipe *p) { const char *hostname = p->binding->host; struct ldb_context *ldb; const char *ldap_url = talloc_asprintf(p, "ldap://%s", hostname); const char *attrs[] = { "defaultNamingContext", NULL }; const char *dnstr; TALLOC_CTX *tmp_ctx = talloc_new(tctx); int ret; struct ldb_result *res; ldb = ldb_init(tmp_ctx, tctx->ev); if (ldb == NULL) { talloc_free(tmp_ctx); return NULL; } if (ldb_set_opaque(ldb, "loadparm", tctx->lp_ctx)) { talloc_free(ldb); return NULL; } ldb_set_modules_dir(ldb, modules_path(ldb, "ldb")); ret = ldb_connect(ldb, ldap_url, 0, NULL); if (ret != LDB_SUCCESS) { torture_comment(tctx, "Failed to make LDB connection to target"); talloc_free(tmp_ctx); return NULL; } ret = dsdb_search_dn(ldb, tmp_ctx, &res, ldb_dn_new(tmp_ctx, ldb, ""), attrs, 0); if (ret != LDB_SUCCESS) { torture_comment(tctx, "Failed to get defaultNamingContext"); talloc_free(tmp_ctx); return NULL; } dnstr = ldb_msg_find_attr_as_string(res->msgs[0], "defaultNamingContext", NULL); dnstr = talloc_strdup(tctx, dnstr); talloc_free(tmp_ctx); return dnstr; }
int dsdb_check_access_on_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct security_token *token, uint32_t access_mask, const char *ext_right) { int ret; struct GUID guid; struct ldb_result *acl_res; static const char *acl_attrs[] = { "nTSecurityDescriptor", "objectSid", NULL }; if (ext_right != NULL) { NTSTATUS status = GUID_from_string(ext_right, &guid); if (!NT_STATUS_IS_OK(status)) { return LDB_ERR_OPERATIONS_ERROR; } } /* * We need AS_SYSTEM in order to get the nTSecurityDescriptor attribute. * Also the result of this search not controlled by the client * nor is the result exposed to the client. */ ret = dsdb_search_dn(ldb, mem_ctx, &acl_res, dn, acl_attrs, DSDB_FLAG_AS_SYSTEM | DSDB_SEARCH_SHOW_RECYCLED); if (ret != LDB_SUCCESS) { DEBUG(10,("access_check: failed to find object %s\n", ldb_dn_get_linearized(dn))); return ret; } return dsdb_check_access_on_dn_internal(ldb, acl_res, mem_ctx, token, dn, access_mask, ext_right ? &guid : NULL); }
/* Check if particular SPN exists for an account */ static bool dreplsrv_spn_exists(struct ldb_context *samdb, struct ldb_dn *ntds_dn, const char *principal_name) { TALLOC_CTX *tmp_ctx; const char *attrs[] = { "serverReference", NULL }; const char *attrs_empty[] = { NULL }; int ret; struct ldb_result *res; struct ldb_dn *account_dn; tmp_ctx = talloc_new(samdb); ret = dsdb_search_dn(samdb, tmp_ctx, &res, ntds_dn, attrs, 0); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return false; } account_dn = ldb_msg_find_attr_as_dn(samdb, tmp_ctx, res->msgs[0], "serverReference"); if (account_dn == NULL) { talloc_free(tmp_ctx); return false; } talloc_free(res); ret = dsdb_search(samdb, tmp_ctx, &res, account_dn, LDB_SCOPE_BASE, attrs_empty, 0, "servicePrincipalName=%s", ldb_binary_encode_string(tmp_ctx, principal_name)); if (ret != LDB_SUCCESS || res->count != 1) { talloc_free(tmp_ctx); return false; } talloc_free(tmp_ctx); return true; }
/* load the partitions list based on replicated NC attributes in our NTDSDSA object */ WERROR dreplsrv_load_partitions(struct dreplsrv_service *s) { WERROR status; static const char *attrs[] = { "hasMasterNCs", "msDs-hasMasterNCs", "hasPartialReplicaNCs", "msDS-HasFullReplicaNCs", NULL }; unsigned int a; int ret; TALLOC_CTX *tmp_ctx; struct ldb_result *res; struct ldb_message_element *el; struct ldb_dn *ntds_dn; tmp_ctx = talloc_new(s); W_ERROR_HAVE_NO_MEMORY(tmp_ctx); ntds_dn = samdb_ntds_settings_dn(s->samdb); if (!ntds_dn) { DEBUG(1,(__location__ ": Unable to find ntds_dn: %s\n", ldb_errstring(s->samdb))); talloc_free(tmp_ctx); return WERR_DS_DRA_INTERNAL_ERROR; } ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, ntds_dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN); if (ret != LDB_SUCCESS) { DEBUG(1,("Searching for hasMasterNCs in NTDS DN failed: %s\n", ldb_errstring(s->samdb))); talloc_free(tmp_ctx); return WERR_DS_DRA_INTERNAL_ERROR; } for (a=0; attrs[a]; a++) { int i; el = ldb_msg_find_element(res->msgs[0], attrs[a]); if (el == NULL) { continue; } for (i=0; i<el->num_values; i++) { struct ldb_dn *pdn; struct dreplsrv_partition *p, *tp; bool found; pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]); if (pdn == NULL) { talloc_free(tmp_ctx); return WERR_DS_DRA_INTERNAL_ERROR; } if (!ldb_dn_validate(pdn)) { return WERR_DS_DRA_INTERNAL_ERROR; } p = talloc_zero(s, struct dreplsrv_partition); W_ERROR_HAVE_NO_MEMORY(p); p->dn = talloc_steal(p, pdn); p->service = s; if (strcasecmp(attrs[a], "hasPartialReplicaNCs") == 0) { p->partial_replica = true; } else if (strcasecmp(attrs[a], "msDS-HasFullReplicaNCs") == 0) { p->rodc_replica = true; } /* Do not add partitions more than once */ found = false; for (tp = s->partitions; tp; tp = tp->next) { if (ldb_dn_compare(tp->dn, p->dn) == 0) { found = true; break; } } if (found) { talloc_free(p); continue; } DLIST_ADD(s->partitions, p); DEBUG(2, ("dreplsrv_partition[%s] loaded\n", ldb_dn_get_linearized(p->dn))); } } talloc_free(tmp_ctx); status = dreplsrv_refresh_partitions(s); W_ERROR_NOT_OK_RETURN(status); return WERR_OK; }
/* work out the principal to use for DRS replication connections */ NTSTATUS dreplsrv_get_target_principal(struct dreplsrv_service *s, TALLOC_CTX *mem_ctx, const struct repsFromTo1 *rft, const char **target_principal) { TALLOC_CTX *tmp_ctx; struct ldb_result *res; const char *attrs_server[] = { "dNSHostName", NULL }; const char *attrs_ntds[] = { "msDS-HasDomainNCs", "hasMasterNCs", NULL }; int ret; const char *hostname, *dnsdomain=NULL; struct ldb_dn *ntds_dn, *server_dn; struct ldb_dn *forest_dn, *nc_dn; *target_principal = NULL; tmp_ctx = talloc_new(mem_ctx); /* we need to find their hostname */ ret = dsdb_find_dn_by_guid(s->samdb, tmp_ctx, &rft->source_dsa_obj_guid, &ntds_dn); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); /* its OK for their NTDSDSA DN not to be in our database */ return NT_STATUS_OK; } server_dn = ldb_dn_copy(tmp_ctx, ntds_dn); if (server_dn == NULL) { talloc_free(tmp_ctx); return NT_STATUS_OK; } /* strip off the NTDS Settings */ if (!ldb_dn_remove_child_components(server_dn, 1)) { talloc_free(tmp_ctx); return NT_STATUS_OK; } ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, server_dn, attrs_server, 0); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); /* its OK for their server DN not to be in our database */ return NT_STATUS_OK; } forest_dn = ldb_get_root_basedn(s->samdb); if (forest_dn == NULL) { talloc_free(tmp_ctx); return NT_STATUS_OK; } hostname = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL); if (hostname != NULL) { char *local_principal; /* if we have the dNSHostName attribute then we can use the GC/hostname/realm SPN. All DCs should have this SPN Windows DC may set up it's dNSHostName before setting up GC/xx/xx SPN. So make sure it exists, before using it. */ local_principal = talloc_asprintf(mem_ctx, "GC/%s/%s", hostname, samdb_dn_to_dns_domain(tmp_ctx, forest_dn)); if (dreplsrv_spn_exists(s->samdb, ntds_dn, local_principal)) { *target_principal = local_principal; talloc_free(tmp_ctx); return NT_STATUS_OK; } talloc_free(local_principal); } /* if we can't find the dNSHostName then we will try for the E3514235-4B06-11D1-AB04-00C04FC2DCD2/${NTDSGUID}/${DNSDOMAIN} SPN. To use that we need the DNS domain name of the target DC. We find that by first looking for the msDS-HasDomainNCs in the NTDSDSA object of the DC, and if we don't find that, then we look for the hasMasterNCs attribute, and eliminate the known schema and configuruation DNs. Despite how bizarre this seems, Hongwei tells us that this is in fact what windows does to find the SPN!! */ ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, ntds_dn, attrs_ntds, 0); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return NT_STATUS_OK; } nc_dn = ldb_msg_find_attr_as_dn(s->samdb, tmp_ctx, res->msgs[0], "msDS-HasDomainNCs"); if (nc_dn != NULL) { dnsdomain = samdb_dn_to_dns_domain(tmp_ctx, nc_dn); } if (dnsdomain == NULL) { struct ldb_message_element *el; int i; el = ldb_msg_find_element(res->msgs[0], "hasMasterNCs"); for (i=0; el && i<el->num_values; i++) { nc_dn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]); if (nc_dn == NULL || ldb_dn_compare(ldb_get_config_basedn(s->samdb), nc_dn) == 0 || ldb_dn_compare(ldb_get_schema_basedn(s->samdb), nc_dn) == 0) { continue; } /* it must be a domain DN, get the equivalent DNS domain name */ dnsdomain = samdb_dn_to_dns_domain(tmp_ctx, nc_dn); break; } } if (dnsdomain != NULL) { *target_principal = talloc_asprintf(mem_ctx, "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s", GUID_string(tmp_ctx, &rft->source_dsa_obj_guid), dnsdomain); } talloc_free(tmp_ctx); return NT_STATUS_OK; }
static int ldb_eval_transitive_filter_helper(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const char *attr, const struct dsdb_dn *dn_to_match, const char *dn_oid, struct dsdb_dn *to_visit, struct dsdb_dn ***visited, unsigned int *visited_count, bool *matched) { TALLOC_CTX *tmp_ctx; int ret, i, j; struct ldb_result *res; struct ldb_message *msg; struct ldb_message_element *el; const char *attrs[] = { attr, NULL }; tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { return LDB_ERR_OPERATIONS_ERROR; } /* * Fetch the entry to_visit * * NOTE: This is a new LDB search from the TOP of the module * stack. This means that this search runs the whole stack * from top to bottom. * * This may seem to be in-efficient, but it is also the only * way to ensure that the ACLs for this search are applied * correctly. * * Note also that we don't have the original request * here, so we can not apply controls or timeouts here. */ ret = dsdb_search_dn(ldb, tmp_ctx, &res, to_visit->dn, attrs, 0); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; } if (res->count != 1) { talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } msg = res->msgs[0]; /* Fetch the attribute to match from the entry being visited */ el = ldb_msg_find_element(msg, attr); if (el == NULL) { /* This entry does not have the attribute to match */ talloc_free(tmp_ctx); *matched = false; return LDB_SUCCESS; } /* * If the value to match is present in the attribute values of the * current entry being visited, set matched to true and return OK */ for (i=0; i<el->num_values; i++) { struct dsdb_dn *dn; dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid); if (dn == NULL) { talloc_free(tmp_ctx); *matched = false; return LDB_ERR_INVALID_DN_SYNTAX; } if (ldb_dn_compare(dn_to_match->dn, dn->dn) == 0) { talloc_free(tmp_ctx); *matched = true; return LDB_SUCCESS; } } /* * If arrived here, the value to match is not in the values of the * entry being visited. Add the entry being visited (to_visit) * to the visited array. The array is (re)allocated in the parent * memory context. */ if (visited == NULL) { return LDB_ERR_OPERATIONS_ERROR; } else if (*visited == NULL) { *visited = talloc_array(mem_ctx, struct dsdb_dn *, 1); if (*visited == NULL) { talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } (*visited)[0] = to_visit; (*visited_count) = 1; } else {
/* * This function generates the transitive closure of a given SAM object "dn_val" * (it basically expands nested memberships). * If the object isn't located in the "res_sids" structure yet and the * "only_childs" flag is false, we add it to "res_sids". * Then we've always to consider the "memberOf" attributes. We invoke the * function recursively on each of it with the "only_childs" flag set to * "false". * The "only_childs" flag is particularly useful if you have a user object and * want to include all it's groups (referenced with "memberOf") but not itself * or considering if that object matches the filter. * * At the beginning "res_sids" should reference to a NULL pointer. */ NTSTATUS dsdb_expand_nested_groups(struct ldb_context *sam_ctx, struct ldb_val *dn_val, const bool only_childs, const char *filter, TALLOC_CTX *res_sids_ctx, struct dom_sid **res_sids, unsigned int *num_res_sids) { const char * const attrs[] = { "memberOf", NULL }; unsigned int i; int ret; bool already_there; struct ldb_dn *dn; struct dom_sid sid; TALLOC_CTX *tmp_ctx; struct ldb_result *res; NTSTATUS status; const struct ldb_message_element *el; if (*res_sids == NULL) { *num_res_sids = 0; } if (!sam_ctx) { DEBUG(0, ("No SAM available, cannot determine local groups\n")); return NT_STATUS_INVALID_SYSTEM_SERVICE; } tmp_ctx = talloc_new(res_sids_ctx); dn = ldb_dn_from_ldb_val(tmp_ctx, sam_ctx, dn_val); if (dn == NULL) { talloc_free(tmp_ctx); DEBUG(0, (__location__ ": we failed parsing DN %.*s, so we cannot calculate the group token\n", (int)dn_val->length, dn_val->data)); return NT_STATUS_INTERNAL_DB_CORRUPTION; } status = dsdb_get_extended_dn_sid(dn, &sid, "SID"); if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { /* If we fail finding a SID then this is no error since it could * be a non SAM object - e.g. a group with object class * "groupOfNames" */ talloc_free(tmp_ctx); return NT_STATUS_OK; } else if (!NT_STATUS_IS_OK(status)) { DEBUG(0, (__location__ ": when parsing DN '%s' we failed to parse it's SID component, so we cannot calculate the group token: %s\n", ldb_dn_get_extended_linearized(tmp_ctx, dn, 1), nt_errstr(status))); talloc_free(tmp_ctx); return status; } if (!ldb_dn_minimise(dn)) { talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } if (only_childs) { ret = dsdb_search_dn(sam_ctx, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN); } else { /* This is an O(n^2) linear search */ already_there = sids_contains_sid(*res_sids, *num_res_sids, &sid); if (already_there) { talloc_free(tmp_ctx); return NT_STATUS_OK; } ret = dsdb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, "%s", filter); } if (ret == LDB_ERR_NO_SUCH_OBJECT) { talloc_free(tmp_ctx); return NT_STATUS_OK; } if (ret != LDB_SUCCESS) { DEBUG(1, (__location__ ": dsdb_search for %s failed: %s\n", ldb_dn_get_extended_linearized(tmp_ctx, dn, 1), ldb_errstring(sam_ctx))); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } /* We may get back 0 results, if the SID didn't match the filter - such as it wasn't a domain group, for example */ if (res->count != 1) { talloc_free(tmp_ctx); return NT_STATUS_OK; } /* We only apply this test once we know the SID matches the filter */ if (!only_childs) { *res_sids = talloc_realloc(res_sids_ctx, *res_sids, struct dom_sid, *num_res_sids + 1); NT_STATUS_HAVE_NO_MEMORY_AND_FREE(*res_sids, tmp_ctx); (*res_sids)[*num_res_sids] = sid; ++(*num_res_sids); }
WERROR dreplsrv_load_partitions(struct dreplsrv_service *s) { WERROR status; static const char *attrs[] = { "hasMasterNCs", "hasPartialReplicaNCs", NULL }; unsigned int i; int ret; TALLOC_CTX *tmp_ctx; struct ldb_result *res; struct ldb_message_element *el; struct ldb_dn *ntds_dn; tmp_ctx = talloc_new(s); W_ERROR_HAVE_NO_MEMORY(tmp_ctx); ntds_dn = samdb_ntds_settings_dn(s->samdb); if (!ntds_dn) { DEBUG(1,(__location__ ": Unable to find ntds_dn: %s\n", ldb_errstring(s->samdb))); talloc_free(tmp_ctx); return WERR_DS_DRA_INTERNAL_ERROR; } ret = dsdb_search_dn(s->samdb, tmp_ctx, &res, ntds_dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN); if (ret != LDB_SUCCESS) { DEBUG(1,("Searching for hasMasterNCs in NTDS DN failed: %s\n", ldb_errstring(s->samdb))); talloc_free(tmp_ctx); return WERR_DS_DRA_INTERNAL_ERROR; } el = ldb_msg_find_element(res->msgs[0], "hasMasterNCs"); if (!el) { DEBUG(1,("Finding hasMasterNCs element in root_res failed: %s\n", ldb_errstring(s->samdb))); talloc_free(tmp_ctx); return WERR_DS_DRA_INTERNAL_ERROR; } for (i=0; i<el->num_values; i++) { struct ldb_dn *pdn; struct dreplsrv_partition *p; pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]); if (pdn == NULL) { talloc_free(tmp_ctx); return WERR_DS_DRA_INTERNAL_ERROR; } if (!ldb_dn_validate(pdn)) { return WERR_DS_DRA_INTERNAL_ERROR; } p = talloc_zero(s, struct dreplsrv_partition); W_ERROR_HAVE_NO_MEMORY(p); p->dn = talloc_steal(p, pdn); p->service = s; DLIST_ADD(s->partitions, p); DEBUG(2, ("dreplsrv_partition[%s] loaded\n", ldb_dn_get_linearized(p->dn))); } el = ldb_msg_find_element(res->msgs[0], "hasPartialReplicaNCs"); for (i=0; el && i<el->num_values; i++) { struct ldb_dn *pdn; struct dreplsrv_partition *p; pdn = ldb_dn_from_ldb_val(tmp_ctx, s->samdb, &el->values[i]); if (pdn == NULL) { talloc_free(tmp_ctx); return WERR_DS_DRA_INTERNAL_ERROR; } if (!ldb_dn_validate(pdn)) { return WERR_DS_DRA_INTERNAL_ERROR; } p = talloc_zero(s, struct dreplsrv_partition); W_ERROR_HAVE_NO_MEMORY(p); p->dn = talloc_steal(p, pdn); p->partial_replica = true; p->service = s; DLIST_ADD(s->partitions, p); DEBUG(2, ("dreplsrv_partition[%s] loaded (partial replica)\n", ldb_dn_get_linearized(p->dn))); } talloc_free(tmp_ctx); status = dreplsrv_refresh_partitions(s); W_ERROR_NOT_OK_RETURN(status); return WERR_OK; }
/* check that the SPN update should be allowed as an override via sam_ctx_system This is only called if the client is not a domain controller or administrator */ static bool writespn_check_spn(struct drsuapi_bind_state *b_state, struct dcesrv_call_state *dce_call, struct ldb_dn *dn, const char *spn) { /* * we only allow SPN updates if: * * 1) they are on the clients own account object * 2) they are of the form SERVICE/dnshostname */ struct dom_sid *user_sid, *sid; TALLOC_CTX *tmp_ctx = talloc_new(dce_call); struct ldb_result *res; const char *attrs[] = { "objectSID", "dNSHostName", NULL }; int ret; krb5_context krb_ctx; krb5_error_code kerr; krb5_principal principal; const krb5_data *component; const char *dns_name, *dnsHostName; /* The service principal name shouldn't be NULL */ if (spn == NULL) { talloc_free(tmp_ctx); return false; } /* get the objectSid of the DN that is being modified, and check it matches the user_sid in their token */ ret = dsdb_search_dn(b_state->sam_ctx, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_ONE_ONLY); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return false; } user_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; sid = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSid"); if (sid == NULL) { talloc_free(tmp_ctx); return false; } dnsHostName = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL); if (dnsHostName == NULL) { talloc_free(tmp_ctx); return false; } if (!dom_sid_equal(sid, user_sid)) { talloc_free(tmp_ctx); return false; } kerr = smb_krb5_init_context_basic(tmp_ctx, dce_call->conn->dce_ctx->lp_ctx, &krb_ctx); if (kerr != 0) { talloc_free(tmp_ctx); return false; } ret = krb5_parse_name_flags(krb_ctx, spn, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); if (kerr != 0) { krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } if (krb5_princ_size(krb_ctx, principal) != 2) { krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } component = krb5_princ_component(krb_ctx, principal, 1); dns_name = (const char *)component->data; if (strcasecmp(dns_name, dnsHostName) != 0) { krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } /* its a simple update on their own account - allow it with * permissions override */ krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return true; }