/*
ldb database library
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2010
Copyright (C) Andrew Tridgell 2005
Copyright (C) Simo Sorce 2006-2008
Copyright (C) Matthias Dieter Wallnöfer 2009
*/
#include "includes.h"
-#include "ldb_includes.h"
-#include "ldb_module.h"
+#include <ldb.h>
+#include <ldb_module.h>
#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
#include "param/param.h"
#include "dsdb/samdb/samdb.h"
#include "dsdb/samdb/ldb_modules/util.h"
#include "auth/auth.h"
-#include "libcli/security/dom_sid.h"
+#include "libcli/security/security.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
construct a canonical name from a message
*/
static int construct_canonical_name(struct ldb_module *module,
- struct ldb_message *msg)
+ struct ldb_message *msg, enum ldb_scope scope,
+ struct ldb_request *parent)
{
char *canonicalName;
canonicalName = ldb_dn_canonical_string(msg, msg->dn);
if (canonicalName == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ return ldb_operr(ldb_module_get_ctx(module));
}
return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
}
construct a primary group token for groups from a message
*/
static int construct_primary_group_token(struct ldb_module *module,
- struct ldb_message *msg)
+ struct ldb_message *msg, enum ldb_scope scope,
+ struct ldb_request *parent)
{
struct ldb_context *ldb;
uint32_t primary_group_token;
construct the token groups for SAM objects from a message
*/
static int construct_token_groups(struct ldb_module *module,
- struct ldb_message *msg)
+ struct ldb_message *msg, enum ldb_scope scope,
+ struct ldb_request *parent)
{
- struct ldb_context *ldb;
- const struct dom_sid *sid;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);;
+ TALLOC_CTX *tmp_ctx = talloc_new(msg);
+ unsigned int i;
+ int ret;
+ const char *filter;
- ldb = ldb_module_get_ctx(module);
+ NTSTATUS status;
- sid = samdb_result_dom_sid(msg, msg, "objectSid");
- if (sid != NULL) {
- NTSTATUS status;
- uint32_t prim_group_rid;
- struct dom_sid **sids = NULL;
- unsigned int i, num_sids = 0;
- int ret;
-
- prim_group_rid = samdb_result_uint(msg, "primaryGroupID", 0);
- if (prim_group_rid != 0) {
- struct dom_sid *prim_group_sid;
-
- prim_group_sid = dom_sid_add_rid(msg,
- samdb_domain_sid(ldb),
- prim_group_rid);
- if (prim_group_sid == NULL) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
+ struct dom_sid *primary_group_sid;
+ const char *primary_group_string;
+ const char *primary_group_dn;
+ DATA_BLOB primary_group_blob;
- /* onlyChilds = false, we want to consider also the
- * "primaryGroupID" for membership */
- status = authsam_expand_nested_groups(ldb,
- prim_group_sid,
- false, msg,
- &sids, &num_sids);
- if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- if (!NT_STATUS_IS_OK(status)) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
+ struct dom_sid *account_sid;
+ const char *account_sid_string;
+ const char *account_sid_dn;
+ DATA_BLOB account_sid_blob;
+ struct dom_sid **groupSIDs = NULL;
+ unsigned int num_groupSIDs = 0;
- for (i = 0; i < num_sids; i++) {
- ret = samdb_msg_add_dom_sid(ldb, msg, msg,
- "tokenGroups",
- sids[i]);
- if (ret != LDB_SUCCESS) {
- talloc_free(sids);
- return ret;
- }
- }
+ struct dom_sid *domain_sid;
- talloc_free(sids);
- }
+ if (scope != LDB_SCOPE_BASE) {
+ ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
- sids = NULL;
- num_sids = 0;
+ /* If it's not a user, it won't have a primaryGroupID */
+ if (ldb_msg_find_element(msg, "primaryGroupID") == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ }
- /* onlyChils = true, we don't want to have the SAM object itself
- * in the result */
- status = authsam_expand_nested_groups(ldb, sid, true, msg,
- &sids, &num_sids);
- if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- if (!NT_STATUS_IS_OK(status)) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
+ /* Ensure it has an objectSID too */
+ account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
+ if (account_sid == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ }
- for (i = 0; i < num_sids; i++) {
- ret = samdb_msg_add_dom_sid(ldb, msg, msg,
- "tokenGroups", sids[i]);
- if (ret != LDB_SUCCESS) {
- talloc_free(sids);
- return ret;
- }
- }
+ status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ primary_group_sid = dom_sid_add_rid(tmp_ctx,
+ domain_sid,
+ ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
+ if (!primary_group_sid) {
+ talloc_free(tmp_ctx);
+ return ldb_oom(ldb);
+ }
+
+ /* only return security groups */
+ filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u))",
+ GROUP_TYPE_SECURITY_ENABLED);
+ if (!filter) {
+ talloc_free(tmp_ctx);
+ return ldb_oom(ldb);
+ }
- talloc_free(sids);
+ primary_group_string = dom_sid_string(tmp_ctx, primary_group_sid);
+ if (!primary_group_string) {
+ talloc_free(tmp_ctx);
+ return ldb_oom(ldb);
+ }
+
+ primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string);
+ if (!primary_group_dn) {
+ talloc_free(tmp_ctx);
+ return ldb_oom(ldb);
+ }
+
+ primary_group_blob = data_blob_string_const(primary_group_dn);
+
+ account_sid_string = dom_sid_string(tmp_ctx, account_sid);
+ if (!account_sid_string) {
+ talloc_free(tmp_ctx);
+ return ldb_oom(ldb);
+ }
+
+ account_sid_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", account_sid_string);
+ if (!account_sid_dn) {
+ talloc_free(tmp_ctx);
+ return ldb_oom(ldb);
+ }
+
+ account_sid_blob = data_blob_string_const(account_sid_dn);
+
+ status = dsdb_expand_nested_groups(ldb, &account_sid_blob,
+ true, /* We don't want to add the object's SID itself,
+ it's not returend in this attribute */
+ filter,
+ tmp_ctx, &groupSIDs, &num_groupSIDs);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s",
+ account_sid_string, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Expands the primary group - this function takes in
+ * memberOf-like values, so we fake one up with the
+ * <SID=S-...> format of DN and then let it expand
+ * them, as long as they meet the filter - so only
+ * domain groups, not builtin groups
+ */
+ status = dsdb_expand_nested_groups(ldb, &primary_group_blob, false, filter,
+ tmp_ctx, &groupSIDs, &num_groupSIDs);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s",
+ account_sid_string, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0; i < num_groupSIDs; i++) {
+ ret = samdb_msg_add_dom_sid(ldb, msg, msg, "tokenGroups", groupSIDs[i]);
+ if (ret) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
}
return LDB_SUCCESS;
construct the parent GUID for an entry from a message
*/
static int construct_parent_guid(struct ldb_module *module,
- struct ldb_message *msg)
+ struct ldb_message *msg, enum ldb_scope scope,
+ struct ldb_request *parent)
{
- struct ldb_result *res;
+ struct ldb_result *res, *parent_res;
const struct ldb_val *parent_guid;
- const char *attrs[] = { "objectGUID", NULL };
+ const char *attrs[] = { "instanceType", NULL };
+ const char *attrs2[] = { "objectGUID", NULL };
+ uint32_t instanceType;
int ret;
+ struct ldb_dn *parent_dn;
struct ldb_val v;
- /* TODO: In the future, this needs to honour the partition boundaries */
- struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
+ /* determine if the object is NC by instance type */
+ ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_SEARCH_SHOW_RECYCLED, parent);
+
+ instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
+ "instanceType", 0);
+ talloc_free(res);
+ if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
+ DEBUG(4,(__location__ ": Object %s is NC\n",
+ ldb_dn_get_linearized(msg->dn)));
+ return LDB_SUCCESS;
+ }
+ parent_dn = ldb_dn_get_parent(msg, msg->dn);
if (parent_dn == NULL) {
DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
ldb_dn_get_linearized(msg->dn)));
return LDB_SUCCESS;
}
-
- ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
+ ret = dsdb_module_search_dn(module, msg, &parent_res, parent_dn, attrs2,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_SEARCH_SHOW_RECYCLED, parent);
talloc_free(parent_dn);
- /* if there is no parentGUID for this object, then return */
+
+ /* not NC, so the object should have a parent*/
if (ret == LDB_ERR_NO_SUCH_OBJECT) {
DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
ldb_dn_get_linearized(msg->dn)));
- return LDB_SUCCESS;
+ return ldb_operr(ldb_module_get_ctx(module));
} else if (ret != LDB_SUCCESS) {
return ret;
}
- parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
+ parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
if (!parent_guid) {
- talloc_free(res);
+ talloc_free(parent_res);
return LDB_SUCCESS;
}
- v = data_blob_dup_talloc(res, parent_guid);
+ v = data_blob_dup_talloc(parent_res, parent_guid);
if (!v.data) {
- talloc_free(res);
- return LDB_ERR_OPERATIONS_ERROR;
+ talloc_free(parent_res);
+ return ldb_oom(ldb_module_get_ctx(module));
}
ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
- talloc_free(res);
+ talloc_free(parent_res);
return ret;
}
construct a subSchemaSubEntry
*/
static int construct_subschema_subentry(struct ldb_module *module,
- struct ldb_message *msg)
+ struct ldb_message *msg, enum ldb_scope scope,
+ struct ldb_request *parent)
{
struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
char *subSchemaSubEntry;
- if (data && data->aggregate_dn) {
+
+ /* We may be being called before the init function has finished */
+ if (!data) {
+ return LDB_SUCCESS;
+ }
+
+ /* Try and set this value up, if possible. Don't worry if it
+ * fails, we may not have the DB set up yet, and it's not
+ * really vital anyway */
+ if (!data->aggregate_dn) {
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
+ }
+
+ if (data->aggregate_dn) {
subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
}
}
+static int construct_msds_isrodc_with_dn(struct ldb_module *module,
+ struct ldb_message *msg,
+ struct ldb_message_element *object_category)
+{
+ struct ldb_context *ldb;
+ struct ldb_dn *dn;
+ const struct ldb_val *val;
+
+ ldb = ldb_module_get_ctx(module);
+ if (!ldb) {
+ DEBUG(4, (__location__ ": Failed to get ldb \n"));
+ return ldb_operr(ldb);
+ }
+
+ dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
+ if (!dn) {
+ DEBUG(4, (__location__ ": Failed to create dn from %s \n",
+ (const char *)object_category->values[0].data));
+ return ldb_operr(ldb);
+ }
+
+ val = ldb_dn_get_rdn_val(dn);
+ if (!val) {
+ DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
+ ldb_dn_get_linearized(dn)));
+ return ldb_operr(ldb);
+ }
+
+ if (strequal((const char *)val->data, "NTDS-DSA")) {
+ ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
+ } else {
+ ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
+ }
+ return LDB_SUCCESS;
+}
+
+static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
+ struct ldb_message *msg,
+ struct ldb_dn *dn,
+ struct ldb_request *parent)
+{
+ struct ldb_dn *server_dn;
+ const char *attr_obj_cat[] = { "objectCategory", NULL };
+ struct ldb_result *res;
+ struct ldb_message_element *object_category;
+ int ret;
+
+ server_dn = ldb_dn_copy(msg, dn);
+ if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
+ DEBUG(4, (__location__ ": Failed to add child to %s \n",
+ ldb_dn_get_linearized(server_dn)));
+ return ldb_operr(ldb_module_get_ctx(module));
+ }
+
+ ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
+ DSDB_FLAG_NEXT_MODULE, parent);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
+ ldb_dn_get_linearized(server_dn)));
+ return LDB_SUCCESS;
+ } else if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
+ if (!object_category) {
+ DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
+ ldb_dn_get_linearized(res->msgs[0]->dn)));
+ return LDB_SUCCESS;
+ }
+ return construct_msds_isrodc_with_dn(module, msg, object_category);
+}
+
+static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
+ struct ldb_message *msg,
+ struct ldb_request *parent)
+{
+ struct ldb_context *ldb;
+ const char *attr[] = { "serverReferenceBL", NULL };
+ struct ldb_result *res;
+ int ret;
+ struct ldb_dn *server_dn;
+
+ ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attr,
+ DSDB_FLAG_NEXT_MODULE, parent);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
+ ldb_dn_get_linearized(msg->dn)));
+ return LDB_SUCCESS;
+ } else if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ldb = ldb_module_get_ctx(module);
+ if (!ldb) {
+ return LDB_SUCCESS;
+ }
+
+ server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
+ if (!server_dn) {
+ DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
+ ldb_dn_get_linearized(res->msgs[0]->dn)));
+ return LDB_SUCCESS;
+ }
+ return construct_msds_isrodc_with_server_dn(module, msg, server_dn, parent);
+}
+
+/*
+ construct msDS-isRODC attr
+*/
+static int construct_msds_isrodc(struct ldb_module *module,
+ struct ldb_message *msg, enum ldb_scope scope,
+ struct ldb_request *parent)
+{
+ struct ldb_message_element * object_class;
+ struct ldb_message_element * object_category;
+ unsigned int i;
+
+ object_class = ldb_msg_find_element(msg, "objectClass");
+ if (!object_class) {
+ DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
+ ldb_dn_get_linearized(msg->dn)));
+ return ldb_operr(ldb_module_get_ctx(module));
+ }
+
+ for (i=0; i<object_class->num_values; i++) {
+ if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
+ /* If TO!objectCategory equals the DN of the classSchema object for the nTDSDSA
+ * object class, then TO!msDS-isRODC is false. Otherwise, TO!msDS-isRODC is true.
+ */
+ object_category = ldb_msg_find_element(msg, "objectCategory");
+ if (!object_category) {
+ DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
+ ldb_dn_get_linearized(msg->dn)));
+ return LDB_SUCCESS;
+ }
+ return construct_msds_isrodc_with_dn(module, msg, object_category);
+ }
+ if (strequal((const char*)object_class->values[i].data, "server")) {
+ /* Let TN be the nTDSDSA object whose DN is "CN=NTDS Settings," prepended to
+ * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA object" case,
+ * substituting TN for TO.
+ */
+ return construct_msds_isrodc_with_server_dn(module, msg, msg->dn, parent);
+ }
+ if (strequal((const char*)object_class->values[i].data, "computer")) {
+ /* Let TS be the server object named by TO!serverReferenceBL. Apply the previous
+ * rule for the "TO is a server object" case, substituting TS for TO.
+ */
+ return construct_msds_isrodc_with_computer_dn(module, msg, parent);
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+
+/*
+ construct msDS-keyVersionNumber attr
+
+ TODO: Make this based on the 'win2k' DS huristics bit...
+
+*/
+static int construct_msds_keyversionnumber(struct ldb_module *module,
+ struct ldb_message *msg,
+ enum ldb_scope scope,
+ struct ldb_request *parent)
+{
+ uint32_t i;
+ enum ndr_err_code ndr_err;
+ const struct ldb_val *omd_value;
+ struct replPropertyMetaDataBlob *omd;
+ int ret;
+
+ omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
+ if (!omd_value) {
+ /* We can't make up a key version number without meta data */
+ return LDB_SUCCESS;
+ }
+ if (!omd_value) {
+ return LDB_SUCCESS;
+ }
+
+ omd = talloc(msg, struct replPropertyMetaDataBlob);
+ if (!omd) {
+ ldb_module_oom(module);
+ return LDB_SUCCESS;
+ }
+
+ ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
+ (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
+ ldb_dn_get_linearized(msg->dn)));
+ return ldb_operr(ldb_module_get_ctx(module));
+ }
+
+ if (omd->version != 1) {
+ DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
+ omd->version, ldb_dn_get_linearized(msg->dn)));
+ talloc_free(omd);
+ return LDB_SUCCESS;
+ }
+ for (i=0; i<omd->ctr.ctr1.count; i++) {
+ if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
+ ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
+ msg, msg,
+ "msDS-KeyVersionNumber",
+ omd->ctr.ctr1.array[i].version);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(omd);
+ return ret;
+ }
+ break;
+ }
+ }
+ return LDB_SUCCESS;
+
+}
+
+struct op_controls_flags {
+ bool sd;
+ bool bypassoperational;
+};
+
+static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
+ if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
+ return true;
+ }
+ return false;
+}
+
/*
a list of attribute names that should be substituted in the parse
tree before the search is done
const char *attr;
const char *replace;
const char *extra_attr;
- int (*constructor)(struct ldb_module *, struct ldb_message *);
+ int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope, struct ldb_request *);
} search_sub[] = {
{ "createTimestamp", "whenCreated", NULL , NULL },
{ "modifyTimestamp", "whenChanged", NULL , NULL },
- { "structuralObjectClass", "objectClass", NULL , NULL },
- { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
+ { "structuralObjectClass", NULL, NULL , NULL },
+ { "canonicalName", NULL, NULL , construct_canonical_name },
{ "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
- { "tokenGroups", "objectSid", "primaryGroupID", construct_token_groups },
+ { "tokenGroups", "primaryGroupID", "objectSid", construct_token_groups },
{ "parentGUID", NULL, NULL, construct_parent_guid },
- { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry }
+ { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
+ { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
+ { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
};
enum op_remove {
OPERATIONAL_REMOVE_ALWAYS, /* remove always */
OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
- OPERATIONAL_SD_FLAGS /* show if SD_FLAGS_OID set, or asked for */
+ OPERATIONAL_SD_FLAGS, /* show if SD_FLAGS_OID set, or asked for */
+ OPERATIONAL_REMOVE_UNLESS_CONTROL /* remove always unless an adhoc control has been specified */
};
/*
a list of attributes that may need to be removed from the
underlying db return
+
+ Some of these are attributes that were once stored, but are now calculated
*/
static const struct {
const char *attr;
enum op_remove op;
} operational_remove[] = {
{ "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
+ { "msDS-KeyVersionNumber", OPERATIONAL_REMOVE_UNLESS_CONTROL },
{ "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
{ "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
- { "unicodePwd", OPERATIONAL_REMOVE_UNASKED },
- { "dBCSPwd", OPERATIONAL_REMOVE_UNASKED },
- { "ntPwdHistory", OPERATIONAL_REMOVE_UNASKED },
- { "lmPwdHistory", OPERATIONAL_REMOVE_UNASKED },
- { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
+#define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
+ { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
};
*/
static int operational_search_post_process(struct ldb_module *module,
struct ldb_message *msg,
- const char * const *attrs,
- bool sd_flags_set)
+ enum ldb_scope scope,
+ const char * const *attrs_from_user,
+ const char * const *attrs_searched_for,
+ struct op_controls_flags* controls_flags,
+ struct ldb_request *parent)
{
struct ldb_context *ldb;
unsigned int i, a = 0;
+ bool constructed_attributes = false;
ldb = ldb_module_get_ctx(module);
for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
switch (operational_remove[i].op) {
case OPERATIONAL_REMOVE_UNASKED:
- if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
+ if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
+ continue;
+ }
+ if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
continue;
}
case OPERATIONAL_REMOVE_ALWAYS:
ldb_msg_remove_attr(msg, operational_remove[i].attr);
break;
+ case OPERATIONAL_REMOVE_UNLESS_CONTROL:
+ if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
+ ldb_msg_remove_attr(msg, operational_remove[i].attr);
+ break;
+ } else {
+ continue;
+ }
case OPERATIONAL_SD_FLAGS:
- if (sd_flags_set ||
- ldb_attr_in_list(attrs, operational_remove[i].attr)) {
+ if (controls_flags->sd ||
+ ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
continue;
}
ldb_msg_remove_attr(msg, operational_remove[i].attr);
}
}
- for (a=0;attrs && attrs[a];a++) {
+ for (a=0;attrs_from_user && attrs_from_user[a];a++) {
+ if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
+ continue;
+ }
for (i=0;i<ARRAY_SIZE(search_sub);i++) {
- if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
+ if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
continue;
}
/* construct the new attribute, using either a supplied
constructor or a simple copy */
+ constructed_attributes = true;
if (search_sub[i].constructor != NULL) {
- if (search_sub[i].constructor(module, msg) != LDB_SUCCESS) {
+ if (search_sub[i].constructor(module, msg, scope, parent) != LDB_SUCCESS) {
goto failed;
}
} else if (ldb_msg_copy_attr(msg,
- search_sub[i].replace,
- search_sub[i].attr) != LDB_SUCCESS) {
+ search_sub[i].attr,
+ search_sub[i].replace) != LDB_SUCCESS) {
goto failed;
}
+ }
+ }
- /* remove the added search attribute, unless it was
- asked for by the user */
+ /* Deletion of the search helper attributes are needed if:
+ * - we generated constructed attributes and
+ * - we aren't requesting all attributes
+ */
+ if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
+ for (i=0;i<ARRAY_SIZE(search_sub);i++) {
+ /* remove the added search helper attributes, unless
+ * they were asked for by the user */
if (search_sub[i].replace != NULL &&
- !ldb_attr_in_list(attrs, search_sub[i].replace) &&
- !ldb_attr_in_list(attrs, "*")) {
+ !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
ldb_msg_remove_attr(msg, search_sub[i].replace);
}
if (search_sub[i].extra_attr != NULL &&
- !ldb_attr_in_list(attrs, search_sub[i].extra_attr) &&
- !ldb_attr_in_list(attrs, "*")) {
+ !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
}
}
failed:
ldb_debug_set(ldb, LDB_DEBUG_WARNING,
- "operational_search_post_process failed for attribute '%s'",
- attrs[a]);
+ "operational_search_post_process failed for attribute '%s' - %s",
+ attrs_from_user[a], ldb_errstring(ldb));
return -1;
}
-
/*
hook search operations
*/
struct operational_context {
struct ldb_module *module;
struct ldb_request *req;
-
+ enum ldb_scope scope;
const char * const *attrs;
- bool sd_flags_set;
+ struct op_controls_flags* controls_flags;
};
static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
attributes that have been asked for */
ret = operational_search_post_process(ac->module,
ares->message,
+ ac->scope,
ac->attrs,
- ac->sd_flags_set);
+ req->op.search.attrs,
+ ac->controls_flags, req);
if (ret != 0) {
return ldb_module_done(ac->req, NULL, NULL,
LDB_ERR_OPERATIONS_ERROR);
unsigned int i, a;
int ret;
+ /* There are no operational attributes on special DNs */
+ if (ldb_dn_is_special(req->op.search.base)) {
+ return ldb_next_request(module, req);
+ }
+
ldb = ldb_module_get_ctx(module);
ac = talloc(req, struct operational_context);
if (ac == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ return ldb_oom(ldb);
}
ac->module = module;
ac->req = req;
+ ac->scope = req->op.search.scope;
ac->attrs = req->op.search.attrs;
/* FIXME: We must copy the tree and keep the original
parse_tree_sub[i].replace);
}
+ ac->controls_flags = talloc(ac, struct op_controls_flags);
+ /* remember if the SD_FLAGS_OID was set */
+ ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
+ /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
+ ac->controls_flags->bypassoperational =
+ (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
+
/* in the list of attributes we are looking for, rename any
attributes to the alias for any hidden attributes that can
be fetched directly using non-hidden names */
for (a=0;ac->attrs && ac->attrs[a];a++) {
+ if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
+ continue;
+ }
for (i=0;i<ARRAY_SIZE(search_sub);i++) {
if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
search_sub[i].replace) {
: ac->attrs,
search_sub[i].extra_attr);
if (search_attrs2 == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ return ldb_operr(ldb);
}
/* may be NULL, talloc_free() doesn't mind */
talloc_free(search_attrs);
if (!search_attrs) {
search_attrs = ldb_attr_list_copy(req, ac->attrs);
if (search_attrs == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ return ldb_operr(ldb);
}
}
/* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
}
}
- /* remember if the SD_FLAGS_OID was set */
- ac->sd_flags_set = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
-
ret = ldb_build_search_req_ex(&down_req, ldb, ac,
req->op.search.base,
req->op.search.scope,
req->controls,
ac, operational_callback,
req);
+ LDB_REQ_SET_LOCATION(down_req);
if (ret != LDB_SUCCESS) {
- return LDB_ERR_OPERATIONS_ERROR;
+ return ldb_operr(ldb);
}
/* perform the search */
static int operational_init(struct ldb_module *ctx)
{
struct operational_data *data;
- struct ldb_context *ldb = ldb_module_get_ctx(ctx);
- int ret = ldb_next_init(ctx);
+ int ret;
+ auth_init();
+
+ ret = ldb_next_init(ctx);
if (ret != LDB_SUCCESS) {
return ret;
}
- data = talloc(ctx, struct operational_data);
+ data = talloc_zero(ctx, struct operational_data);
if (!data) {
- ldb_module_oom(ctx);
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
- if (!data->aggregate_dn) {
- ldb_set_errstring(ldb, "Could not build aggregate schema DN");
- return LDB_ERR_OPERATIONS_ERROR;
+ return ldb_module_oom(ctx);
}
ldb_module_set_private(ctx, data);
return LDB_SUCCESS;
}
-const struct ldb_module_ops ldb_operational_module_ops = {
+static const struct ldb_module_ops ldb_operational_module_ops = {
.name = "operational",
.search = operational_search,
.init_context = operational_init
};
+
+int ldb_operational_module_init(const char *version)
+{
+ LDB_MODULE_CHECK_VERSION(version);
+ return ldb_register_module(&ldb_operational_module_ops);
+}