X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Fdsdb%2Fcommon%2Futil.c;h=7c27b4adfb2f1e1557fdc4151b308c02033a1184;hb=1b1e2c7913097213a99ef74b90654e13b56775c4;hp=6147940e3b79905ce8f60c6a2f0f1ae9643ceef1;hpb=9b3871ed293f76e770e572cd6b59f59670f1f6f8;p=metze%2Fsamba%2Fwip.git diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c index 6147940e3b79..7c27b4adfb2f 100644 --- a/source4/dsdb/common/util.c +++ b/source4/dsdb/common/util.c @@ -38,6 +38,10 @@ #include "libcli/auth/libcli_auth.h" #include "librpc/gen_ndr/ndr_drsblobs.h" #include "system/locale.h" +#include "lib/util/tsort.h" +#include "dsdb/common/util.h" +#include "lib/socket/socket.h" +#include "dsdb/samdb/ldb_modules/util.h" /* search the sam for the specified attributes in a specific domain, filter on @@ -208,9 +212,9 @@ int samdb_search_count(struct ldb_context *sam_ldb, /* search the sam for a single integer attribute in exactly 1 record */ -uint_t samdb_search_uint(struct ldb_context *sam_ldb, +unsigned int samdb_search_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, - uint_t default_value, + unsigned int default_value, struct ldb_dn *basedn, const char *attr_name, const char *format, ...) _PRINTF_ATTRIBUTE(6,7) @@ -314,7 +318,7 @@ int samdb_search_string_multiple(struct ldb_context *sam_ldb, /* pull a uint from a result set. */ -uint_t samdb_result_uint(const struct ldb_message *msg, const char *attr, uint_t default_value) +unsigned int samdb_result_uint(const struct ldb_message *msg, const char *attr, unsigned int default_value) { return ldb_msg_find_attr_as_uint(msg, attr, default_value); } @@ -555,12 +559,12 @@ struct samr_Password *samdb_result_hash(TALLOC_CTX *mem_ctx, const struct ldb_me } /* - pull an array of samr_Password structutres from a result set. + pull an array of samr_Password structures from a result set. */ -uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, const struct ldb_message *msg, +unsigned int samdb_result_hashes(TALLOC_CTX *mem_ctx, const struct ldb_message *msg, const char *attr, struct samr_Password **hashes) { - uint_t count, i; + unsigned int count, i; const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr); *hashes = NULL; @@ -589,7 +593,7 @@ NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct loadparm_context *lp { struct samr_Password *lmPwdHash, *ntPwdHash; if (nt_pwd) { - int num_nt; + unsigned int num_nt; num_nt = samdb_result_hashes(mem_ctx, msg, "unicodePwd", &ntPwdHash); if (num_nt == 0) { *nt_pwd = NULL; @@ -604,7 +608,7 @@ NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct loadparm_context *lp * authentication, that we never use the LM hash, even * if we store it */ if (lp_lanman_auth(lp_ctx)) { - int num_lm; + unsigned int num_lm; num_lm = samdb_result_hashes(mem_ctx, msg, "dBCSPwd", &lmPwdHash); if (num_lm == 0) { *lm_pwd = NULL; @@ -686,7 +690,7 @@ struct lsa_BinaryString samdb_result_parameters(TALLOC_CTX *mem_ctx, if (!s.array) { return s; } - s.length = s.size = val->length/2; + s.length = s.size = val->length; memcpy(s.array, val->data, val->length); return s; @@ -702,7 +706,7 @@ struct ldb_message_element *samdb_find_attribute(struct ldb_context *ldb, const struct ldb_message *msg, const char *name, const char *value) { - int i; + unsigned int i; struct ldb_message_element *el = ldb_msg_find_element(msg, name); if (!el) { @@ -718,6 +722,47 @@ struct ldb_message_element *samdb_find_attribute(struct ldb_context *ldb, return NULL; } +/* + * This is intended for use by the "password hash" module since there + * password changes can be specified through one message element with the + * new password (to set) and another one with the old password (to unset). + * + * The first which sets a password (new value) can have flags + * (LDB_FLAG_MOD_ADD, LDB_FLAG_MOD_REPLACE) but also none (on "add" operations + * for entries). The latter (old value) has always specified + * LDB_FLAG_MOD_DELETE. + * + * Returns LDB_ERR_NO_SUCH_ATTRIBUTE if the attribute which should be deleted + * doesn't contain only one value (this is the Windows Server behaviour) + * otherwise LDB_SUCCESS. + */ +int samdb_msg_find_old_and_new_ldb_val(const struct ldb_message *msg, + const char *name, + const struct ldb_val **new_val, + const struct ldb_val **old_val) +{ + unsigned int i; + + *new_val = NULL; + *old_val = NULL; + + if (msg == NULL) { + return LDB_SUCCESS; + } + + for (i = 0; i < msg->num_elements; i++) { + if (ldb_attr_cmp(msg->elements[i].name, name) == 0) { + if (msg->elements[i].flags == LDB_FLAG_MOD_DELETE) { + *old_val = &msg->elements[i].values[0]; + } else { + *new_val = &msg->elements[i].values[0]; + } + } + } + + return LDB_SUCCESS; +} + int samdb_find_or_add_value(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value) { if (samdb_find_attribute(ldb, msg, name, set_value) == NULL) { @@ -768,7 +813,7 @@ int samdb_msg_add_dom_sid(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, stru sid, (ndr_push_flags_fn_t)ndr_push_dom_sid); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - return -1; + return LDB_ERR_OPERATIONS_ERROR; } return ldb_msg_add_value(msg, attr_name, &v, NULL); } @@ -781,7 +826,7 @@ int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struc const char *attr_name) { /* we use an empty replace rather than a delete, as it allows for - samdb_replace() to be used everywhere */ + dsdb_replace() to be used everywhere */ return ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE, NULL); } @@ -796,18 +841,18 @@ int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struc int ret; a = talloc_strdup(mem_ctx, attr_name); if (a == NULL) - return -1; + return LDB_ERR_OPERATIONS_ERROR; v = talloc_strdup(mem_ctx, value); if (v == NULL) - return -1; + return LDB_ERR_OPERATIONS_ERROR; ret = ldb_msg_add_string(msg, a, v); if (ret != 0) return ret; el = ldb_msg_find_element(msg, a); if (el == NULL) - return -1; + return LDB_ERR_OPERATIONS_ERROR; el->flags = LDB_FLAG_MOD_ADD; - return 0; + return LDB_SUCCESS; } /* @@ -821,18 +866,18 @@ int samdb_msg_add_delval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struc int ret; a = talloc_strdup(mem_ctx, attr_name); if (a == NULL) - return -1; + return LDB_ERR_OPERATIONS_ERROR; v = talloc_strdup(mem_ctx, value); if (v == NULL) - return -1; + return LDB_ERR_OPERATIONS_ERROR; ret = ldb_msg_add_string(msg, a, v); if (ret != 0) return ret; el = ldb_msg_find_element(msg, a); if (el == NULL) - return -1; + return LDB_ERR_OPERATIONS_ERROR; el->flags = LDB_FLAG_MOD_DELETE; - return 0; + return LDB_SUCCESS; } /* @@ -846,13 +891,12 @@ int samdb_msg_add_int(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct l } /* - add a uint_t element to a message + add a unsigned int element to a message */ int samdb_msg_add_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg, - const char *attr_name, uint_t v) + const char *attr_name, unsigned int v) { - const char *s = talloc_asprintf(mem_ctx, "%u", v); - return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s); + return samdb_msg_add_int(sam_ldb, mem_ctx, msg, attr_name, (int)v); } /* @@ -871,8 +915,7 @@ int samdb_msg_add_int64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr_name, uint64_t v) { - const char *s = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)v); - return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s); + return samdb_msg_add_int64(sam_ldb, mem_ctx, msg, attr_name, (int64_t)v); } /* @@ -884,7 +927,7 @@ int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct struct ldb_val val; val.data = talloc_memdup(mem_ctx, hash->hash, 16); if (!val.data) { - return -1; + return LDB_ERR_OPERATIONS_ERROR; } val.length = 16; return ldb_msg_add_value(msg, attr_name, &val, NULL); @@ -894,14 +937,15 @@ int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct add a samr_Password array to a message */ int samdb_msg_add_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg, - const char *attr_name, struct samr_Password *hashes, uint_t count) + const char *attr_name, struct samr_Password *hashes, + unsigned int count) { struct ldb_val val; - int i; + unsigned int i; val.data = talloc_array_size(mem_ctx, 16, count); val.length = count*16; if (!val.data) { - return -1; + return LDB_ERR_OPERATIONS_ERROR; } for (i=0;ilength * 2; + val.length = parameters->length; val.data = (uint8_t *)parameters->array; return ldb_msg_add_value(msg, attr_name, &val, NULL); } @@ -980,27 +1024,11 @@ int samdb_msg_set_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struc return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str); } -/* - replace elements in a record -*/ -int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg) -{ - int i; - - /* mark all the message elements as LDB_FLAG_MOD_REPLACE */ - for (i=0;inum_elements;i++) { - msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; - } - - /* modify the samdb record */ - return ldb_modify(sam_ldb, msg); -} - /* * Handle ldb_request in transaction */ static int dsdb_autotransaction_request(struct ldb_context *sam_ldb, - struct ldb_request *req) + struct ldb_request *req) { int ret; @@ -1022,55 +1050,6 @@ static int dsdb_autotransaction_request(struct ldb_context *sam_ldb, return ret; } -/* - * replace elements in a record using LDB_CONTROL_AS_SYSTEM - * used to skip access checks on operations - * that are performed by the system - */ -int samdb_replace_as_system(struct ldb_context *sam_ldb, - TALLOC_CTX *mem_ctx, - struct ldb_message *msg) -{ - int i; - int ldb_ret; - struct ldb_request *req = NULL; - - /* mark all the message elements as LDB_FLAG_MOD_REPLACE */ - for (i=0;inum_elements;i++) { - msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; - } - - - ldb_ret = ldb_msg_sanity_check(sam_ldb, msg); - if (ldb_ret != LDB_SUCCESS) { - return ldb_ret; - } - - ldb_ret = ldb_build_mod_req(&req, sam_ldb, mem_ctx, - msg, - NULL, - NULL, - ldb_op_default_callback, - NULL); - - if (ldb_ret != LDB_SUCCESS) { - talloc_free(req); - return ldb_ret; - } - - ldb_ret = ldb_request_add_control(req, LDB_CONTROL_AS_SYSTEM_OID, false, NULL); - if (ldb_ret != LDB_SUCCESS) { - talloc_free(req); - return ldb_ret; - } - - /* do request and auto start a transaction */ - ldb_ret = dsdb_autotransaction_request(sam_ldb, req); - - talloc_free(req); - return ldb_ret; -} - /* return a default security descriptor */ @@ -1083,21 +1062,6 @@ struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ct return sd; } -struct ldb_dn *samdb_base_dn(struct ldb_context *sam_ctx) -{ - return ldb_get_default_basedn(sam_ctx); -} - -struct ldb_dn *samdb_config_dn(struct ldb_context *sam_ctx) -{ - return ldb_get_config_basedn(sam_ctx); -} - -struct ldb_dn *samdb_schema_dn(struct ldb_context *sam_ctx) -{ - return ldb_get_schema_basedn(sam_ctx); -} - struct ldb_dn *samdb_aggregate_schema_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx) { struct ldb_dn *schema_dn = ldb_get_schema_basedn(sam_ctx); @@ -1116,16 +1080,11 @@ struct ldb_dn *samdb_aggregate_schema_dn(struct ldb_context *sam_ctx, TALLOC_CTX return aggregate_dn; } -struct ldb_dn *samdb_root_dn(struct ldb_context *sam_ctx) -{ - return ldb_get_root_basedn(sam_ctx); -} - struct ldb_dn *samdb_partitions_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx) { struct ldb_dn *new_dn; - new_dn = ldb_dn_copy(mem_ctx, samdb_config_dn(sam_ctx)); + new_dn = ldb_dn_copy(mem_ctx, ldb_get_config_basedn(sam_ctx)); if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Partitions")) { talloc_free(new_dn); return NULL; @@ -1133,11 +1092,23 @@ struct ldb_dn *samdb_partitions_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ return new_dn; } +struct ldb_dn *samdb_infrastructure_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx) +{ + struct ldb_dn *new_dn; + + new_dn = ldb_dn_copy(mem_ctx, ldb_get_default_basedn(sam_ctx)); + if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Infrastructure")) { + talloc_free(new_dn); + return NULL; + } + return new_dn; +} + struct ldb_dn *samdb_sites_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx) { struct ldb_dn *new_dn; - new_dn = ldb_dn_copy(mem_ctx, samdb_config_dn(sam_ctx)); + new_dn = ldb_dn_copy(mem_ctx, ldb_get_config_basedn(sam_ctx)); if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Sites")) { talloc_free(new_dn); return NULL; @@ -1245,6 +1216,43 @@ failed: return false; } +bool samdb_set_ntds_settings_dn(struct ldb_context *ldb, struct ldb_dn *ntds_settings_dn_in) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_dn *ntds_settings_dn_new; + struct ldb_dn *ntds_settings_dn_old; + + /* see if we have a cached copy */ + ntds_settings_dn_old = talloc_get_type(ldb_get_opaque(ldb, + "cache.ntds_settings_dn"), struct ldb_dn); + + tmp_ctx = talloc_new(ldb); + if (tmp_ctx == NULL) { + goto failed; + } + + ntds_settings_dn_new = ldb_dn_copy(tmp_ctx, ntds_settings_dn_in); + if (!ntds_settings_dn_new) { + goto failed; + } + + /* cache the domain_sid in the ldb */ + if (ldb_set_opaque(ldb, "cache.ntds_settings_dn", ntds_settings_dn_new) != LDB_SUCCESS) { + goto failed; + } + + talloc_steal(ldb, ntds_settings_dn_new); + talloc_free(tmp_ctx); + talloc_free(ntds_settings_dn_old); + + return true; + +failed: + DEBUG(1,("Failed to set our NTDS Settings DN in the ldb!\n")); + talloc_free(tmp_ctx); + return false; +} + /* Obtain the short name of the flexible single master operator * (FSMO), such as the PDC Emulator */ const char *samdb_result_fsmo_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg, @@ -1279,7 +1287,7 @@ struct ldb_dn *samdb_ntds_settings_dn(struct ldb_context *ldb) struct ldb_dn *settings_dn; /* see if we have a cached copy */ - settings_dn = (struct ldb_dn *)ldb_get_opaque(ldb, "cache.settings_dn"); + settings_dn = (struct ldb_dn *)ldb_get_opaque(ldb, "cache.ntds_settings_dn"); if (settings_dn) { return settings_dn; } @@ -1517,14 +1525,20 @@ struct ldb_dn *samdb_server_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx) struct ldb_dn *samdb_server_site_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx) { struct ldb_dn *server_dn; + struct ldb_dn *servers_dn; struct ldb_dn *server_site_dn; + /* TODO: there must be a saner way to do this!! */ server_dn = samdb_server_dn(ldb, mem_ctx); if (!server_dn) return NULL; - server_site_dn = ldb_dn_get_parent(mem_ctx, server_dn); - + servers_dn = ldb_dn_get_parent(mem_ctx, server_dn); talloc_free(server_dn); + if (!servers_dn) return NULL; + + server_site_dn = ldb_dn_get_parent(mem_ctx, servers_dn); + talloc_free(servers_dn); + return server_site_dn; } @@ -1587,7 +1601,8 @@ int samdb_server_reference_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, stru */ int samdb_rid_manager_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn **dn) { - return samdb_reference_dn(ldb, mem_ctx, samdb_base_dn(ldb), "rIDManagerReference", dn); + return samdb_reference_dn(ldb, mem_ctx, ldb_get_default_basedn(ldb), + "rIDManagerReference", dn); } /* @@ -1610,12 +1625,115 @@ int samdb_rid_set_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn const char *samdb_server_site_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx) { - const struct ldb_val *val = ldb_dn_get_rdn_val(samdb_server_site_dn(ldb, mem_ctx)); + const struct ldb_val *val = ldb_dn_get_rdn_val(samdb_server_site_dn(ldb, + mem_ctx)); + + if (val == NULL) { + return NULL; + } + + return (const char *) val->data; +} + +/* + * Finds the client site by using the client's IP address. + * The "subnet_name" returns the name of the subnet if parameter != NULL + */ +const char *samdb_client_site_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, + const char *ip_address, char **subnet_name) +{ + const char *attrs[] = { "cn", "siteObject", NULL }; + struct ldb_dn *sites_container_dn, *subnets_dn, *sites_dn; + struct ldb_result *res; + const struct ldb_val *val; + const char *site_name = NULL, *l_subnet_name = NULL; + const char *allow_list[2] = { NULL, NULL }; + unsigned int i, count; + int cnt, ret; + + /* + * if we don't have a client ip e.g. ncalrpc + * the server site is the client site + */ + if (ip_address == NULL) { + return samdb_server_site_name(ldb, mem_ctx); + } + + sites_container_dn = samdb_sites_dn(ldb, mem_ctx); + if (sites_container_dn == NULL) { + return NULL; + } + + subnets_dn = ldb_dn_copy(mem_ctx, sites_container_dn); + if ( ! ldb_dn_add_child_fmt(subnets_dn, "CN=Subnets")) { + talloc_free(sites_container_dn); + talloc_free(subnets_dn); + return NULL; + } - if (val != NULL) - return (const char *) val->data; - else + ret = ldb_search(ldb, mem_ctx, &res, subnets_dn, LDB_SCOPE_ONELEVEL, + attrs, NULL); + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + count = 0; + } else if (ret != LDB_SUCCESS) { + talloc_free(sites_container_dn); + talloc_free(subnets_dn); return NULL; + } else { + count = res->count; + } + + for (i = 0; i < count; i++) { + l_subnet_name = ldb_msg_find_attr_as_string(res->msgs[i], "cn", + NULL); + + allow_list[0] = l_subnet_name; + + if (allow_access(mem_ctx, NULL, allow_list, "", ip_address)) { + sites_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, + res->msgs[i], + "siteObject"); + if (sites_dn == NULL) { + /* No reference, maybe another subnet matches */ + continue; + } + + /* "val" cannot be NULL here since "sites_dn" != NULL */ + val = ldb_dn_get_rdn_val(sites_dn); + site_name = talloc_strdup(mem_ctx, + (const char *) val->data); + + talloc_free(sites_dn); + + break; + } + } + + if (site_name == NULL) { + /* This is the Windows Server fallback rule: when no subnet + * exists and we have only one site available then use it (it + * is for sure the same as our server site). If more sites do + * exist then we don't know which one to use and set the site + * name to "". */ + cnt = samdb_search_count(ldb, sites_container_dn, + "(objectClass=site)"); + if (cnt == 1) { + site_name = samdb_server_site_name(ldb, mem_ctx); + } else { + site_name = talloc_strdup(mem_ctx, ""); + } + l_subnet_name = NULL; + } + + if (subnet_name != NULL) { + *subnet_name = talloc_strdup(mem_ctx, l_subnet_name); + } + + talloc_free(sites_container_dn); + talloc_free(subnets_dn); + talloc_free(res); + + return site_name; } /* @@ -1773,277 +1891,195 @@ enum samr_ValidationStatus samdb_check_password(const DATA_BLOB *password, return SAMR_VALIDATION_STATUS_SUCCESS; } +/* + * Callback for "samdb_set_password" password change + */ +int samdb_set_password_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + int ret; + + if (!ares) { + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + + if (ares->error != LDB_SUCCESS) { + ret = ares->error; + req->context = talloc_steal(req, + ldb_reply_get_control(ares, DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID)); + talloc_free(ares); + return ldb_request_done(req, ret); + } + + if (ares->type != LDB_REPLY_DONE) { + talloc_free(ares); + return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR); + } + + req->context = talloc_steal(req, + ldb_reply_get_control(ares, DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID)); + talloc_free(ares); + return ldb_request_done(req, LDB_SUCCESS); +} + /* * Sets the user password using plaintext UTF16 (attribute "new_password") or * LM (attribute "lmNewHash") or NT (attribute "ntNewHash") hash. Also pass * as parameter if it's a user change or not ("userChange"). The "rejectReason" * gives some more informations if the changed failed. * - * The caller should have a LDB transaction wrapping this. - * * Results: NT_STATUS_OK, NT_STATUS_INTERNAL_DB_CORRUPTION, * NT_STATUS_INVALID_PARAMETER, NT_STATUS_UNSUCCESSFUL, - * NT_STATUS_PASSWORD_RESTRICTION + * NT_STATUS_WRONG_PASSWORD, NT_STATUS_PASSWORD_RESTRICTION */ -NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, +NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *user_dn, struct ldb_dn *domain_dn, - struct ldb_message *mod, const DATA_BLOB *new_password, - struct samr_Password *param_lmNewHash, - struct samr_Password *param_ntNewHash, + struct samr_Password *lmNewHash, + struct samr_Password *ntNewHash, bool user_change, enum samPwdChangeReason *reject_reason, struct samr_DomInfo1 **_dominfo) { - const char * const user_attrs[] = { "userAccountControl", - "lmPwdHistory", - "ntPwdHistory", - "dBCSPwd", "unicodePwd", - "objectSid", - "pwdLastSet", NULL }; - const char * const domain_attrs[] = { "minPwdLength", "pwdProperties", - "pwdHistoryLength", - "maxPwdAge", "minPwdAge", NULL }; - NTTIME pwdLastSet; - uint32_t minPwdLength, pwdProperties, pwdHistoryLength; - int64_t maxPwdAge, minPwdAge; - uint32_t userAccountControl; - struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, - *lmPwdHash, *ntPwdHash, *lmNewHash, *ntNewHash; - struct samr_Password local_lmNewHash, local_ntNewHash; - int sambaLMPwdHistory_len, sambaNTPwdHistory_len; - struct dom_sid *domain_sid; - struct ldb_message **res; - bool restrictions; - int count; - time_t now = time(NULL); - NTTIME now_nt; - int i; + struct ldb_message *msg; + struct ldb_message_element *el; + struct ldb_request *req; + struct dsdb_control_password_change_status *pwd_stat = NULL; + int ret; + NTSTATUS status; - /* we need to know the time to compute password age */ - unix_to_nt_time(&now_nt, now); +#define CHECK_RET(x) \ + if (x != LDB_SUCCESS) { \ + talloc_free(msg); \ + return NT_STATUS_NO_MEMORY; \ + } - /* pull all the user parameters */ - count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs); - if (count != 1) { - return NT_STATUS_INTERNAL_DB_CORRUPTION; + msg = ldb_msg_new(mem_ctx); + if (msg == NULL) { + return NT_STATUS_NO_MEMORY; } - userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0); - sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res[0], - "lmPwdHistory", &sambaLMPwdHistory); - sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res[0], - "ntPwdHistory", &sambaNTPwdHistory); - lmPwdHash = samdb_result_hash(mem_ctx, res[0], "dBCSPwd"); - ntPwdHash = samdb_result_hash(mem_ctx, res[0], "unicodePwd"); - pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0); - - /* Copy parameters */ - lmNewHash = param_lmNewHash; - ntNewHash = param_ntNewHash; - - /* Only non-trust accounts have restrictions (possibly this - * test is the wrong way around, but I like to be restrictive - * if possible */ - restrictions = !(userAccountControl & (UF_INTERDOMAIN_TRUST_ACCOUNT - |UF_WORKSTATION_TRUST_ACCOUNT - |UF_SERVER_TRUST_ACCOUNT)); - - if (domain_dn != NULL) { - /* pull the domain parameters */ - count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, - domain_attrs); - if (count != 1) { - DEBUG(2, ("samdb_set_password: Domain DN %s is invalid, for user %s\n", - ldb_dn_get_linearized(domain_dn), - ldb_dn_get_linearized(user_dn))); - return NT_STATUS_NO_SUCH_DOMAIN; + msg->dn = user_dn; + if ((new_password != NULL) + && ((lmNewHash == NULL) && (ntNewHash == NULL))) { + /* we have the password as plaintext UTF16 */ + CHECK_RET(samdb_msg_add_value(ldb, mem_ctx, msg, + "clearTextPassword", new_password)); + el = ldb_msg_find_element(msg, "clearTextPassword"); + el->flags = LDB_FLAG_MOD_REPLACE; + } else if ((new_password == NULL) + && ((lmNewHash != NULL) || (ntNewHash != NULL))) { + /* we have a password as LM and/or NT hash */ + if (lmNewHash != NULL) { + CHECK_RET(samdb_msg_add_hash(ldb, mem_ctx, msg, + "dBCSPwd", lmNewHash)); + el = ldb_msg_find_element(msg, "dBCSPwd"); + el->flags = LDB_FLAG_MOD_REPLACE; } - } else { - /* work out the domain sid, and pull the domain from there */ - domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], - "objectSid"); - if (domain_sid == NULL) { - return NT_STATUS_INTERNAL_DB_CORRUPTION; + if (ntNewHash != NULL) { + CHECK_RET(samdb_msg_add_hash(ldb, mem_ctx, msg, + "unicodePwd", ntNewHash)); + el = ldb_msg_find_element(msg, "unicodePwd"); + el->flags = LDB_FLAG_MOD_REPLACE; } + } else { + /* the password wasn't specified correctly */ + talloc_free(msg); + return NT_STATUS_INVALID_PARAMETER; + } - count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, - "(objectSid=%s)", - ldap_encode_ndr_dom_sid(mem_ctx, domain_sid)); - if (count != 1) { - DEBUG(2, ("samdb_set_password: Could not find domain to match SID: %s, for user %s\n", - dom_sid_string(mem_ctx, domain_sid), - ldb_dn_get_linearized(user_dn))); - return NT_STATUS_NO_SUCH_DOMAIN; + /* build modify request */ + ret = ldb_build_mod_req(&req, ldb, mem_ctx, msg, NULL, NULL, + samdb_set_password_callback, NULL); + if (ret != LDB_SUCCESS) { + talloc_free(msg); + return NT_STATUS_NO_MEMORY; + } + + if (user_change) { + /* a user password change and we've checked already the old + * password somewhere else (callers responsability) */ + ret = ldb_request_add_control(req, + DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID, + true, NULL); + if (ret != LDB_SUCCESS) { + talloc_free(req); + talloc_free(msg); + return NT_STATUS_NO_MEMORY; } } + ret = ldb_request_add_control(req, + DSDB_CONTROL_PASSWORD_HASH_VALUES_OID, + true, NULL); + if (ret != LDB_SUCCESS) { + talloc_free(req); + talloc_free(msg); + return NT_STATUS_NO_MEMORY; + } + ret = ldb_request_add_control(req, + DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID, + true, NULL); + if (ret != LDB_SUCCESS) { + talloc_free(req); + talloc_free(msg); + return NT_STATUS_NO_MEMORY; + } - minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0); - pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0); - pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0); - maxPwdAge = samdb_result_int64(res[0], "maxPwdAge", 0); - minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0); + ret = dsdb_autotransaction_request(ldb, req); - if ((userAccountControl & UF_PASSWD_NOTREQD) != 0) { - /* see [MS-ADTS] 2.2.15 */ - minPwdLength = 0; + if (req->context != NULL) { + pwd_stat = talloc_steal(mem_ctx, + ((struct ldb_control *)req->context)->data); } + talloc_free(req); + talloc_free(msg); + + /* Sets the domain info (if requested) */ if (_dominfo != NULL) { struct samr_DomInfo1 *dominfo; - /* on failure we need to fill in the reject reasons */ - dominfo = talloc(mem_ctx, struct samr_DomInfo1); + + dominfo = talloc_zero(mem_ctx, struct samr_DomInfo1); if (dominfo == NULL) { return NT_STATUS_NO_MEMORY; } - dominfo->min_password_length = minPwdLength; - dominfo->password_properties = pwdProperties; - dominfo->password_history_length = pwdHistoryLength; - dominfo->max_password_age = maxPwdAge; - dominfo->min_password_age = minPwdAge; - *_dominfo = dominfo; - } - if ((restrictions != 0) && (new_password != 0)) { - char *new_pass; - - /* checks if the "minPwdLength" property is satisfied */ - if ((restrictions != 0) - && (minPwdLength > utf16_len_n( - new_password->data, new_password->length)/2)) { - if (reject_reason) { - *reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT; - } - return NT_STATUS_PASSWORD_RESTRICTION; + if (pwd_stat != NULL) { + dominfo->min_password_length = pwd_stat->domain_data.minPwdLength; + dominfo->password_properties = pwd_stat->domain_data.pwdProperties; + dominfo->password_history_length = pwd_stat->domain_data.pwdHistoryLength; + dominfo->max_password_age = pwd_stat->domain_data.maxPwdAge; + dominfo->min_password_age = pwd_stat->domain_data.minPwdAge; } - /* Create the NT hash */ - mdfour(local_ntNewHash.hash, new_password->data, - new_password->length); - - ntNewHash = &local_ntNewHash; - - /* Only check complexity if we can convert it at all. Assuming unconvertable passwords are 'strong' */ - if (convert_string_talloc_convenience(mem_ctx, - lp_iconv_convenience(ldb_get_opaque(ctx, "loadparm")), - CH_UTF16, CH_UNIX, - new_password->data, new_password->length, - (void **)&new_pass, NULL, false)) { - - /* checks the password complexity */ - if ((restrictions != 0) - && ((pwdProperties - & DOMAIN_PASSWORD_COMPLEX) != 0) - && (!check_password_quality(new_pass))) { - if (reject_reason) { - *reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX; - } - return NT_STATUS_PASSWORD_RESTRICTION; - } - - /* compute the new lm hashes (for checking history - case insenitivly!) */ - if (E_deshash(new_pass, local_lmNewHash.hash)) { - lmNewHash = &local_lmNewHash; - } - } + *_dominfo = dominfo; } - if ((restrictions != 0) && user_change) { - /* are all password changes disallowed? */ - if ((pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) != 0) { - if (reject_reason) { - *reject_reason = SAM_PWD_CHANGE_NO_ERROR; - } - return NT_STATUS_PASSWORD_RESTRICTION; - } - - /* can this user change the password? */ - if ((userAccountControl & UF_PASSWD_CANT_CHANGE) != 0) { - if (reject_reason) { - *reject_reason = SAM_PWD_CHANGE_NO_ERROR; - } - return NT_STATUS_PASSWORD_RESTRICTION; - } - - /* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */ - if (pwdLastSet - minPwdAge > now_nt) { - if (reject_reason) { - *reject_reason = SAM_PWD_CHANGE_NO_ERROR; - } - return NT_STATUS_PASSWORD_RESTRICTION; - } - - /* check the immediately past password */ - if (pwdHistoryLength > 0) { - if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, - lmPwdHash->hash, 16) == 0) { - if (reject_reason) { - *reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY; - } - return NT_STATUS_PASSWORD_RESTRICTION; - } - if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, - ntPwdHash->hash, 16) == 0) { - if (reject_reason) { - *reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY; - } - return NT_STATUS_PASSWORD_RESTRICTION; - } - } - - /* check the password history */ - sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, - pwdHistoryLength); - sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, - pwdHistoryLength); - - for (i=0; lmNewHash && ihash, sambaLMPwdHistory[i].hash, - 16) == 0) { - if (reject_reason) { - *reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY; - } - return NT_STATUS_PASSWORD_RESTRICTION; - } - } - for (i=0; ntNewHash && ihash, sambaNTPwdHistory[i].hash, - 16) == 0) { - if (reject_reason) { - *reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY; - } - return NT_STATUS_PASSWORD_RESTRICTION; - } + if (reject_reason != NULL) { + if (pwd_stat != NULL) { + *reject_reason = pwd_stat->reject_reason; + } else { + *reject_reason = SAM_PWD_CHANGE_NO_ERROR; } } -#define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0) + if (pwd_stat != NULL) { + talloc_free(pwd_stat); + } - /* the password is acceptable. Start forming the new fields */ - if (new_password != NULL) { - /* if we know the cleartext UTF16 password, then set it. - * Modules in ldb will set all the appropriate - * hashes */ - CHECK_RET(ldb_msg_add_value(mod, "clearTextPassword", new_password, NULL)); + /* TODO: Error results taken from "password_hash" module. Are they + correct? */ + if (ret == LDB_ERR_UNWILLING_TO_PERFORM) { + status = NT_STATUS_WRONG_PASSWORD; + } else if (ret == LDB_ERR_CONSTRAINT_VIOLATION) { + status = NT_STATUS_PASSWORD_RESTRICTION; + } else if (ret != LDB_SUCCESS) { + status = NT_STATUS_UNSUCCESSFUL; } else { - /* we don't have the cleartext, so set what we have of the - * hashes */ - - if (lmNewHash) { - CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash)); - } - - if (ntNewHash) { - CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "unicodePwd", ntNewHash)); - } + status = NT_STATUS_OK; } - if (reject_reason) { - *reject_reason = SAM_PWD_CHANGE_NO_ERROR; - } - return NT_STATUS_OK; + return status; } - /* * Sets the user password using plaintext UTF16 (attribute "new_password") or * LM (attribute "lmNewHash") or NT (attribute "ntNewHash") hash. Also pass @@ -2058,7 +2094,7 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, * * Results: NT_STATUS_OK, NT_STATUS_INTERNAL_DB_CORRUPTION, * NT_STATUS_INVALID_PARAMETER, NT_STATUS_UNSUCCESSFUL, - * NT_STATUS_PASSWORD_RESTRICTION + * NT_STATUS_WRONG_PASSWORD, NT_STATUS_PASSWORD_RESTRICTION */ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid, @@ -2071,7 +2107,6 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, { NTSTATUS nt_status; struct ldb_dn *user_dn; - struct ldb_message *msg; int ret; ret = ldb_transaction_start(ldb); @@ -2090,45 +2125,18 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, return NT_STATUS_NO_SUCH_USER; } - msg = ldb_msg_new(mem_ctx); - if (msg == NULL) { - ldb_transaction_cancel(ldb); - talloc_free(user_dn); - return NT_STATUS_NO_MEMORY; - } - - msg->dn = ldb_dn_copy(msg, user_dn); - if (!msg->dn) { - ldb_transaction_cancel(ldb); - talloc_free(user_dn); - talloc_free(msg); - return NT_STATUS_NO_MEMORY; - } - nt_status = samdb_set_password(ldb, mem_ctx, user_dn, NULL, - msg, new_password, + new_password, lmNewHash, ntNewHash, user_change, reject_reason, _dominfo); if (!NT_STATUS_IS_OK(nt_status)) { ldb_transaction_cancel(ldb); talloc_free(user_dn); - talloc_free(msg); return nt_status; } - /* modify the samdb record */ - ret = samdb_replace(ldb, mem_ctx, msg); - if (ret != LDB_SUCCESS) { - ldb_transaction_cancel(ldb); - talloc_free(user_dn); - talloc_free(msg); - return NT_STATUS_ACCESS_DENIED; - } - - talloc_free(msg); - ret = ldb_transaction_commit(ldb); if (ret != LDB_SUCCESS) { DEBUG(0,("Failed to commit transaction to change password on %s: %s\n", @@ -2163,7 +2171,8 @@ NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TA return NT_STATUS_NO_MEMORY; } - ret = dsdb_wellknown_dn(sam_ctx, sidstr, samdb_base_dn(sam_ctx), + ret = dsdb_wellknown_dn(sam_ctx, sidstr, + ldb_get_default_basedn(sam_ctx), DS_GUID_FOREIGNSECURITYPRINCIPALS_CONTAINER, &basedn); if (ret != LDB_SUCCESS) { @@ -2208,7 +2217,7 @@ NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TA struct ldb_dn *samdb_dns_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *dns_domain) { - int i; + unsigned int i; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); const char *binary_encoded; const char **split_realm; @@ -2302,143 +2311,33 @@ struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, */ int dsdb_find_dn_by_guid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, - const char *guid_str, struct ldb_dn **dn) + const struct GUID *guid, struct ldb_dn **dn) { int ret; struct ldb_result *res; const char *attrs[] = { NULL }; - struct ldb_request *search_req; - char *expression; - struct ldb_search_options_control *options; - - expression = talloc_asprintf(mem_ctx, "objectGUID=%s", guid_str); - if (!expression) { - DEBUG(0, (__location__ ": out of memory\n")); - return LDB_ERR_OPERATIONS_ERROR; - } - - res = talloc_zero(expression, struct ldb_result); - if (!res) { - DEBUG(0, (__location__ ": out of memory\n")); - talloc_free(expression); - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_build_search_req(&search_req, ldb, expression, - ldb_get_default_basedn(ldb), - LDB_SCOPE_SUBTREE, - expression, attrs, - NULL, - res, ldb_search_default_callback, - NULL); - if (ret != LDB_SUCCESS) { - talloc_free(expression); - return ret; - } + char *guid_str = GUID_string(mem_ctx, guid); - /* we need to cope with cross-partition links, so search for - the GUID over all partitions */ - options = talloc(search_req, struct ldb_search_options_control); - if (options == NULL) { - DEBUG(0, (__location__ ": out of memory\n")); - talloc_free(expression); + if (!guid_str) { return LDB_ERR_OPERATIONS_ERROR; } - options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT; - ret = ldb_request_add_control(search_req, LDB_CONTROL_EXTENDED_DN_OID, true, NULL); + ret = dsdb_search(ldb, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs, + DSDB_SEARCH_SEARCH_ALL_PARTITIONS | + DSDB_SEARCH_SHOW_EXTENDED_DN | + DSDB_SEARCH_ONE_ONLY, + "objectGUID=%s", guid_str); + talloc_free(guid_str); if (ret != LDB_SUCCESS) { - talloc_free(expression); return ret; } - ret = ldb_request_add_control(search_req, - LDB_CONTROL_SEARCH_OPTIONS_OID, - true, options); - if (ret != LDB_SUCCESS) { - talloc_free(expression); - return ret; - } - - ret = ldb_request(ldb, search_req); - if (ret != LDB_SUCCESS) { - talloc_free(expression); - return ret; - } - - ret = ldb_wait(search_req->handle, LDB_WAIT_ALL); - if (ret != LDB_SUCCESS) { - talloc_free(expression); - return ret; - } - - /* this really should be exactly 1, but there is a bug in the - partitions module that can return two here with the - search_options control set */ - if (res->count < 1) { - talloc_free(expression); - return LDB_ERR_NO_SUCH_OBJECT; - } - *dn = talloc_steal(mem_ctx, res->msgs[0]->dn); - talloc_free(expression); + talloc_free(res); return LDB_SUCCESS; } -/* - search for attrs on one DN, allowing for deleted objects - */ -int dsdb_search_dn_with_deleted(struct ldb_context *ldb, - TALLOC_CTX *mem_ctx, - struct ldb_result **_res, - struct ldb_dn *basedn, - const char * const *attrs) -{ - int ret; - struct ldb_request *req; - TALLOC_CTX *tmp_ctx; - struct ldb_result *res; - - tmp_ctx = talloc_new(mem_ctx); - - res = talloc_zero(tmp_ctx, struct ldb_result); - if (!res) { - talloc_free(tmp_ctx); - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_build_search_req(&req, ldb, tmp_ctx, - basedn, - LDB_SCOPE_BASE, - NULL, - attrs, - NULL, - res, - ldb_search_default_callback, - NULL); - if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - return ret; - } - - ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL); - if (ret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - return ret; - } - - ret = ldb_request(ldb, req); - if (ret == LDB_SUCCESS) { - ret = ldb_wait(req->handle, LDB_WAIT_ALL); - } - - *_res = talloc_steal(mem_ctx, res); - talloc_free(tmp_ctx); - return ret; -} - - /* use a DN to find a GUID with a given attribute name */ @@ -2454,7 +2353,7 @@ int dsdb_find_guid_attr_by_dn(struct ldb_context *ldb, attrs[0] = attribute; attrs[1] = NULL; - ret = dsdb_search_dn_with_deleted(ldb, tmp_ctx, &res, dn, attrs); + ret = dsdb_search_dn(ldb, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_DELETED); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -2528,7 +2427,7 @@ int dsdb_find_sid_by_dn(struct ldb_context *ldb, ZERO_STRUCTP(sid); - ret = dsdb_search_dn_with_deleted(ldb, tmp_ctx, &res, dn, attrs); + ret = dsdb_search_dn(ldb, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_DELETED); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -2548,7 +2447,6 @@ int dsdb_find_sid_by_dn(struct ldb_context *ldb, } - /* load a repsFromTo blob list for a given partition GUID attr must be "repsFrom" or "repsTo" @@ -2559,7 +2457,7 @@ WERROR dsdb_loadreps(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct ld const char *attrs[] = { attr, NULL }; struct ldb_result *res = NULL; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); - int i; + unsigned int i; struct ldb_message_element *el; *r = NULL; @@ -2613,7 +2511,7 @@ WERROR dsdb_savereps(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct ld TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); struct ldb_message *msg; struct ldb_message_element *el; - int i; + unsigned int i; msg = ldb_msg_new(tmp_ctx); msg->dn = dn; @@ -2657,10 +2555,11 @@ failed: /* - load the uSNHighest attribute from the @REPLCHANGED object for a - partition + load the uSNHighest and the uSNUrgent attributes from the @REPLCHANGED + object for a partition */ -int dsdb_load_partition_usn(struct ldb_context *ldb, struct ldb_dn *dn, uint64_t *uSN) +int dsdb_load_partition_usn(struct ldb_context *ldb, struct ldb_dn *dn, + uint64_t *uSN, uint64_t *urgent_uSN) { struct ldb_request *req; int ret; @@ -2725,8 +2624,14 @@ int dsdb_load_partition_usn(struct ldb_context *ldb, struct ldb_dn *dn, uint64_t if (res->count < 1) { *uSN = 0; + if (urgent_uSN) { + *urgent_uSN = 0; + } } else { *uSN = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNHighest", 0); + if (urgent_uSN) { + *urgent_uSN = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNUrgent", 0); + } } talloc_free(tmp_ctx); @@ -2734,105 +2639,107 @@ int dsdb_load_partition_usn(struct ldb_context *ldb, struct ldb_dn *dn, uint64_t return LDB_SUCCESS; } -/* - save the uSNHighest attribute in the @REPLCHANGED object for a - partition - */ -int dsdb_save_partition_usn(struct ldb_context *ldb, struct ldb_dn *dn, uint64_t uSN) +int drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1, + const struct drsuapi_DsReplicaCursor2 *c2) { - struct ldb_request *req; - struct ldb_message *msg; - struct dsdb_control_current_partition *p_ctrl; + return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id); +} + +int drsuapi_DsReplicaCursor_compare(const struct drsuapi_DsReplicaCursor *c1, + const struct drsuapi_DsReplicaCursor *c2) +{ + return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id); +} + + +/* + see if a computer identified by its invocationId is a RODC +*/ +int samdb_is_rodc(struct ldb_context *sam_ctx, const struct GUID *objectGUID, bool *is_rodc) +{ + /* 1) find the DN for this servers NTDSDSA object + 2) search for the msDS-isRODC attribute + 3) if not present then not a RODC + 4) if present and TRUE then is a RODC + */ + struct ldb_dn *config_dn; + const char *attrs[] = { "msDS-isRODC", NULL }; int ret; + struct ldb_result *res; + TALLOC_CTX *tmp_ctx = talloc_new(sam_ctx); - msg = ldb_msg_new(ldb); - if (msg == NULL) { + config_dn = ldb_get_config_basedn(sam_ctx); + if (!config_dn) { + talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } - msg->dn = ldb_dn_new(msg, ldb, "@REPLCHANGED"); - if (msg->dn == NULL) { - talloc_free(msg); - return LDB_ERR_OPERATIONS_ERROR; + ret = dsdb_search(sam_ctx, tmp_ctx, &res, config_dn, LDB_SCOPE_SUBTREE, attrs, + DSDB_SEARCH_ONE_ONLY, "objectGUID=%s", GUID_string(tmp_ctx, objectGUID)); + + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + *is_rodc = false; + talloc_free(tmp_ctx); + return LDB_SUCCESS; } - - ret = ldb_msg_add_fmt(msg, "uSNHighest", "%llu", (unsigned long long)uSN); + if (ret != LDB_SUCCESS) { - talloc_free(msg); + DEBUG(1,(("Failed to find our own NTDS Settings object by objectGUID=%s!\n"), + GUID_string(tmp_ctx, objectGUID))); + *is_rodc = false; + talloc_free(tmp_ctx); return ret; } - msg->elements[0].flags = LDB_FLAG_MOD_REPLACE; - - p_ctrl = talloc(msg, struct dsdb_control_current_partition); - if (p_ctrl == NULL) { - talloc_free(msg); - return LDB_ERR_OPERATIONS_ERROR; + ret = ldb_msg_find_attr_as_bool(res->msgs[0], "msDS-isRODC", 0); + *is_rodc = (ret == 1); + + talloc_free(tmp_ctx); + return LDB_SUCCESS; +} + + +/* + see if we are a RODC +*/ +int samdb_rodc(struct ldb_context *sam_ctx, bool *am_rodc) +{ + const struct GUID *objectGUID; + int ret; + bool *cached; + + /* see if we have a cached copy */ + cached = (bool *)ldb_get_opaque(ldb, "cache.am_rodc"); + if (cached) { + *am_rodc = *cached; + return LDB_SUCCESS; } - p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION; - p_ctrl->dn = dn; - ret = ldb_build_mod_req(&req, ldb, msg, - msg, - NULL, - NULL, ldb_op_default_callback, - NULL); -again: - if (ret != LDB_SUCCESS) { - talloc_free(msg); - return ret; + objectGUID = samdb_ntds_objectGUID(sam_ctx); + if (!objectGUID) { + return LDB_ERR_OPERATIONS_ERROR; } - - ret = ldb_request_add_control(req, - DSDB_CONTROL_CURRENT_PARTITION_OID, - false, p_ctrl); + + ret = samdb_is_rodc(sam_ctx, objectGUID, am_rodc); if (ret != LDB_SUCCESS) { - talloc_free(msg); return ret; } - - /* Run the new request */ - ret = ldb_request(ldb, req); - - if (ret == LDB_SUCCESS) { - ret = ldb_wait(req->handle, LDB_WAIT_ALL); - } - if (ret == LDB_ERR_NO_SUCH_OBJECT) { - ret = ldb_build_add_req(&req, ldb, msg, - msg, - NULL, - NULL, ldb_op_default_callback, - NULL); - goto again; + + cached = talloc(ldb, bool); + if (cached == NULL) { + return LDB_ERR_OPERATIONS_ERROR; } - - talloc_free(msg); - - return ret; -} + *cached = *am_rodc; -int drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1, - const struct drsuapi_DsReplicaCursor2 *c2) -{ - return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id); -} + ret = ldb_set_opaque(ldb, "cache.am_rodc", cached); + if (ret != LDB_SUCCESS) { + talloc_free(cached); + return LDB_ERR_OPERATIONS_ERROR; + } -int drsuapi_DsReplicaCursor_compare(const struct drsuapi_DsReplicaCursor *c1, - const struct drsuapi_DsReplicaCursor *c2) -{ - return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id); + return LDB_SUCCESS; } -/* - see if we are a RODC - - TODO: This should take a sam_ctx, and lookup the right object (with - a cache) -*/ -bool samdb_rodc(struct loadparm_context *lp_ctx) -{ - return lp_parm_bool(lp_ctx, NULL, "repl", "RODC", false); -} /* @@ -2868,11 +2775,33 @@ int samdb_ntds_options(struct ldb_context *ldb, uint32_t *options) return LDB_SUCCESS; failed: - DEBUG(1,("Failed to find our own NTDS Settings objectGUID in the ldb!\n")); + DEBUG(1,("Failed to find our own NTDS Settings options in the ldb!\n")); talloc_free(tmp_ctx); return LDB_ERR_NO_SUCH_OBJECT; } +const char* samdb_ntds_object_category(TALLOC_CTX *tmp_ctx, struct ldb_context *ldb) +{ + const char *attrs[] = { "objectCategory", NULL }; + int ret; + struct ldb_result *res; + + ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL); + if (ret) { + goto failed; + } + + if (res->count != 1) { + goto failed; + } + + return samdb_result_string(res->msgs[0], "objectCategory", NULL); + +failed: + DEBUG(1,("Failed to find our own NTDS Settings objectCategory in the ldb!\n")); + return NULL; +} + /* * Function which generates a "lDAPDisplayName" attribute from a "CN" one. * Algorithm implemented according to MS-ADTS 3.1.1.2.3.4 @@ -3000,6 +2929,35 @@ NTSTATUS dsdb_get_extended_dn_uint32(struct ldb_dn *dn, uint32_t *val, const cha return NT_STATUS_OK; } +/* + return a dom_sid from a extended DN structure + */ +NTSTATUS dsdb_get_extended_dn_sid(struct ldb_dn *dn, struct dom_sid *sid, const char *component_name) +{ + const struct ldb_val *sid_blob; + struct TALLOC_CTX *tmp_ctx; + enum ndr_err_code ndr_err; + + sid_blob = ldb_dn_get_extended_component(dn, "SID"); + if (!sid_blob) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + tmp_ctx = talloc_new(NULL); + + ndr_err = ndr_pull_struct_blob_all(sid_blob, tmp_ctx, NULL, sid, + (ndr_pull_flags_fn_t)ndr_pull_dom_sid); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + NTSTATUS status = ndr_map_error2ntstatus(ndr_err); + talloc_free(tmp_ctx); + return status; + } + + talloc_free(tmp_ctx); + return NT_STATUS_OK; +} + + /* return RMD_FLAGS directly from a ldb_dn returns 0 if not found @@ -3078,7 +3036,7 @@ int dsdb_wellknown_dn(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, return LDB_ERR_OPERATIONS_ERROR; } - ret = dsdb_search_dn_with_deleted(samdb, tmp_ctx, &res, dn, attrs); + ret = dsdb_search_dn(samdb, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_DELETED); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -3144,7 +3102,7 @@ int dsdb_find_nc_root(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, struct ldb } } - qsort(nc_dns, el->num_values, sizeof(nc_dns[0]), (comparison_fn_t)dsdb_dn_compare_ptrs); + TYPESAFE_QSORT(nc_dns, el->num_values, dsdb_dn_compare_ptrs); for (i=0; inum_values; i++) { if (ldb_dn_compare_base(nc_dns[i], dn) == 0) { @@ -3186,7 +3144,7 @@ int dsdb_get_deleted_objects_dn(struct ldb_context *ldb, int dsdb_tombstone_lifetime(struct ldb_context *ldb, uint32_t *lifetime) { struct ldb_dn *dn; - dn = samdb_config_dn(ldb); + dn = ldb_get_config_basedn(ldb); if (!dn) { return LDB_ERR_NO_SUCH_OBJECT; } @@ -3196,7 +3154,7 @@ int dsdb_tombstone_lifetime(struct ldb_context *ldb, uint32_t *lifetime) } /* see MS-ADTS section 7.1.1.2.4.1.1. There doesn't appear to be a wellknown GUID for this */ - if (!ldb_dn_add_child_fmt(dn, "CN=Directory Service,CN=Windows NT")) { + if (!ldb_dn_add_child_fmt(dn, "CN=Directory Service,CN=Windows NT,CN=Services")) { talloc_free(dn); return LDB_ERR_OPERATIONS_ERROR; } @@ -3221,3 +3179,577 @@ int samdb_ldb_val_case_cmp(const char *s, struct ldb_val *v) } return 0; } + + +/* + load the UDV for a partition in v2 format + The list is returned sorted, and with our local cursor added + */ +int dsdb_load_udv_v2(struct ldb_context *samdb, struct ldb_dn *dn, TALLOC_CTX *mem_ctx, + struct drsuapi_DsReplicaCursor2 **cursors, uint32_t *count) +{ + static const char *attrs[] = { "replUpToDateVector", NULL }; + struct ldb_result *r; + const struct ldb_val *ouv_value; + unsigned int i; + int ret; + uint64_t highest_usn; + const struct GUID *our_invocation_id; + struct timeval now = timeval_current(); + + ret = ldb_search(samdb, mem_ctx, &r, dn, LDB_SCOPE_BASE, attrs, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + + ouv_value = ldb_msg_find_ldb_val(r->msgs[0], "replUpToDateVector"); + if (ouv_value) { + enum ndr_err_code ndr_err; + struct replUpToDateVectorBlob ouv; + + ndr_err = ndr_pull_struct_blob(ouv_value, r, + lp_iconv_convenience(ldb_get_opaque(samdb, "loadparm")), &ouv, + (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(r); + return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; + } + if (ouv.version != 2) { + /* we always store as version 2, and + * replUpToDateVector is not replicated + */ + return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; + } + + *count = ouv.ctr.ctr2.count; + *cursors = talloc_steal(mem_ctx, ouv.ctr.ctr2.cursors); + } else { + *count = 0; + *cursors = NULL; + } + + talloc_free(r); + + our_invocation_id = samdb_ntds_invocation_id(samdb); + if (!our_invocation_id) { + DEBUG(0,(__location__ ": No invocationID on samdb - %s\n", ldb_errstring(samdb))); + talloc_free(*cursors); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = dsdb_load_partition_usn(samdb, dn, &highest_usn, NULL); + if (ret != LDB_SUCCESS) { + /* nothing to add - this can happen after a vampire */ + TYPESAFE_QSORT(*cursors, *count, drsuapi_DsReplicaCursor2_compare); + return LDB_SUCCESS; + } + + for (i=0; i<*count; i++) { + if (GUID_equal(our_invocation_id, &(*cursors)[i].source_dsa_invocation_id)) { + (*cursors)[i].highest_usn = highest_usn; + (*cursors)[i].last_sync_success = timeval_to_nttime(&now); + TYPESAFE_QSORT(*cursors, *count, drsuapi_DsReplicaCursor2_compare); + return LDB_SUCCESS; + } + } + + (*cursors) = talloc_realloc(mem_ctx, *cursors, struct drsuapi_DsReplicaCursor2, (*count)+1); + if (! *cursors) { + return LDB_ERR_OPERATIONS_ERROR; + } + + (*cursors)[*count].source_dsa_invocation_id = *our_invocation_id; + (*cursors)[*count].highest_usn = highest_usn; + (*cursors)[*count].last_sync_success = timeval_to_nttime(&now); + (*count)++; + + TYPESAFE_QSORT(*cursors, *count, drsuapi_DsReplicaCursor2_compare); + + return LDB_SUCCESS; +} + +/* + load the UDV for a partition in version 1 format + The list is returned sorted, and with our local cursor added + */ +int dsdb_load_udv_v1(struct ldb_context *samdb, struct ldb_dn *dn, TALLOC_CTX *mem_ctx, + struct drsuapi_DsReplicaCursor **cursors, uint32_t *count) +{ + struct drsuapi_DsReplicaCursor2 *v2; + unsigned int i; + int ret; + + ret = dsdb_load_udv_v2(samdb, dn, mem_ctx, &v2, count); + if (ret != LDB_SUCCESS) { + return ret; + } + + if (*count == 0) { + talloc_free(v2); + *cursors = NULL; + return LDB_SUCCESS; + } + + *cursors = talloc_array(mem_ctx, struct drsuapi_DsReplicaCursor, *count); + if (*cursors == NULL) { + talloc_free(v2); + return LDB_ERR_OPERATIONS_ERROR; + } + + for (i=0; i<*count; i++) { + (*cursors)[i].source_dsa_invocation_id = v2[i].source_dsa_invocation_id; + (*cursors)[i].highest_usn = v2[i].highest_usn; + } + talloc_free(v2); + return LDB_SUCCESS; +} + +/* + add a set of controls to a ldb_request structure based on a set of + flags. See util.h for a list of available flags + */ +int dsdb_request_add_controls(struct ldb_request *req, uint32_t dsdb_flags) +{ + int ret; + if (dsdb_flags & DSDB_SEARCH_SEARCH_ALL_PARTITIONS) { + struct ldb_search_options_control *options; + /* Using the phantom root control allows us to search all partitions */ + options = talloc(req, struct ldb_search_options_control); + if (options == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT; + + ret = ldb_request_add_control(req, + LDB_CONTROL_SEARCH_OPTIONS_OID, + true, options); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + if (dsdb_flags & DSDB_SEARCH_SHOW_DELETED) { + ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + if (dsdb_flags & DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT) { + ret = ldb_request_add_control(req, DSDB_CONTROL_DN_STORAGE_FORMAT_OID, true, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + if (dsdb_flags & DSDB_SEARCH_SHOW_EXTENDED_DN) { + struct ldb_extended_dn_control *extended_ctrl = talloc(req, struct ldb_extended_dn_control); + if (!extended_ctrl) { + return LDB_ERR_OPERATIONS_ERROR; + } + extended_ctrl->type = 1; + + ret = ldb_request_add_control(req, LDB_CONTROL_EXTENDED_DN_OID, true, extended_ctrl); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + if (dsdb_flags & DSDB_SEARCH_REVEAL_INTERNALS) { + ret = ldb_request_add_control(req, LDB_CONTROL_REVEAL_INTERNALS, false, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + if (dsdb_flags & DSDB_MODIFY_RELAX) { + ret = ldb_request_add_control(req, LDB_CONTROL_RELAX_OID, false, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + if (dsdb_flags & DSDB_MODIFY_PERMISSIVE) { + ret = ldb_request_add_control(req, LDB_CONTROL_PERMISSIVE_MODIFY_OID, false, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + if (dsdb_flags & DSDB_FLAG_AS_SYSTEM) { + ret = ldb_request_add_control(req, LDB_CONTROL_AS_SYSTEM_OID, false, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return LDB_SUCCESS; +} + +/* + a modify with a set of controls +*/ +int dsdb_modify(struct ldb_context *ldb, const struct ldb_message *message, + uint32_t dsdb_flags) +{ + struct ldb_request *req; + int ret; + + ret = ldb_build_mod_req(&req, ldb, ldb, + message, + NULL, + NULL, + ldb_op_default_callback, + NULL); + + if (ret != LDB_SUCCESS) return ret; + + ret = dsdb_request_add_controls(req, dsdb_flags); + if (ret != LDB_SUCCESS) { + talloc_free(req); + return ret; + } + + ret = dsdb_autotransaction_request(ldb, req); + + talloc_free(req); + return ret; +} + +/* + like dsdb_modify() but set all the element flags to + LDB_FLAG_MOD_REPLACE + */ +int dsdb_replace(struct ldb_context *ldb, struct ldb_message *msg, uint32_t dsdb_flags) +{ + unsigned int i; + + /* mark all the message elements as LDB_FLAG_MOD_REPLACE */ + for (i=0;inum_elements;i++) { + msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + } + + return dsdb_modify(ldb, msg, dsdb_flags); +} + + +/* + search for attrs on one DN, allowing for dsdb_flags controls + */ +int dsdb_search_dn(struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + struct ldb_result **_res, + struct ldb_dn *basedn, + const char * const *attrs, + uint32_t dsdb_flags) +{ + int ret; + struct ldb_request *req; + struct ldb_result *res; + + res = talloc_zero(mem_ctx, struct ldb_result); + if (!res) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_build_search_req(&req, ldb, res, + basedn, + LDB_SCOPE_BASE, + NULL, + attrs, + NULL, + res, + ldb_search_default_callback, + NULL); + if (ret != LDB_SUCCESS) { + talloc_free(res); + return ret; + } + + ret = dsdb_request_add_controls(req, dsdb_flags); + if (ret != LDB_SUCCESS) { + talloc_free(res); + return ret; + } + + ret = ldb_request(ldb, req); + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + talloc_free(req); + if (ret != LDB_SUCCESS) { + talloc_free(res); + return ret; + } + + *_res = res; + return LDB_SUCCESS; +} + +/* + general search with dsdb_flags for controls + */ +int dsdb_search(struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + struct ldb_result **_res, + struct ldb_dn *basedn, + enum ldb_scope scope, + const char * const *attrs, + uint32_t dsdb_flags, + const char *exp_fmt, ...) _PRINTF_ATTRIBUTE(8, 9) +{ + int ret; + struct ldb_request *req; + struct ldb_result *res; + va_list ap; + char *expression = NULL; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (exp_fmt) { + va_start(ap, exp_fmt); + expression = talloc_vasprintf(tmp_ctx, exp_fmt, ap); + va_end(ap); + + if (!expression) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + ret = ldb_build_search_req(&req, ldb, tmp_ctx, + basedn, + scope, + expression, + attrs, + NULL, + res, + ldb_search_default_callback, + NULL); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + ret = dsdb_request_add_controls(req, dsdb_flags); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + ret = ldb_request(ldb, req); + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + if (dsdb_flags & DSDB_SEARCH_ONE_ONLY) { + if (res->count == 0) { + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_OBJECT; + } + if (res->count != 1) { + talloc_free(tmp_ctx); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } + + *_res = talloc_steal(mem_ctx, res); + talloc_free(tmp_ctx); + + return LDB_SUCCESS; +} + + +/* + general search with dsdb_flags for controls + returns exactly 1 record or an error + */ +int dsdb_search_one(struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, + struct ldb_message **msg, + struct ldb_dn *basedn, + enum ldb_scope scope, + const char * const *attrs, + uint32_t dsdb_flags, + const char *exp_fmt, ...) _PRINTF_ATTRIBUTE(8, 9) +{ + int ret; + struct ldb_result *res; + va_list ap; + char *expression = NULL; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + + dsdb_flags |= DSDB_SEARCH_ONE_ONLY; + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (exp_fmt) { + va_start(ap, exp_fmt); + expression = talloc_vasprintf(tmp_ctx, exp_fmt, ap); + va_end(ap); + + if (!expression) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + ret = dsdb_search(ldb, tmp_ctx, &res, basedn, scope, attrs, + dsdb_flags, "%s", expression); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + *msg = talloc_steal(mem_ctx, res->msgs[0]); + talloc_free(tmp_ctx); + + return LDB_SUCCESS; +} + +/* returns back the forest DNS name */ +const char *samdb_forest_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx) +{ + const char *forest_name = ldb_dn_canonical_string(mem_ctx, + ldb_get_root_basedn(ldb)); + char *p; + + if (forest_name == NULL) { + return NULL; + } + + p = strchr(forest_name, '/'); + if (p) { + *p = '\0'; + } + + return forest_name; +} + +/* + validate that an DSA GUID belongs to the specified user sid. + The user SID must be a domain controller account (either RODC or + RWDC) + */ +int dsdb_validate_dsa_guid(struct ldb_context *ldb, + const struct GUID *dsa_guid, + const struct dom_sid *sid) +{ + /* strategy: + - find DN of record with the DSA GUID in the + configuration partition (objectGUID) + - remove "NTDS Settings" component from DN + - do a base search on that DN for serverReference with + extended-dn enabled + - extract objectSID from resulting serverReference + attribute + - check this sid matches the sid argument + */ + struct ldb_dn *config_dn; + TALLOC_CTX *tmp_ctx = talloc_new(ldb); + struct ldb_message *msg; + const char *attrs1[] = { NULL }; + const char *attrs2[] = { "serverReference", NULL }; + int ret; + struct ldb_dn *dn, *account_dn; + struct dom_sid sid2; + NTSTATUS status; + + config_dn = ldb_get_config_basedn(ldb); + + ret = dsdb_search_one(ldb, tmp_ctx, &msg, config_dn, LDB_SCOPE_SUBTREE, + attrs1, 0, "(&(objectGUID=%s)(objectClass=nTDSDSA))", GUID_string(tmp_ctx, dsa_guid)); + if (ret != LDB_SUCCESS) { + DEBUG(1,(__location__ ": Failed to find DSA objectGUID %s for sid %s\n", + GUID_string(tmp_ctx, dsa_guid), dom_sid_string(tmp_ctx, sid))); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + dn = msg->dn; + + if (!ldb_dn_remove_child_components(dn, 1)) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = dsdb_search_one(ldb, tmp_ctx, &msg, dn, LDB_SCOPE_BASE, + attrs2, DSDB_SEARCH_SHOW_EXTENDED_DN, + "(objectClass=server)"); + if (ret != LDB_SUCCESS) { + DEBUG(1,(__location__ ": Failed to find server record for DSA with objectGUID %s, sid %s\n", + GUID_string(tmp_ctx, dsa_guid), dom_sid_string(tmp_ctx, sid))); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + account_dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, msg, "serverReference"); + if (account_dn == NULL) { + DEBUG(1,(__location__ ": Failed to find account_dn for DSA with objectGUID %s, sid %s\n", + GUID_string(tmp_ctx, dsa_guid), dom_sid_string(tmp_ctx, sid))); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + status = dsdb_get_extended_dn_sid(account_dn, &sid2, "SID"); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1,(__location__ ": Failed to find SID for DSA with objectGUID %s, sid %s\n", + GUID_string(tmp_ctx, dsa_guid), dom_sid_string(tmp_ctx, sid))); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (!dom_sid_equal(sid, &sid2)) { + /* someone is trying to spoof another account */ + DEBUG(0,(__location__ ": Bad DSA objectGUID %s for sid %s - expected sid %s\n", + GUID_string(tmp_ctx, dsa_guid), + dom_sid_string(tmp_ctx, sid), + dom_sid_string(tmp_ctx, &sid2))); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + talloc_free(tmp_ctx); + return LDB_SUCCESS; +} + +const char *rodc_fas_list[] = {"ms-PKI-DPAPIMasterKeys", + "ms-PKI-AccountCredentials", + "ms-PKI-RoamingTimeStamp", + "ms-FVE-KeyPackage", + "ms-FVE-RecoveryGuid", + "ms-FVE-RecoveryInformation", + "ms-FVE-RecoveryPassword", + "ms-FVE-VolumeGuid", + "ms-TPM-OwnerInformation", + NULL}; +/* + check if the attribute belongs to the RODC filtered attribute set +*/ +bool dsdb_attr_in_rodc_fas(uint32_t replica_flags, const struct dsdb_attribute *sa) +{ + int rodc_filtered_flags = SEARCH_FLAG_RODC_ATTRIBUTE | SEARCH_FLAG_CONFIDENTIAL; + bool drs_write_replica = ((replica_flags & DRSUAPI_DRS_WRIT_REP) == 0); + + if (drs_write_replica && (sa->searchFlags & rodc_filtered_flags)) { + return true; + } + if (drs_write_replica && is_attr_in_list(rodc_fas_list, sa->cn)) { + return true; + } + return false; +}