X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Fdsdb%2Fcommon%2Futil.c;h=31f3ad41c44302f77a26c130cab179e8159321d4;hb=e9194af939aa47aea67c074853fd228acb3ac03b;hp=633279ead11c28f4d1cb59c9985acdc5a3e234ff;hpb=98f2a3b6a3a068e4d9741eed8a8648d85c318207;p=kamenim%2Fsamba.git diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c index 633279ead1..31f3ad41c4 100644 --- a/source4/dsdb/common/util.c +++ b/source4/dsdb/common/util.c @@ -37,6 +37,9 @@ #include "param/param.h" #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" /* search the sam for the specified attributes in a specific domain, filter on @@ -186,18 +189,19 @@ struct dom_sid *samdb_search_dom_sid(struct ldb_context *sam_ldb, return the count of the number of records in the sam matching the query */ int samdb_search_count(struct ldb_context *sam_ldb, - TALLOC_CTX *mem_ctx, struct ldb_dn *basedn, - const char *format, ...) _PRINTF_ATTRIBUTE(4,5) + const char *format, ...) _PRINTF_ATTRIBUTE(3,4) { va_list ap; struct ldb_message **res; - const char * const attrs[] = { NULL }; + const char *attrs[] = { NULL }; int ret; + TALLOC_CTX *tmp_ctx = talloc_new(sam_ldb); va_start(ap, format); - ret = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap); + ret = gendb_search_v(sam_ldb, tmp_ctx, basedn, &res, attrs, format, ap); va_end(ap); + talloc_free(tmp_ctx); return ret; } @@ -206,9 +210,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) @@ -312,7 +316,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); } @@ -394,22 +398,15 @@ struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_messa struct GUID samdb_result_guid(const struct ldb_message *msg, const char *attr) { const struct ldb_val *v; - enum ndr_err_code ndr_err; struct GUID guid; - TALLOC_CTX *mem_ctx; - - ZERO_STRUCT(guid); + NTSTATUS status; v = ldb_msg_find_ldb_val(msg, attr); - if (!v) return guid; + if (!v) return GUID_zero(); - mem_ctx = talloc_named_const(NULL, 0, "samdb_result_guid"); - if (!mem_ctx) return guid; - ndr_err = ndr_pull_struct_blob(v, mem_ctx, NULL, &guid, - (ndr_pull_flags_fn_t)ndr_pull_GUID); - talloc_free(mem_ctx); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - return guid; + status = GUID_from_ndr_blob(v, &guid); + if (!NT_STATUS_IS_OK(status)) { + return GUID_zero(); } return guid; @@ -431,7 +428,8 @@ struct dom_sid *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, const struct ldb_me /* pull a NTTIME in a result set. */ -NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME default_value) +NTTIME samdb_result_nttime(const struct ldb_message *msg, const char *attr, + NTTIME default_value) { return ldb_msg_find_attr_as_uint64(msg, attr, default_value); } @@ -442,7 +440,7 @@ NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME def * it returns 0x7FFFFFFFFFFFFFFF, not returning this value in this case * cause windows 2008 and newer version to fail for SMB requests */ -NTTIME samdb_result_last_logoff(struct ldb_message *msg) +NTTIME samdb_result_last_logoff(const struct ldb_message *msg) { NTTIME ret = ldb_msg_find_attr_as_uint64(msg, "lastLogoff",0); @@ -464,7 +462,7 @@ NTTIME samdb_result_last_logoff(struct ldb_message *msg) * Consolidate that logic here to allow clearer logic for account expiry in * the rest of the code. */ -NTTIME samdb_result_account_expires(struct ldb_message *msg) +NTTIME samdb_result_account_expires(const struct ldb_message *msg) { NTTIME ret = ldb_msg_find_attr_as_uint64(msg, "accountExpires", 0); @@ -478,7 +476,8 @@ NTTIME samdb_result_account_expires(struct ldb_message *msg) /* pull a uint64_t from a result set. */ -uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value) +uint64_t samdb_result_uint64(const struct ldb_message *msg, const char *attr, + uint64_t default_value) { return ldb_msg_find_attr_as_uint64(msg, attr, default_value); } @@ -558,14 +557,13 @@ 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 = 0; + unsigned int count, i; const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr); - int i; *hashes = NULL; if (!val) { @@ -593,7 +591,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; @@ -608,7 +606,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; @@ -690,7 +688,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; @@ -706,7 +704,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) { @@ -772,7 +770,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); } @@ -785,7 +783,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); } @@ -800,18 +798,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; } /* @@ -825,18 +823,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; } /* @@ -850,13 +848,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); } /* @@ -875,8 +872,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); } /* @@ -888,7 +884,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); @@ -898,14 +894,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); } @@ -985,19 +982,29 @@ int samdb_msg_set_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struc } /* - replace elements in a record -*/ -int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg) + * Handle ldb_request in transaction + */ +static int dsdb_autotransaction_request(struct ldb_context *sam_ldb, + struct ldb_request *req) { - int i; + int ret; - /* mark all the message elements as LDB_FLAG_MOD_REPLACE */ - for (i=0;inum_elements;i++) { - msg->elements[i].flags = LDB_FLAG_MOD_REPLACE; + ret = ldb_transaction_start(sam_ldb); + if (ret != LDB_SUCCESS) { + return ret; } - /* modify the samdb record */ - return ldb_modify(sam_ldb, msg); + ret = ldb_request(sam_ldb, req); + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + if (ret == LDB_SUCCESS) { + return ldb_transaction_commit(sam_ldb); + } + ldb_transaction_cancel(sam_ldb); + + return ret; } /* @@ -1012,31 +1019,29 @@ 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) +struct ldb_dn *samdb_aggregate_schema_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx) { - return ldb_get_schema_basedn(sam_ctx); -} + struct ldb_dn *schema_dn = ldb_get_schema_basedn(sam_ctx); + struct ldb_dn *aggregate_dn; + if (!schema_dn) { + return NULL; + } -struct ldb_dn *samdb_root_dn(struct ldb_context *sam_ctx) -{ - return ldb_get_root_basedn(sam_ctx); + aggregate_dn = ldb_dn_copy(mem_ctx, schema_dn); + if (!aggregate_dn) { + return NULL; + } + if (!ldb_dn_add_child_fmt(aggregate_dn, "CN=Aggregate")) { + return NULL; + } + return aggregate_dn; } 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; @@ -1044,11 +1049,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; @@ -1107,11 +1124,18 @@ const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb) return domain_sid; failed: - DEBUG(1,("Failed to find domain_sid for open ldb\n")); talloc_free(tmp_ctx); return NULL; } +/* + get domain sid from cache +*/ +const struct dom_sid *samdb_domain_sid_cache_only(struct ldb_context *ldb) +{ + return (struct dom_sid *)ldb_get_opaque(ldb, "cache.domain_sid"); +} + bool samdb_set_domain_sid(struct ldb_context *ldb, const struct dom_sid *dom_sid_in) { TALLOC_CTX *tmp_ctx; @@ -1149,6 +1173,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, @@ -1183,7 +1244,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; } @@ -1421,17 +1482,116 @@ 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; } +/* + find a 'reference' DN that points at another object + (eg. serverReference, rIDManagerReference etc) + */ +int samdb_reference_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *base, + const char *attribute, struct ldb_dn **dn) +{ + const char *attrs[2]; + struct ldb_result *res; + int ret; + + attrs[0] = attribute; + attrs[1] = NULL; + + ret = ldb_search(ldb, mem_ctx, &res, base, LDB_SCOPE_BASE, attrs, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + if (res->count != 1) { + talloc_free(res); + return LDB_ERR_NO_SUCH_OBJECT; + } + + *dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, res->msgs[0], attribute); + if (!*dn) { + talloc_free(res); + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + + talloc_free(res); + return LDB_SUCCESS; +} + +/* + find our machine account via the serverReference attribute in the + server DN + */ +int samdb_server_reference_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn **dn) +{ + struct ldb_dn *server_dn; + int ret; + + server_dn = samdb_server_dn(ldb, mem_ctx); + if (server_dn == NULL) { + return LDB_ERR_NO_SUCH_OBJECT; + } + + ret = samdb_reference_dn(ldb, mem_ctx, server_dn, "serverReference", dn); + talloc_free(server_dn); + + return ret; +} + +/* + find the RID Manager$ DN via the rIDManagerReference attribute in the + base DN + */ +int samdb_rid_manager_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn **dn) +{ + return samdb_reference_dn(ldb, mem_ctx, ldb_get_default_basedn(ldb), + "rIDManagerReference", dn); +} + +/* + find the RID Set DN via the rIDSetReferences attribute in our + machine account DN + */ +int samdb_rid_set_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn **dn) +{ + struct ldb_dn *server_ref_dn; + int ret; + + ret = samdb_server_reference_dn(ldb, mem_ctx, &server_ref_dn); + if (ret != LDB_SUCCESS) { + return ret; + } + ret = samdb_reference_dn(ldb, mem_ctx, server_ref_dn, "rIDSetReferences", dn); + talloc_free(server_ref_dn); + return ret; +} + +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)); + + if (val == NULL) { + return NULL; + } + + return (const char *) val->data; +} + /* work out if we are the PDC for the domain of the current open ldb */ @@ -1531,7 +1691,7 @@ int samdb_search_for_parent_domain(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, while ((sdn = ldb_dn_get_parent(local_ctx, sdn))) { ret = ldb_search(ldb, local_ctx, &res, sdn, LDB_SCOPE_BASE, attrs, - "(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain))"); + "(|(objectClass=domain)(objectClass=builtinDomain))"); if (ret == LDB_SUCCESS) { if (res->count == 1) { break; @@ -1552,6 +1712,7 @@ int samdb_search_for_parent_domain(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, if (res->count != 1) { *errstring = talloc_asprintf(mem_ctx, "Invalid dn (%s), not child of a domain object", ldb_dn_get_linearized(dn)); + DEBUG(0,(__location__ ": %s\n", *errstring)); talloc_free(local_ctx); return LDB_ERR_CONSTRAINT_VIOLATION; } @@ -1563,40 +1724,64 @@ int samdb_search_for_parent_domain(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, /* - set the user password using plaintext, obeying any user or domain - password restrictions + * Performs checks on a user password (plaintext UNIX format - attribute + * "password"). The remaining parameters have to be extracted from the domain + * object in the AD. + * + * Result codes from "enum samr_ValidationStatus" (consider "samr.idl") + */ +enum samr_ValidationStatus samdb_check_password(const DATA_BLOB *password, + const uint32_t pwdProperties, + const uint32_t minPwdLength) +{ + /* checks if the "minPwdLength" property is satisfied */ + if (minPwdLength > password->length) + return SAMR_VALIDATION_STATUS_PWD_TOO_SHORT; - note that this function doesn't actually store the result in the - database, it just fills in the "mod" structure with ldb modify - elements to setup the correct change when samdb_replace() is - called. This allows the caller to combine the change with other - changes (as is needed by some of the set user info levels) + /* checks the password complexity */ + if (((pwdProperties & DOMAIN_PASSWORD_COMPLEX) != 0) + && (password->data != NULL) + && (!check_password_quality((const char *) password->data))) + return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH; - The caller should probably have a transaction wrapping this -*/ + return SAMR_VALIDATION_STATUS_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 + */ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, - struct ldb_dn *user_dn, - struct ldb_dn *domain_dn, + 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, bool user_change, - enum samr_RejectReason *reject_reason, + enum samPwdChangeReason *reject_reason, struct samr_DomInfo1 **_dominfo) { - const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory", + const char * const user_attrs[] = { "userAccountControl", + "lmPwdHistory", "ntPwdHistory", "dBCSPwd", "unicodePwd", "objectSid", "pwdLastSet", NULL }; - const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength", - "maxPwdAge", "minPwdAge", - "minPwdLength", NULL }; + const char * const domain_attrs[] = { "minPwdLength", "pwdProperties", + "pwdHistoryLength", + "maxPwdAge", "minPwdAge", NULL }; NTTIME pwdLastSet; - int64_t minPwdAge; - uint_t minPwdLength, pwdProperties, pwdHistoryLength; - uint_t userAccountControl; + 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; @@ -1607,7 +1792,7 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, int count; time_t now = time(NULL); NTTIME now_nt; - int i; + unsigned int i; /* we need to know the time to compute password age */ unix_to_nt_time(&now_nt, now); @@ -1617,14 +1802,14 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, if (count != 1) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } - 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); + 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; @@ -1637,9 +1822,10 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, |UF_WORKSTATION_TRUST_ACCOUNT |UF_SERVER_TRUST_ACCOUNT)); - if (domain_dn) { + if (domain_dn != NULL) { /* pull the domain parameters */ - count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs); + 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), @@ -1648,14 +1834,15 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, } } else { /* work out the domain sid, and pull the domain from there */ - domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid"); + domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], + "objectSid"); if (domain_sid == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, - "(objectSid=%s)", - ldap_encode_ndr_dom_sid(mem_ctx, domain_sid)); + "(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), @@ -1664,17 +1851,18 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, } } - pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0); - pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0); - minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0); - minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0); + 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); - if (userAccountControl & UF_PASSWD_NOTREQD) { + if ((userAccountControl & UF_PASSWD_NOTREQD) != 0) { /* see [MS-ADTS] 2.2.15 */ minPwdLength = 0; } - if (_dominfo) { + if (_dominfo != NULL) { struct samr_DomInfo1 *dominfo; /* on failure we need to fill in the reject reasons */ dominfo = talloc(mem_ctx, struct samr_DomInfo1); @@ -1684,24 +1872,27 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, dominfo->min_password_length = minPwdLength; dominfo->password_properties = pwdProperties; dominfo->password_history_length = pwdHistoryLength; - dominfo->max_password_age = minPwdAge; + dominfo->max_password_age = maxPwdAge; dominfo->min_password_age = minPwdAge; *_dominfo = dominfo; } - if (restrictions && new_password) { + if ((restrictions != 0) && (new_password != 0)) { char *new_pass; - /* check the various password restrictions */ - if (restrictions && minPwdLength > utf16_len_n(new_password->data, new_password->length) / 2) { + /* 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 = SAMR_REJECT_TOO_SHORT; + *reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT; } return NT_STATUS_PASSWORD_RESTRICTION; } /* Create the NT hash */ - mdfour(local_ntNewHash.hash, new_password->data, new_password->length); + mdfour(local_ntNewHash.hash, new_password->data, + new_password->length); ntNewHash = &local_ntNewHash; @@ -1712,11 +1903,13 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, new_password->data, new_password->length, (void **)&new_pass, NULL, false)) { - /* possibly check password complexity */ - if (restrictions && (pwdProperties & DOMAIN_PASSWORD_COMPLEX) && - !check_password_quality(new_pass)) { + /* checks the password complexity */ + if ((restrictions != 0) + && ((pwdProperties + & DOMAIN_PASSWORD_COMPLEX) != 0) + && (!check_password_quality(new_pass))) { if (reject_reason) { - *reject_reason = SAMR_REJECT_COMPLEXITY; + *reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX; } return NT_STATUS_PASSWORD_RESTRICTION; } @@ -1728,63 +1921,69 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, } } - if (restrictions && user_change) { + if ((restrictions != 0) && user_change) { /* are all password changes disallowed? */ - if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) { + if ((pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) != 0) { if (reject_reason) { - *reject_reason = SAMR_REJECT_OTHER; + *reject_reason = SAM_PWD_CHANGE_NO_ERROR; } return NT_STATUS_PASSWORD_RESTRICTION; } - /* can this user change password? */ - if (userAccountControl & UF_PASSWD_CANT_CHANGE) { + /* can this user change the password? */ + if ((userAccountControl & UF_PASSWD_CANT_CHANGE) != 0) { if (reject_reason) { - *reject_reason = SAMR_REJECT_OTHER; + *reject_reason = SAM_PWD_CHANGE_NO_ERROR; } return NT_STATUS_PASSWORD_RESTRICTION; } - /* yes, this is a minus. The ages are in negative 100nsec units! */ + /* 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 = SAMR_REJECT_OTHER; + *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 (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, + lmPwdHash->hash, 16) == 0) { if (reject_reason) { - *reject_reason = SAMR_REJECT_IN_HISTORY; + *reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY; } return NT_STATUS_PASSWORD_RESTRICTION; } - if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) { + if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, + ntPwdHash->hash, 16) == 0) { if (reject_reason) { - *reject_reason = SAMR_REJECT_IN_HISTORY; + *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); + sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, + pwdHistoryLength); + sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, + pwdHistoryLength); for (i=0; lmNewHash && ihash, sambaLMPwdHistory[i].hash, 16) == 0) { + if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, + 16) == 0) { if (reject_reason) { - *reject_reason = SAMR_REJECT_IN_HISTORY; + *reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY; } return NT_STATUS_PASSWORD_RESTRICTION; } } for (i=0; ntNewHash && ihash, sambaNTPwdHistory[i].hash, 16) == 0) { + if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, + 16) == 0) { if (reject_reason) { - *reject_reason = SAMR_REJECT_IN_HISTORY; + *reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY; } return NT_STATUS_PASSWORD_RESTRICTION; } @@ -1794,48 +1993,54 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0) /* the password is acceptable. Start forming the new fields */ - if (new_password) { + 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)); } else { - /* We don't have the cleartext, so delete the old one - * and set what we have of the hashes */ - CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "clearTextPassword")); + /* 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)); - } else { - CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "dBCSPwd")); } if (ntNewHash) { CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "unicodePwd", ntNewHash)); - } else { - CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd")); } } + if (reject_reason) { + *reject_reason = SAM_PWD_CHANGE_NO_ERROR; + } return NT_STATUS_OK; } /* - set the user password using plaintext, obeying any user or domain - password restrictions - - This wrapper function takes a SID as input, rather than a user DN, - and actually performs the password change - -*/ -NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, + * 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. + * + * This wrapper function for "samdb_set_password" takes a SID as input rather + * than a user DN. + * + * This call encapsulates a new LDB transaction for changing the password; + * therefore the user hasn't to start a new one. + * + * Results: NT_STATUS_OK, NT_STATUS_INTERNAL_DB_CORRUPTION, + * NT_STATUS_INVALID_PARAMETER, NT_STATUS_UNSUCCESSFUL, + * NT_STATUS_PASSWORD_RESTRICTION + */ +NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid, - const DATA_BLOB *new_pass, + const DATA_BLOB *new_password, struct samr_Password *lmNewHash, struct samr_Password *ntNewHash, bool user_change, - enum samr_RejectReason *reject_reason, + enum samPwdChangeReason *reject_reason, struct samr_DomInfo1 **_dominfo) { NTSTATUS nt_status; @@ -1843,17 +2048,17 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg; int ret; - ret = ldb_transaction_start(ctx); - if (ret) { - DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx))); + ret = ldb_transaction_start(ldb); + if (ret != LDB_SUCCESS) { + DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ldb))); return NT_STATUS_TRANSACTION_ABORTED; } - user_dn = samdb_search_dn(ctx, mem_ctx, NULL, + user_dn = samdb_search_dn(ldb, mem_ctx, NULL, "(&(objectSid=%s)(objectClass=user))", ldap_encode_ndr_dom_sid(mem_ctx, user_sid)); if (!user_dn) { - ldb_transaction_cancel(ctx); + ldb_transaction_cancel(ldb); DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n", dom_sid_string(mem_ctx, user_sid))); return NT_STATUS_NO_SUCH_USER; @@ -1861,52 +2066,63 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, msg = ldb_msg_new(mem_ctx); if (msg == NULL) { - ldb_transaction_cancel(ctx); + 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(ctx); + ldb_transaction_cancel(ldb); + talloc_free(user_dn); + talloc_free(msg); return NT_STATUS_NO_MEMORY; } - nt_status = samdb_set_password(ctx, mem_ctx, + nt_status = samdb_set_password(ldb, mem_ctx, user_dn, NULL, - msg, new_pass, + msg, new_password, lmNewHash, ntNewHash, - user_change, /* This is a password set, not change */ + user_change, reject_reason, _dominfo); if (!NT_STATUS_IS_OK(nt_status)) { - ldb_transaction_cancel(ctx); + ldb_transaction_cancel(ldb); + talloc_free(user_dn); + talloc_free(msg); return nt_status; } /* modify the samdb record */ - ret = samdb_replace(ctx, mem_ctx, msg); - if (ret != 0) { - ldb_transaction_cancel(ctx); + ret = dsdb_replace(ldb, msg, 0); + if (ret != LDB_SUCCESS) { + ldb_transaction_cancel(ldb); + talloc_free(user_dn); + talloc_free(msg); return NT_STATUS_ACCESS_DENIED; } - ret = ldb_transaction_commit(ctx); - if (ret != 0) { + 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", - ldb_dn_get_linearized(msg->dn), - ldb_errstring(ctx))); + ldb_dn_get_linearized(user_dn), + ldb_errstring(ldb))); + talloc_free(user_dn); return NT_STATUS_TRANSACTION_ABORTED; } + + talloc_free(user_dn); return NT_STATUS_OK; } - NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct dom_sid *sid, struct ldb_dn **ret_dn) { struct ldb_message *msg; struct ldb_dn *basedn; - const char *sidstr; + char *sidstr; int ret; sidstr = dom_sid_string(mem_ctx, sid); @@ -1915,45 +2131,48 @@ NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TA /* We might have to create a ForeignSecurityPrincipal, even if this user * is in our own domain */ - msg = ldb_msg_new(mem_ctx); + msg = ldb_msg_new(sidstr); if (msg == NULL) { + talloc_free(sidstr); return NT_STATUS_NO_MEMORY; } - /* TODO: Hmmm. This feels wrong. How do I find the base dn to - * put the ForeignSecurityPrincipals? d_state->domain_dn does - * not work, this is wrong for the Builtin domain, there's no - * cn=For...,cn=Builtin,dc={BASEDN}. -- vl - */ - - basedn = samdb_search_dn(sam_ctx, mem_ctx, NULL, - "(&(objectClass=container)(cn=ForeignSecurityPrincipals))"); - - if (basedn == NULL) { + ret = dsdb_wellknown_dn(sam_ctx, sidstr, + ldb_get_default_basedn(sam_ctx), + DS_GUID_FOREIGNSECURITYPRINCIPALS_CONTAINER, + &basedn); + if (ret != LDB_SUCCESS) { DEBUG(0, ("Failed to find DN for " - "ForeignSecurityPrincipal container\n")); + "ForeignSecurityPrincipal container - %s\n", ldb_errstring(sam_ctx))); + talloc_free(sidstr); return NT_STATUS_INTERNAL_DB_CORRUPTION; } /* add core elements to the ldb_message for the alias */ - msg->dn = ldb_dn_copy(mem_ctx, basedn); - if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr)) + msg->dn = basedn; + if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr)) { + talloc_free(sidstr); return NT_STATUS_NO_MEMORY; + } - samdb_msg_add_string(sam_ctx, mem_ctx, msg, + samdb_msg_add_string(sam_ctx, msg, msg, "objectClass", "foreignSecurityPrincipal"); /* create the alias */ ret = ldb_add(sam_ctx, msg); - if (ret != 0) { + if (ret != LDB_SUCCESS) { DEBUG(0,("Failed to create foreignSecurityPrincipal " "record %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(sam_ctx))); + talloc_free(sidstr); return NT_STATUS_INTERNAL_DB_CORRUPTION; } - *ret_dn = msg->dn; + + *ret_dn = talloc_steal(mem_ctx, msg->dn); + talloc_free(sidstr); + return NT_STATUS_OK; } @@ -1964,7 +2183,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; @@ -1992,14 +2211,16 @@ struct ldb_dn *samdb_dns_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_c if (!ldb_dn_validate(dn)) { DEBUG(2, ("Failed to validated DN %s\n", ldb_dn_get_linearized(dn))); + talloc_free(tmp_ctx); return NULL; } + talloc_free(tmp_ctx); return dn; } + /* 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) { @@ -2056,111 +2277,153 @@ 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(mem_ctx, struct ldb_result); - if (!res) { - DEBUG(0, (__location__ ": out of memory\n")); - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_build_search_req(&search_req, ldb, mem_ctx, - ldb_get_default_basedn(ldb), - LDB_SCOPE_SUBTREE, - expression, attrs, - NULL, - res, ldb_search_default_callback, - NULL); - if (ret != LDB_SUCCESS) { - 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")); + 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_SEARCH_OPTIONS_OID, - true, options); - if (ret != LDB_SUCCESS) { - return ret; - } - - ret = ldb_request(ldb, search_req); - if (ret != LDB_SUCCESS) { - return ret; - } - ret = ldb_wait(search_req->handle, LDB_WAIT_ALL); + 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) { 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) { - return LDB_ERR_NO_SUCH_OBJECT; - } - - *dn = res->msgs[0]->dn; + *dn = talloc_steal(mem_ctx, res->msgs[0]->dn); + talloc_free(res); return LDB_SUCCESS; } - /* - use a DN to find a GUID + use a DN to find a GUID with a given attribute name */ -int dsdb_find_guid_by_dn(struct ldb_context *ldb, - struct ldb_dn *dn, struct GUID *guid) +int dsdb_find_guid_attr_by_dn(struct ldb_context *ldb, + struct ldb_dn *dn, const char *attribute, + struct GUID *guid) { int ret; struct ldb_result *res; - const char *attrs[] = { "objectGUID", NULL }; + const char *attrs[2]; TALLOC_CTX *tmp_ctx = talloc_new(ldb); - ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, NULL); + attrs[0] = attribute; + attrs[1] = NULL; + + ret = dsdb_search_dn(ldb, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_DELETED); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; } - *guid = samdb_result_guid(res->msgs[0], "objectGUID"); + if (res->count < 1) { + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_OBJECT; + } + *guid = samdb_result_guid(res->msgs[0], attribute); talloc_free(tmp_ctx); return LDB_SUCCESS; } - - /* - load a repsFromTo blob list for a given partition GUID - attr must be "repsFrom" or "repsTo" + use a DN to find a GUID */ -WERROR dsdb_loadreps(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct ldb_dn *dn, - const char *attr, struct repsFromToBlob **r, uint32_t *count) +int dsdb_find_guid_by_dn(struct ldb_context *ldb, + struct ldb_dn *dn, struct GUID *guid) +{ + return dsdb_find_guid_attr_by_dn(ldb, dn, "objectGUID", guid); +} + + + +/* + adds the given GUID to the given ldb_message. This value is added + for the given attr_name (may be either "objectGUID" or "parentGUID"). + */ +int dsdb_msg_add_guid(struct ldb_message *msg, + struct GUID *guid, + const char *attr_name) +{ + int ret; + struct ldb_val v; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_init("dsdb_msg_add_guid"); + + status = GUID_to_ndr_blob(guid, tmp_ctx, &v); + if (!NT_STATUS_IS_OK(status)) { + ret = LDB_ERR_OPERATIONS_ERROR; + goto done; + } + + ret = ldb_msg_add_steal_value(msg, attr_name, &v); + if (ret != LDB_SUCCESS) { + DEBUG(4,(__location__ ": Failed to add %s to the message\n", + attr_name)); + goto done; + } + + ret = LDB_SUCCESS; + +done: + talloc_free(tmp_ctx); + return ret; + +} + + +/* + use a DN to find a SID + */ +int dsdb_find_sid_by_dn(struct ldb_context *ldb, + struct ldb_dn *dn, struct dom_sid *sid) +{ + int ret; + struct ldb_result *res; + const char *attrs[] = { "objectSID", NULL }; + TALLOC_CTX *tmp_ctx = talloc_new(ldb); + struct dom_sid *s; + + ZERO_STRUCTP(sid); + + ret = dsdb_search_dn(ldb, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_DELETED); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + if (res->count < 1) { + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_OBJECT; + } + s = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSID"); + if (s == NULL) { + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_OBJECT; + } + *sid = *s; + talloc_free(tmp_ctx); + return LDB_SUCCESS; +} + + +/* + load a repsFromTo blob list for a given partition GUID + attr must be "repsFrom" or "repsTo" + */ +WERROR dsdb_loadreps(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct ldb_dn *dn, + const char *attr, struct repsFromToBlob **r, uint32_t *count) { 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; @@ -2214,7 +2477,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; @@ -2258,10 +2521,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; @@ -2326,8 +2590,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); @@ -2335,79 +2605,1052 @@ 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 *invocationId, 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 = ldb_msg_add_fmt(msg, "uSNHighest", "%llu", (unsigned long long)uSN); + ret = dsdb_search(sam_ctx, tmp_ctx, &res, config_dn, LDB_SCOPE_SUBTREE, attrs, + DSDB_SEARCH_ONE_ONLY, "invocationID=%s", GUID_string(tmp_ctx, invocationId)); if (ret != LDB_SUCCESS) { - talloc_free(msg); + 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); + 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 *invocationId; + invocationId = samdb_ntds_invocation_id(sam_ctx); + if (!invocationId) { return LDB_ERR_OPERATIONS_ERROR; } - p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION; - p_ctrl->dn = dn; + return samdb_is_rodc(sam_ctx, invocationId, am_rodc); +} - 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; + + +/* + return NTDS options flags. See MS-ADTS 7.1.1.2.2.1.2.1.1 + + flags are DS_NTDS_OPTION_* +*/ +int samdb_ntds_options(struct ldb_context *ldb, uint32_t *options) +{ + TALLOC_CTX *tmp_ctx; + const char *attrs[] = { "options", NULL }; + int ret; + struct ldb_result *res; + + tmp_ctx = talloc_new(ldb); + if (tmp_ctx == NULL) { + goto failed; } - - ret = ldb_request_add_control(req, - DSDB_CONTROL_CURRENT_PARTITION_OID, - false, p_ctrl); - if (ret != LDB_SUCCESS) { - talloc_free(msg); - return ret; + + ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL); + if (ret) { + goto failed; } - - /* Run the new request */ - ret = ldb_request(ldb, req); - - if (ret == LDB_SUCCESS) { - ret = ldb_wait(req->handle, LDB_WAIT_ALL); + + if (res->count != 1) { + goto failed; } - 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; + + *options = samdb_result_uint(res->msgs[0], "options", 0); + + talloc_free(tmp_ctx); + + return LDB_SUCCESS; + +failed: + 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; } - - talloc_free(msg); - + + 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 + */ +const char *samdb_cn_to_lDAPDisplayName(TALLOC_CTX *mem_ctx, const char *cn) +{ + char **tokens, *ret; + size_t i; + + tokens = str_list_make(mem_ctx, cn, " -_"); + if (tokens == NULL) + return NULL; + + /* "tolower()" and "toupper()" should also work properly on 0x00 */ + tokens[0][0] = tolower(tokens[0][0]); + for (i = 1; i < str_list_length((const char **)tokens); i++) + tokens[i][0] = toupper(tokens[i][0]); + + ret = talloc_strdup(mem_ctx, tokens[0]); + for (i = 1; i < str_list_length((const char **)tokens); i++) + ret = talloc_asprintf_append_buffer(ret, "%s", tokens[i]); + + talloc_free(tokens); + + return ret; +} + +/* + return domain functional level + returns DS_DOMAIN_FUNCTION_* + */ +int dsdb_functional_level(struct ldb_context *ldb) +{ + int *domainFunctionality = + talloc_get_type(ldb_get_opaque(ldb, "domainFunctionality"), int); + if (!domainFunctionality) { + DEBUG(0,(__location__ ": WARNING: domainFunctionality not setup\n")); + return DS_DOMAIN_FUNCTION_2000; + } + return *domainFunctionality; +} + +/* + set a GUID in an extended DN structure + */ +int dsdb_set_extended_dn_guid(struct ldb_dn *dn, const struct GUID *guid, const char *component_name) +{ + struct ldb_val v; + NTSTATUS status; + int ret; + + status = GUID_to_ndr_blob(guid, dn, &v); + if (!NT_STATUS_IS_OK(status)) { + return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; + } + + ret = ldb_dn_set_extended_component(dn, component_name, &v); + data_blob_free(&v); return ret; } + +/* + return a GUID from a extended DN structure + */ +NTSTATUS dsdb_get_extended_dn_guid(struct ldb_dn *dn, struct GUID *guid, const char *component_name) +{ + const struct ldb_val *v; + + v = ldb_dn_get_extended_component(dn, component_name); + if (v == NULL) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + return GUID_from_ndr_blob(v, guid); +} + +/* + return a uint64_t from a extended DN structure + */ +NTSTATUS dsdb_get_extended_dn_uint64(struct ldb_dn *dn, uint64_t *val, const char *component_name) +{ + const struct ldb_val *v; + char *s; + + v = ldb_dn_get_extended_component(dn, component_name); + if (v == NULL) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + s = talloc_strndup(dn, (const char *)v->data, v->length); + NT_STATUS_HAVE_NO_MEMORY(s); + + *val = strtoull(s, NULL, 0); + + talloc_free(s); + return NT_STATUS_OK; +} + +/* + return a NTTIME from a extended DN structure + */ +NTSTATUS dsdb_get_extended_dn_nttime(struct ldb_dn *dn, NTTIME *nttime, const char *component_name) +{ + return dsdb_get_extended_dn_uint64(dn, nttime, component_name); +} + +/* + return a uint32_t from a extended DN structure + */ +NTSTATUS dsdb_get_extended_dn_uint32(struct ldb_dn *dn, uint32_t *val, const char *component_name) +{ + const struct ldb_val *v; + char *s; + + v = ldb_dn_get_extended_component(dn, component_name); + if (v == NULL) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + s = talloc_strndup(dn, (const char *)v->data, v->length); + NT_STATUS_HAVE_NO_MEMORY(s); + + *val = strtoul(s, NULL, 0); + + talloc_free(s); + 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 + */ +uint32_t dsdb_dn_rmd_flags(struct ldb_dn *dn) +{ + const struct ldb_val *v; + char buf[32]; + v = ldb_dn_get_extended_component(dn, "RMD_FLAGS"); + if (!v || v->length > sizeof(buf)-1) return 0; + strncpy(buf, (const char *)v->data, v->length); + buf[v->length] = 0; + return strtoul(buf, NULL, 10); +} + +/* + return RMD_FLAGS directly from a ldb_val for a DN + returns 0 if RMD_FLAGS is not found + */ +uint32_t dsdb_dn_val_rmd_flags(struct ldb_val *val) +{ + const char *p; + uint32_t flags; + char *end; + + if (val->length < 13) { + return 0; + } + p = memmem(val->data, val->length-2, " */ + return 0; + } + return flags; +} + +/* + return true if a ldb_val containing a DN in storage form is deleted + */ +bool dsdb_dn_is_deleted_val(struct ldb_val *val) +{ + return (dsdb_dn_val_rmd_flags(val) & DSDB_RMD_FLAG_DELETED) != 0; +} + +/* + return true if a ldb_val containing a DN in storage form is + in the upgraded w2k3 linked attribute format + */ +bool dsdb_dn_is_upgraded_link_val(struct ldb_val *val) +{ + return memmem(val->data, val->length, "", + wk_guid, ldb_dn_get_linearized(nc_root)); + if (!wkguid_dn) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = dsdb_search_dn(samdb, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_DELETED); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + (*wkguid_dn) = talloc_steal(mem_ctx, res->msgs[0]->dn); + talloc_free(tmp_ctx); + return LDB_SUCCESS; +} + + +static int dsdb_dn_compare_ptrs(struct ldb_dn **dn1, struct ldb_dn **dn2) +{ + return ldb_dn_compare(*dn1, *dn2); +} + +/* + find a NC root given a DN within the NC + */ +int dsdb_find_nc_root(struct ldb_context *samdb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn, + struct ldb_dn **nc_root) +{ + const char *root_attrs[] = { "namingContexts", NULL }; + TALLOC_CTX *tmp_ctx; + int ret; + struct ldb_message_element *el; + struct ldb_result *root_res; + int i; + struct ldb_dn **nc_dns; + + tmp_ctx = talloc_new(samdb); + if (tmp_ctx == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_search(samdb, tmp_ctx, &root_res, + ldb_dn_new(tmp_ctx, samdb, ""), LDB_SCOPE_BASE, root_attrs, NULL); + if (ret) { + DEBUG(1,("Searching for namingContexts in rootDSE failed: %s\n", ldb_errstring(samdb))); + talloc_free(tmp_ctx); + return ret; + } + + el = ldb_msg_find_element(root_res->msgs[0], "namingContexts"); + if (!el) { + DEBUG(1,("Finding namingContexts element in root_res failed: %s\n", + ldb_errstring(samdb))); + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + + nc_dns = talloc_array(tmp_ctx, struct ldb_dn *, el->num_values); + if (!nc_dns) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + for (i=0; inum_values; i++) { + nc_dns[i] = ldb_dn_from_ldb_val(nc_dns, samdb, &el->values[i]); + if (nc_dns[i] == NULL) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + 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) { + (*nc_root) = talloc_steal(mem_ctx, nc_dns[i]); + talloc_free(tmp_ctx); + return LDB_SUCCESS; + } + } + + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_OBJECT; +} + + +/* + find the deleted objects DN for any object, by looking for the NC + root, then looking up the wellknown GUID + */ +int dsdb_get_deleted_objects_dn(struct ldb_context *ldb, + TALLOC_CTX *mem_ctx, struct ldb_dn *obj_dn, + struct ldb_dn **do_dn) +{ + struct ldb_dn *nc_root; + int ret; + + ret = dsdb_find_nc_root(ldb, mem_ctx, obj_dn, &nc_root); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = dsdb_wellknown_dn(ldb, mem_ctx, nc_root, DS_GUID_DELETED_OBJECTS_CONTAINER, do_dn); + talloc_free(nc_root); + return ret; +} + +/* + return the tombstoneLifetime, in days + */ +int dsdb_tombstone_lifetime(struct ldb_context *ldb, uint32_t *lifetime) +{ + struct ldb_dn *dn; + dn = ldb_get_config_basedn(ldb); + if (!dn) { + return LDB_ERR_NO_SUCH_OBJECT; + } + dn = ldb_dn_copy(ldb, dn); + if (!dn) { + return LDB_ERR_OPERATIONS_ERROR; + } + /* 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,CN=Services")) { + talloc_free(dn); + return LDB_ERR_OPERATIONS_ERROR; + } + + *lifetime = samdb_search_uint(ldb, dn, 180, dn, "tombstoneLifetime", "objectClass=nTDSService"); + talloc_free(dn); + return LDB_SUCCESS; +} + +/* + compare a ldb_val to a string case insensitively + */ +int samdb_ldb_val_case_cmp(const char *s, struct ldb_val *v) +{ + size_t len = strlen(s); + int ret; + if (len > v->length) return 1; + ret = strncasecmp(s, (const char *)v->data, v->length); + if (ret != 0) return ret; + if (v->length > len && v->data[len] != 0) { + return -1; + } + 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; +}