#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
/*
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)
/*
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);
}
/*
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);
}
* 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);
* 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);
/*
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);
}
}
/*
- 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;
{
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;
* 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;
struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
{
struct samr_LogonHours hours;
- const int units_per_week = 168;
+ size_t units_per_week = 168;
const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
+
ZERO_STRUCT(hours);
- hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
+
+ if (val) {
+ units_per_week = val->length * 8;
+ }
+
+ hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week/8);
if (!hours.bits) {
return hours;
}
hours.units_per_week = units_per_week;
- memset(hours.bits, 0xFF, units_per_week);
+ memset(hours.bits, 0xFF, units_per_week/8);
if (val) {
- memcpy(hours.bits, val->data, MIN(val->length, units_per_week));
+ memcpy(hours.bits, val->data, val->length);
}
+
return hours;
}
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;
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) {
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) {
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);
}
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);
}
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;
}
/*
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;
}
/*
}
/*
- 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);
}
/*
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);
}
/*
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);
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;i<count;i++) {
memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
const char *attr_name, struct lsa_BinaryString *parameters)
{
struct ldb_val val;
- val.length = parameters->length * 2;
+ val.length = parameters->length;
val.data = (uint8_t *)parameters->array;
return ldb_msg_add_value(msg, attr_name, &val, NULL);
}
}
/*
- 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;i<msg->num_elements;i++) {
- msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ ret = ldb_transaction_start(sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_request(sam_ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
}
- /* modify the samdb record */
- return ldb_modify(sam_ldb, msg);
+ if (ret == LDB_SUCCESS) {
+ return ldb_transaction_commit(sam_ldb);
+ }
+ ldb_transaction_cancel(sam_ldb);
+
+ return ret;
}
/*
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);
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;
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;
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;
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,
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;
}
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));
+ 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;
}
/*
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;
- }
- } 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;
+ 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;
}
-
- 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;
+ 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;
}
- 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) != 0) {
- /* see [MS-ADTS] 2.2.15 */
- minPwdLength = 0;
- }
-
- if (_dominfo != NULL) {
- struct samr_DomInfo1 *dominfo;
- /* on failure we need to fill in the reject reasons */
- dominfo = talloc(mem_ctx, struct samr_DomInfo1);
- if (dominfo == NULL) {
+ /* 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;
}
- 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;
+ }
+ 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;
}
- 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;
- }
-
- /* 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;
- }
+ ret = dsdb_autotransaction_request(ldb, req);
- /* compute the new lm hashes (for checking history - case insenitivly!) */
- if (E_deshash(new_pass, local_lmNewHash.hash)) {
- lmNewHash = &local_lmNewHash;
- }
- }
+ if (req->context != NULL) {
+ pwd_stat = talloc_steal(mem_ctx,
+ ((struct ldb_control *)req->context)->data);
}
- 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;
- }
+ talloc_free(req);
+ talloc_free(msg);
- /* 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;
- }
+ /* Sets the domain info (if requested) */
+ if (_dominfo != NULL) {
+ struct samr_DomInfo1 *dominfo;
- /* 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;
+ dominfo = talloc_zero(mem_ctx, struct samr_DomInfo1);
+ if (dominfo == NULL) {
+ return NT_STATUS_NO_MEMORY;
}
- /* 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;
- }
+ 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;
}
- /* check the password history */
- sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len,
- pwdHistoryLength);
- sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len,
- pwdHistoryLength);
-
- for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
- if (memcmp(lmNewHash->hash, 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 && i<sambaNTPwdHistory_len;i++) {
- if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash,
- 16) == 0) {
- if (reject_reason) {
- *reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
- }
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
- }
+ *_dominfo = dominfo;
}
-#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 != 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"));
-
- if (lmNewHash) {
- CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash));
+ if (reject_reason != NULL) {
+ if (pwd_stat != NULL) {
+ *reject_reason = pwd_stat->reject_reason;
} else {
- CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "dBCSPwd"));
+ *reject_reason = SAM_PWD_CHANGE_NO_ERROR;
}
+ }
- 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 (pwd_stat != NULL) {
+ talloc_free(pwd_stat);
}
- if (reject_reason) {
- *reject_reason = SAM_PWD_CHANGE_NO_ERROR;
+ /* 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 {
+ status = NT_STATUS_OK;
}
- return NT_STATUS_OK;
-}
+ return status;
+}
/*
* Sets the user password using plaintext UTF16 (attribute "new_password") or
*
* 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,
{
NTSTATUS nt_status;
struct ldb_dn *user_dn;
- struct ldb_message *msg;
int ret;
ret = ldb_transaction_start(ldb);
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",
{
struct ldb_message *msg;
struct ldb_dn *basedn;
- const char *sidstr;
+ char *sidstr;
int ret;
sidstr = dom_sid_string(mem_ctx, sid);
/* 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;
}
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;
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)
{
*/
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;
+ char *guid_str = GUID_string(mem_ctx, guid);
- expression = talloc_asprintf(mem_ctx, "objectGUID=%s", guid_str);
- if (!expression) {
- DEBUG(0, (__location__ ": out of memory\n"));
+ if (!guid_str) {
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;
- }
-
- /* 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"));
- 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);
- if (ret != LDB_SUCCESS) {
- return ret;
- }
-
- 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;
}
/*
- search for attrs on one DN, allowing for deleted objects
+ use a DN to find a GUID with a given attribute name
*/
-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 dsdb_find_guid_attr_by_dn(struct ldb_context *ldb,
+ struct ldb_dn *dn, const char *attribute,
+ struct GUID *guid)
{
int ret;
- struct ldb_request *req;
- TALLOC_CTX *tmp_ctx;
struct ldb_result *res;
+ const char *attrs[2];
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
- tmp_ctx = talloc_new(mem_ctx);
-
- res = talloc_zero(tmp_ctx, struct ldb_result);
- if (!res) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
+ attrs[0] = attribute;
+ attrs[1] = NULL;
- ret = ldb_build_search_req(&req, ldb, tmp_ctx,
- basedn,
- LDB_SCOPE_BASE,
- NULL,
- attrs,
- NULL,
- res,
- ldb_search_default_callback,
- 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;
}
-
- ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
- if (ret != LDB_SUCCESS) {
- return ret;
- }
-
- ret = ldb_request(ldb, req);
- if (ret == LDB_SUCCESS) {
- ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ if (res->count < 1) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
}
-
- talloc_free(req);
- *_res = talloc_steal(mem_ctx, res);
- return ret;
+ *guid = samdb_result_guid(res->msgs[0], attribute);
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
}
-
/*
use a DN to find a GUID
*/
-int dsdb_find_guid_by_dn(struct ldb_context *ldb,
+int dsdb_find_guid_by_dn(struct ldb_context *ldb,
struct ldb_dn *dn, struct GUID *guid)
{
- int ret;
- struct ldb_result *res;
- const char *attrs[] = { "objectGUID", NULL };
- TALLOC_CTX *tmp_ctx = talloc_new(ldb);
-
- ret = dsdb_search_dn_with_deleted(ldb, tmp_ctx, &res, dn, attrs);
- if (ret != LDB_SUCCESS) {
- talloc_free(tmp_ctx);
- return ret;
- }
- if (res->count < 1) {
- talloc_free(tmp_ctx);
- return LDB_ERR_NO_SUCH_OBJECT;
- }
- *guid = samdb_result_guid(res->msgs[0], "objectGUID");
- talloc_free(tmp_ctx);
- return LDB_SUCCESS;
+ return dsdb_find_guid_attr_by_dn(ldb, dn, "objectGUID", guid);
}
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;
}
-
/*
load a repsFromTo blob list for a given partition GUID
attr must be "repsFrom" or "repsTo"
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;
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;
/*
- 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;
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);
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(sam_ctx, "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);
+
+ cached = talloc(sam_ctx, bool);
+ if (cached == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
}
- 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 = *am_rodc;
+
+ ret = ldb_set_opaque(sam_ctx, "cache.am_rodc", cached);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(cached);
+ return LDB_ERR_OPERATIONS_ERROR;
}
-
- talloc_free(msg);
-
- return ret;
-}
-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);
+ 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);
-}
/*
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
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)
+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, "GUID");
+ 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, "<RMD_FLAGS=", 11);
+ if (!p) {
+ return 0;
+ }
+ flags = strtoul(p+11, &end, 10);
+ if (!end || *end != '>') {
+ /* it must end in a > */
+ 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, "<RMD_ADDTIME=", 13) != NULL;
+}
+
+/*
+ return a DN for a wellknown GUID
+ */
+int dsdb_wellknown_dn(struct ldb_context *samdb, TALLOC_CTX *mem_ctx,
+ struct ldb_dn *nc_root, const char *wk_guid,
+ struct ldb_dn **wkguid_dn)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ const char *attrs[] = { NULL };
+ int ret;
+ struct ldb_dn *dn;
+ struct ldb_result *res;
+
+ /* construct the magic WKGUID DN */
+ dn = ldb_dn_new_fmt(tmp_ctx, samdb, "<WKGUID=%s,%s>",
+ 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; i<el->num_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; i<el->num_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;i<msg->num_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;
+}