#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
/*
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 an array of samr_Password structutres 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;
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);
}
}
/*
- 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)
{
return samdb_msg_add_int(sam_ldb, mem_ctx, msg, attr_name, (int)v);
}
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;
return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str);
}
-/*
- replace elements in a record
-*/
-int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
-{
- int i;
-
- /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
- for (i=0;i<msg->num_elements;i++) {
- msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
- }
-
- /* modify the samdb record */
- return ldb_modify(sam_ldb, msg);
-}
-
/*
* Handle ldb_request in transaction
*/
static int dsdb_autotransaction_request(struct ldb_context *sam_ldb,
- struct ldb_request *req)
+ struct ldb_request *req)
{
int ret;
return ret;
}
-/*
- * replace elements in a record using LDB_CONTROL_AS_SYSTEM
- * used to skip access checks on operations
- * that are performed by the system
- */
-int samdb_replace_as_system(struct ldb_context *sam_ldb,
- TALLOC_CTX *mem_ctx,
- struct ldb_message *msg)
-{
- int i;
- int ldb_ret;
- struct ldb_request *req = NULL;
-
- /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
- for (i=0;i<msg->num_elements;i++) {
- msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
- }
-
-
- ldb_ret = ldb_msg_sanity_check(sam_ldb, msg);
- if (ldb_ret != LDB_SUCCESS) {
- return ldb_ret;
- }
-
- ldb_ret = ldb_build_mod_req(&req, sam_ldb, mem_ctx,
- msg,
- NULL,
- NULL,
- ldb_op_default_callback,
- NULL);
-
- if (ldb_ret != LDB_SUCCESS) {
- talloc_free(req);
- return ldb_ret;
- }
-
- ldb_ret = ldb_request_add_control(req, LDB_CONTROL_AS_SYSTEM_OID, false, NULL);
- if (ldb_ret != LDB_SUCCESS) {
- talloc_free(req);
- return ldb_ret;
- }
-
- /* do request and auto start a transaction */
- ldb_ret = dsdb_autotransaction_request(sam_ldb, req);
-
- talloc_free(req);
- return ldb_ret;
-}
-
/*
return a default security descriptor
*/
}
/* modify the samdb record */
- ret = samdb_replace(ldb, mem_ctx, msg);
+ ret = dsdb_replace(ldb, msg, 0);
if (ret != LDB_SUCCESS) {
ldb_transaction_cancel(ldb);
talloc_free(user_dn);
int ret;
struct ldb_result *res;
const char *attrs[] = { NULL };
- struct ldb_request *search_req;
- char *expression;
- struct ldb_search_options_control *options;
-
- expression = talloc_asprintf(mem_ctx, "objectGUID=%s", guid_str);
- if (!expression) {
- DEBUG(0, (__location__ ": out of memory\n"));
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- res = talloc_zero(expression, struct ldb_result);
- if (!res) {
- DEBUG(0, (__location__ ": out of memory\n"));
- talloc_free(expression);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- ret = ldb_build_search_req(&search_req, ldb, expression,
- ldb_get_default_basedn(ldb),
- LDB_SCOPE_SUBTREE,
- expression, attrs,
- NULL,
- res, ldb_search_default_callback,
- NULL);
- if (ret != LDB_SUCCESS) {
- talloc_free(expression);
- return ret;
- }
-
- /* we need to cope with cross-partition links, so search for
- the GUID over all partitions */
- options = talloc(search_req, struct ldb_search_options_control);
- if (options == NULL) {
- DEBUG(0, (__location__ ": out of memory\n"));
- talloc_free(expression);
- 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) {
- talloc_free(expression);
- return ret;
- }
-
- ret = ldb_request_add_control(search_req,
- LDB_CONTROL_SEARCH_OPTIONS_OID,
- true, options);
- if (ret != LDB_SUCCESS) {
- talloc_free(expression);
- return ret;
- }
-
- ret = ldb_request(ldb, search_req);
- if (ret != LDB_SUCCESS) {
- talloc_free(expression);
- return ret;
- }
-
- ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
+ ret = dsdb_search(ldb, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
+ DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
+ DSDB_SEARCH_SHOW_EXTENDED_DN,
+ "objectGUID=%s", guid_str);
if (ret != LDB_SUCCESS) {
- talloc_free(expression);
return ret;
}
-
- /* this really should be exactly 1, but there is a bug in the
- partitions module that can return two here with the
- search_options control set */
- if (res->count < 1) {
- talloc_free(expression);
+ if (res->count == 0) {
+ talloc_free(res);
return LDB_ERR_NO_SUCH_OBJECT;
}
-
- *dn = talloc_steal(mem_ctx, res->msgs[0]->dn);
- talloc_free(expression);
-
- return LDB_SUCCESS;
-}
-
-/*
- search for attrs on one DN, allowing for deleted objects
- */
-int dsdb_search_dn_with_deleted(struct ldb_context *ldb,
- TALLOC_CTX *mem_ctx,
- struct ldb_result **_res,
- struct ldb_dn *basedn,
- const char * const *attrs)
-{
- int ret;
- struct ldb_request *req;
- TALLOC_CTX *tmp_ctx;
- struct ldb_result *res;
-
- tmp_ctx = talloc_new(mem_ctx);
-
- res = talloc_zero(tmp_ctx, struct ldb_result);
- if (!res) {
- talloc_free(tmp_ctx);
+ if (res->count != 1) {
+ DEBUG(1,(__location__ ": found %u records with GUID %s\n", res->count, guid_str));
+ talloc_free(res);
return LDB_ERR_OPERATIONS_ERROR;
}
- ret = ldb_build_search_req(&req, ldb, tmp_ctx,
- basedn,
- LDB_SCOPE_BASE,
- NULL,
- attrs,
- NULL,
- res,
- ldb_search_default_callback,
- NULL);
- if (ret != LDB_SUCCESS) {
- talloc_free(tmp_ctx);
- return ret;
- }
-
- ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
- if (ret != LDB_SUCCESS) {
- talloc_free(tmp_ctx);
- return ret;
- }
-
- ret = ldb_request(ldb, req);
- if (ret == LDB_SUCCESS) {
- ret = ldb_wait(req->handle, LDB_WAIT_ALL);
- }
+ *dn = talloc_steal(mem_ctx, res->msgs[0]->dn);
+ talloc_free(res);
- *_res = talloc_steal(mem_ctx, res);
- talloc_free(tmp_ctx);
- return ret;
+ return LDB_SUCCESS;
}
-
/*
use a DN to find a GUID with a given attribute name
*/
attrs[0] = attribute;
attrs[1] = NULL;
- ret = dsdb_search_dn_with_deleted(ldb, tmp_ctx, &res, dn, attrs);
+ ret = dsdb_search_dn(ldb, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_DELETED);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
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 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);
}
/*
- save the uSNHighest attribute in the @REPLCHANGED object for a
+ save uSNHighest and uSNUrgent attributes in the @REPLCHANGED object for a
partition
*/
-int dsdb_save_partition_usn(struct ldb_context *ldb, struct ldb_dn *dn, uint64_t uSN)
+int dsdb_save_partition_usn(struct ldb_context *ldb, struct ldb_dn *dn,
+ uint64_t uSN, uint64_t urgent_uSN)
{
struct ldb_request *req;
struct ldb_message *msg;
}
msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
+ /* urgent_uSN is optional so may not be stored */
+ if (urgent_uSN) {
+ ret = ldb_msg_add_fmt(msg, "uSNUrgent", "%llu", (unsigned long long)urgent_uSN);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return ret;
+ }
+ msg->elements[1].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
p_ctrl = talloc(msg, struct dsdb_control_current_partition);
if (p_ctrl == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
- ret = dsdb_search_dn_with_deleted(samdb, tmp_ctx, &res, dn, attrs);
+ ret = dsdb_search_dn(samdb, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_DELETED);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
}
}
- qsort(nc_dns, el->num_values, sizeof(nc_dns[0]), (comparison_fn_t)dsdb_dn_compare_ptrs);
+ TYPESAFE_QSORT(nc_dns, el->num_values, dsdb_dn_compare_ptrs);
for (i=0; i<el->num_values; i++) {
if (ldb_dn_compare_base(nc_dns[i], dn) == 0) {
}
/* see MS-ADTS section 7.1.1.2.4.1.1. There doesn't appear to
be a wellknown GUID for this */
- if (!ldb_dn_add_child_fmt(dn, "CN=Directory Service,CN=Windows NT")) {
+ if (!ldb_dn_add_child_fmt(dn, "CN=Directory Service,CN=Windows NT,CN=Services")) {
talloc_free(dn);
return LDB_ERR_OPERATIONS_ERROR;
}
}
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;
+ int ret, i;
+ 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;
+ int ret, i;
+
+ 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)
+{
+ 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;
+ }
+
+ *_res = talloc_steal(mem_ctx, res);
+ talloc_free(tmp_ctx);
+
+ return LDB_SUCCESS;
+}