ldb database library
Copyright (C) Simo Sorce 2005-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2008
- ** NOTE! The following LGPL license applies to the ldb
- ** library. This does NOT imply that all of Samba is released
- ** under the LGPL
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
+ This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
*
* Component: ldb extended dn control module
*
- * Description: this module builds a special dn
+ * Description: this module builds a special dn for returned search
+ * results nad creates the special DN in the backend store for new
+ * values.
*
- * Author: Simo Sorce
+ * Authors: Simo Sorce
+ * Andrew Bartlett
*/
#include "includes.h"
#include <time.h>
+struct extended_dn_replace_list {
+ struct extended_dn_replace_list *next;
+ struct ldb_dn *dn;
+ TALLOC_CTX *mem_ctx;
+ struct ldb_val *replace_dn;
+ struct extended_dn_context *ac;
+ struct ldb_request *search_req;
+};
+
+
+struct extended_dn_context {
+ const struct dsdb_schema *schema;
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct extended_dn_replace_list *ops;
+ struct extended_dn_replace_list *cur;
+};
+
+
+static struct extended_dn_context *extended_dn_context_init(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct extended_dn_context *ac;
+
+ ac = talloc_zero(req, struct extended_dn_context);
+ if (ac == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ return NULL;
+ }
+
+ ac->schema = dsdb_get_schema(module->ldb);
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
static bool is_attr_in_list(const char * const * attrs, const char *attr)
{
int i;
return true;
}
-static int inject_extended_dn(struct ldb_message *msg,
- struct ldb_context *ldb,
- int type,
- bool remove_guid,
- bool remove_sid)
+static int inject_extended_dn(struct ldb_reply *ares,
+ struct ldb_context *ldb,
+ int type,
+ bool remove_guid,
+ bool remove_sid)
{
+ int ret;
const struct ldb_val *val;
- struct GUID guid;
- struct dom_sid *sid;
const DATA_BLOB *guid_blob;
const DATA_BLOB *sid_blob;
- char *object_guid;
- char *object_sid;
- char *new_dn;
- guid_blob = ldb_msg_find_ldb_val(msg, "objectGUID");
- sid_blob = ldb_msg_find_ldb_val(msg, "objectSID");
+ guid_blob = ldb_msg_find_ldb_val(ares->message, "objectGUID");
+ sid_blob = ldb_msg_find_ldb_val(ares->message, "objectSID");
if (!guid_blob) {
return LDB_ERR_OPERATIONS_ERROR;
}
- switch (type) {
- case 0:
- /* return things in hexadecimal format */
- if (sid_blob) {
- const char *lower_guid_hex = strlower_talloc(msg, data_blob_hex_string(msg, guid_blob));
- const char *lower_sid_hex = strlower_talloc(msg, data_blob_hex_string(msg, sid_blob));
- if (!lower_guid_hex || !lower_sid_hex) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
- new_dn = talloc_asprintf(msg, "<GUID=%s>;<SID=%s>;%s",
- lower_guid_hex,
- lower_sid_hex,
- ldb_dn_get_linearized(msg->dn));
- } else {
- const char *lower_guid_hex = strlower_talloc(msg, data_blob_hex_string(msg, guid_blob));
- if (!lower_guid_hex) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
- new_dn = talloc_asprintf(msg, "<GUID=%s>;%s",
- lower_guid_hex,
- ldb_dn_get_linearized(msg->dn));
- }
-
- break;
- case 1:
- /* retrieve object_guid */
- guid = samdb_result_guid(msg, "objectGUID");
- object_guid = GUID_string(msg, &guid);
-
- /* retrieve object_sid */
- object_sid = NULL;
- sid = samdb_result_dom_sid(msg, msg, "objectSID");
- if (sid) {
- object_sid = dom_sid_string(msg, sid);
- if (!object_sid)
- return LDB_ERR_OPERATIONS_ERROR;
-
- }
-
- /* Normal, sane format */
- if (object_sid) {
- new_dn = talloc_asprintf(msg, "<GUID=%s>;<SID=%s>;%s",
- object_guid, object_sid,
- ldb_dn_get_linearized(msg->dn));
- } else {
- new_dn = talloc_asprintf(msg, "<GUID=%s>;%s",
- object_guid,
- ldb_dn_get_linearized(msg->dn));
- }
- break;
- default:
- return LDB_ERR_OPERATIONS_ERROR;
+ ret = ldb_dn_set_extended_component(ares->message->dn, "GUID", guid_blob);
+ if (ret != LDB_SUCCESS) {
+ return ret;
}
-
- if (!new_dn) {
- return LDB_ERR_OPERATIONS_ERROR;
+ if (sid_blob) {
+ ret = ldb_dn_set_extended_component(ares->message->dn, "SID", sid_blob);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
}
if (remove_guid) {
- ldb_msg_remove_attr(msg, "objectGUID");
+ ldb_msg_remove_attr(ares->message, "objectGUID");
}
if (sid_blob && remove_sid) {
- ldb_msg_remove_attr(msg, "objectSID");
+ ldb_msg_remove_attr(ares->message, "objectSID");
}
- msg->dn = ldb_dn_new(msg, ldb, new_dn);
- if (! ldb_dn_validate(msg->dn))
- return LDB_ERR_OPERATIONS_ERROR;
-
- val = ldb_msg_find_ldb_val(msg, "distinguishedName");
+ val = ldb_msg_find_ldb_val(ares->message, "distinguishedName");
if (val) {
- ldb_msg_remove_attr(msg, "distinguishedName");
- if (ldb_msg_add_steal_string(msg, "distinguishedName", new_dn))
+ ldb_msg_remove_attr(ares->message, "distinguishedName");
+ ret = ldb_msg_add_steal_string(ares->message, "distinguishedName",
+ ldb_dn_extended_linearized(ares->message, ares->message->dn, type));
+ if (ret != LDB_SUCCESS) {
return LDB_ERR_OPERATIONS_ERROR;
+ }
}
-
return LDB_SUCCESS;
}
/* search */
-struct extended_context {
-
+struct extended_search_context {
struct ldb_module *module;
+ const struct dsdb_schema *schema;
struct ldb_request *req;
struct ldb_control *control;
struct ldb_dn *basedn;
static int extended_callback(struct ldb_request *req, struct ldb_reply *ares)
{
- struct extended_context *ac;
- int ret;
+ struct extended_search_context *ac;
+ int ret, i, j;
+ struct ldb_message *msg = ares->message;
- ac = talloc_get_type(req->context, struct extended_context);
+ ac = talloc_get_type(req->context, struct extended_search_context);
if (!ares) {
return ldb_module_done(ac->req, NULL, NULL,
}
switch (ares->type) {
- case LDB_REPLY_ENTRY:
- if (ac->inject) {
- /* for each record returned post-process to add any derived
- attributes that have been asked for */
- ret = inject_extended_dn(ares->message, ac->module->ldb,
- ac->extended_type, ac->remove_guid,
- ac->remove_sid);
- if (ret != LDB_SUCCESS) {
- return ldb_module_done(ac->req, NULL, NULL, ret);
- }
- }
-
- return ldb_module_send_entry(ac->req, ares->message);
-
case LDB_REPLY_REFERRAL:
return ldb_module_send_referral(ac->req, ares->referral);
case LDB_REPLY_DONE:
return ldb_module_done(ac->req, ares->controls,
ares->response, LDB_SUCCESS);
+ case LDB_REPLY_ENTRY:
+ break;
+ }
+ if (ac->inject) {
+ /* for each record returned post-process to add any derived
+ attributes that have been asked for */
+ ret = inject_extended_dn(ares, ac->module->ldb,
+ ac->extended_type, ac->remove_guid,
+ ac->remove_sid);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
}
- return LDB_SUCCESS;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(ac->schema, msg->elements[i].name);
+ if (!attribute) {
+ continue;
+ }
+ /* Look to see if this attributeSyntax is a DN */
+ if (!((strcmp(attribute->attributeSyntax_oid, "2.5.5.1") == 0) ||
+ (strcmp(attribute->attributeSyntax_oid, "2.5.5.7") == 0))) {
+ continue;
+ }
+ for (j = 0; j < msg->elements[i].num_values; j++) {
+ const char *dn_str;
+ struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, ac->module->ldb, &msg->elements[i].values[j]);
+ if (!dn) {
+ return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (!ac->inject) {
+ dn_str = talloc_steal(msg->elements[i].values,
+ ldb_dn_get_linearized(dn));
+ } else {
+ dn_str = talloc_steal(msg->elements[i].values,
+ ldb_dn_extended_linearized(msg->elements[i].values,
+ dn, ac->extended_type));
+ }
+ msg->elements[i].values[j] = data_blob_string_const(dn_str);
+ talloc_free(dn);
+ }
+ }
+ return ldb_module_send_entry(ac->req, msg);
}
static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
{
- struct extended_context *ac;
+ struct extended_search_context *ac;
struct ldb_request *down_req;
struct ldb_control **saved_controls;
struct ldb_message_element *el;
char *valstr = NULL;
const char *found = NULL;
- ac = talloc_get_type(req->context, struct extended_context);
+ ac = talloc_get_type(req->context, struct extended_search_context);
if (!ares) {
return ldb_module_done(ac->req, NULL, NULL,
return LDB_SUCCESS;
}
-static int extended_search(struct ldb_module *module, struct ldb_request *req)
+static int extended_dn_search(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_control *control;
struct ldb_extended_dn_control *extended_ctrl = NULL;
struct ldb_control **saved_controls;
- struct extended_context *ac;
+ struct extended_search_context *ac;
struct ldb_request *down_req;
char **new_attrs;
int ret;
NULL
};
- if (ldb_dn_is_special(req->op.search.base)) {
- char *dn;
-
- dn = ldb_dn_alloc_linearized(req, req->op.search.base);
- if (!dn) {
- ldb_oom(module->ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- if (strncasecmp(dn, "<SID=", 5) == 0) {
- char *str;
- char *valstr;
- char *p;
-
- p = strchr(dn, '=');
- if (!p) {
- return LDB_ERR_INVALID_DN_SYNTAX;
- }
-
- p[0] = '\0';
- p++;
+ if (ldb_dn_has_extended(req->op.search.base)) {
+ const struct ldb_val *sid_val, *guid_val, *wkguid_val;
+ struct ldb_dn *dn = req->op.search.base;
- str = p;
-
- p = strchr(str, '>');
- if (!p) {
- return LDB_ERR_INVALID_DN_SYNTAX;
- }
- p[0] = '\0';
-
- if (strncasecmp(str, "S-", 2) == 0) {
- valstr = str;
- } else {
- DATA_BLOB binary;
- binary = strhex_to_data_blob(NULL, str);
- if (!binary.data) {
- ldb_oom(module->ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- valstr = ldb_binary_encode(req, binary);
- data_blob_free(&binary);
- if (!valstr) {
- ldb_oom(module->ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- }
+ sid_val = ldb_dn_get_extended_component(dn, "SID");
+ guid_val = ldb_dn_get_extended_component(dn, "GUID");
+ wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
+ if (sid_val) {
/* TODO: do a search over all partitions */
base_dn = ldb_get_default_basedn(module->ldb);
- base_dn_filter = talloc_asprintf(req, "(objectSid=%s)", valstr);
+ base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
+ ldb_binary_encode(req, *sid_val));
if (!base_dn_filter) {
ldb_oom(module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
base_dn_scope = LDB_SCOPE_SUBTREE;
base_dn_attrs = dnattr;
- } else if (strncasecmp(dn, "<GUID=", 6) == 0) {
- char *str;
- char *valstr;
- char *p;
-
- p = strchr(dn, '=');
- if (!p) {
- return LDB_ERR_INVALID_DN_SYNTAX;
- }
-
- p[0] = '\0';
- p++;
-
- str = p;
-
- p = strchr(str, '>');
- if (!p) {
- return LDB_ERR_INVALID_DN_SYNTAX;
- }
- p[0] = '\0';
- if (strchr(str, '-')) {
- valstr = str;
- } else {
- DATA_BLOB binary;
- binary = strhex_to_data_blob(NULL, str);
- if (!binary.data) {
- ldb_oom(module->ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- valstr = ldb_binary_encode(req, binary);
- data_blob_free(&binary);
- if (!valstr) {
- ldb_oom(module->ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- }
+ } else if (guid_val) {
/* TODO: do a search over all partitions */
base_dn = ldb_get_default_basedn(module->ldb);
- base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)", valstr);
+ base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
+ ldb_binary_encode(req, *guid_val));
if (!base_dn_filter) {
ldb_oom(module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
base_dn_scope = LDB_SCOPE_SUBTREE;
base_dn_attrs = dnattr;
- } else if (strncasecmp(dn, "<WKGUID=", 8) == 0) {
+
+
+ } else if (wkguid_val) {
+ char *wkguid_dup;
char *tail_str;
char *p;
- p = strchr(dn, ',');
+ wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
+
+ p = strchr(wkguid_dup, ',');
if (!p) {
return LDB_ERR_INVALID_DN_SYNTAX;
}
p[0] = '\0';
p++;
- wellknown_object = talloc_asprintf(req, "B:32:%s:", &dn[8]);
+ wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
if (!wellknown_object) {
ldb_oom(module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
p[0] = '\0';
base_dn = ldb_dn_new(req, module->ldb, tail_str);
+ talloc_free(wkguid_dup);
if (!base_dn) {
ldb_oom(module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
base_dn_scope = LDB_SCOPE_BASE;
base_dn_attrs = wkattr;
}
- talloc_free(dn);
}
/* check if there's an extended dn control */
control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
- if (control == NULL && base_dn_filter == NULL) {
- /* not found go on */
- return ldb_next_request(module, req);
- }
-
if (control && control->data) {
extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
if (!extended_ctrl) {
}
}
- ac = talloc_zero(req, struct extended_context);
+ ac = talloc_zero(req, struct extended_search_context);
if (ac == NULL) {
ldb_oom(module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
ac->module = module;
+ ac->schema = dsdb_get_schema(module->ldb);
ac->req = req;
ac->control = control;
ac->basedn = NULL;
ac->remove_guid = false;
ac->remove_sid = false;
+ if (!ac->schema) {
+ /* no schema yet? go on */
+ talloc_free(ac);
+ return ldb_next_request(module, req);
+ }
+
if (control) {
ac->inject = true;
if (extended_ctrl) {
}
}
+ /* If the base DN was an extended DN (perhaps a well known
+ * GUID) then re-create the search */
if (base_dn) {
ret = ldb_build_search_req(&down_req,
module->ldb, ac,
return ldb_next_request(module, down_req);
}
-static int extended_init(struct ldb_module *module)
+static int extended_replace_dn(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct extended_dn_replace_list *os = talloc_get_type(req->context,
+ struct extended_dn_replace_list);
+
+ if (!ares) {
+ return ldb_module_done(os->ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(os->ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* Only entries are interesting, and we only want the olddn */
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ {
+ const struct ldb_val *sid = ldb_msg_find_ldb_val(ares->message, "objectSid");
+ const struct ldb_val *guid = ldb_msg_find_ldb_val(ares->message, "objectGUID");
+ struct ldb_dn *dn = ares->message->dn;
+ int ret = ldb_dn_compare(dn, req->op.search.base);
+ if (ret != 0) {
+ /* Guh? We only asked for this DN */
+ talloc_free(ares);
+ return ldb_module_done(os->ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ret = ldb_dn_set_extended_component(dn, "GUID", guid);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (sid) {
+ ret = ldb_dn_set_extended_component(dn, "SID", sid);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ *os->replace_dn = data_blob_string_const(
+ ldb_dn_extended_linearized(os->mem_ctx,
+ dn, 1));
+ if (os->replace_dn->data != NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+
+ /* Run the next search */
+
+ if (os->next) {
+ struct extended_dn_replace_list *next;
+
+ next = os->next;
+
+ talloc_free(os);
+
+ os = next;
+ return ldb_next_request(os->ac->module, next->search_req);
+ } else {
+ /* Otherwise, we are done - let's run the
+ * request now we have swapped the DNs for the
+ * full versions */
+ return ldb_next_request(os->ac->module, os->ac->req);
+ }
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+/* We have a 'normal' DN in the inbound request. We need to find out
+ * what the GUID and SID are on the DN it points to, so we can
+ * construct an extended DN for storage.
+ *
+ * This creates a list of DNs to look up, and the plain DN to replace
+ */
+
+static int extended_store_replace(struct extended_dn_context *ac,
+ TALLOC_CTX *callback_mem_ctx,
+ struct ldb_val *plain_dn)
+{
+ int ret;
+ struct extended_dn_replace_list *os;
+ static const char *attrs[] = {
+ "objectSid",
+ "objectGUID",
+ NULL
+ };
+
+ struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, ac->module->ldb, plain_dn);
+ if (!dn || !ldb_dn_validate(dn)) {
+ ldb_asprintf_errstring(ac->module->ldb,
+ "could not parse %.*s as a DN", (int)plain_dn->length, plain_dn->data);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ os = talloc_zero(ac, struct extended_dn_replace_list);
+ if (!os) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ os->ac = ac;
+
+ os->mem_ctx = callback_mem_ctx;
+
+ os->dn = talloc_steal(os, dn);
+
+ os->replace_dn = plain_dn;
+
+ if (!os->dn) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&os->search_req,
+ ac->module->ldb, ac->ops, dn, LDB_SCOPE_BASE, NULL,
+ attrs, NULL, os, extended_replace_dn,
+ ac->req);
+
+ if (ac->ops) {
+ ac->cur->next = os;
+ } else {
+ ac->ops = os;
+ }
+ ac->cur = os;
+
+ return LDB_SUCCESS;
+}
+
+
+/* add */
+static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct extended_dn_context *ac;
+ int ret;
+ int i, j;
+
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ ac = extended_dn_context_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!ac->schema) {
+ /* without schema, this doesn't make any sense */
+ talloc_free(ac);
+ return ldb_next_request(module, req);
+ }
+
+ for (i=0; i < req->op.add.message->num_elements; i++) {
+ const struct ldb_message_element *el = &req->op.add.message->elements[i];
+ const struct dsdb_attribute *schema_attr
+ = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
+ if (!schema_attr) {
+ ldb_asprintf_errstring(module->ldb,
+ "attribute %s is not a valid attribute in schema", el->name);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* We only setup an extended DN GUID on these particular DN objects */
+ if (!((strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") == 0) ||
+ (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.7") == 0))) {
+ continue;
+ }
+
+ for (j = 0; j < el->num_values; j++) {
+ ret = extended_store_replace(ac, req->op.add.message->elements, &el->values[j]);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ /* if DNs were set continue */
+ if (ac->ops == NULL) {
+ talloc_free(ac);
+ return ldb_next_request(module, req);
+ }
+
+ /* start with the searches */
+ return ldb_next_request(module, ac->ops->search_req);
+}
+
+/* modify */
+static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ /* Look over list of modifications */
+ /* Find if any are for linked attributes */
+ /* Determine the effect of the modification */
+ /* Apply the modify to the linked entry */
+
+ int i, j;
+ struct extended_dn_context *ac;
+ int ret;
+
+ if (ldb_dn_is_special(req->op.mod.message->dn)) {
+ /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ ac = extended_dn_context_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!ac->schema) {
+ /* without schema, this doesn't make any sense */
+ return ldb_next_request(module, req);
+ }
+
+ for (i=0; i < req->op.mod.message->num_elements; i++) {
+ const struct ldb_message_element *el = &req->op.mod.message->elements[i];
+ const struct dsdb_attribute *schema_attr
+ = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
+ if (!schema_attr) {
+ ldb_asprintf_errstring(module->ldb,
+ "attribute %s is not a valid attribute in schema", el->name);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* We only setup an extended DN GUID on these particular DN objects */
+ if (strcmp(schema_attr->attributeSyntax_oid, LDB_SYNTAX_DN) != 0 &&
+ strcmp(schema_attr->attributeSyntax_oid, "1.2.840.113556.1.4.903") != 0) {
+ continue;
+ }
+
+ switch (el->flags & LDB_FLAG_MOD_MASK) {
+ case LDB_FLAG_MOD_REPLACE:
+ case LDB_FLAG_MOD_ADD:
+
+ /* For each value being added, we need to setup the lookups to fill in the extended DN */
+ for (j = 0; j < el->num_values; j++) {
+ struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, module->ldb, &el->values[j]);
+ if (!dn || !ldb_dn_validate(dn)) {
+ ldb_asprintf_errstring(module->ldb,
+ "could not parse attribute %s as a DN", el->name);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ ret = extended_store_replace(ac, req->op.mod.message->elements, &el->values[j]);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ break;
+ }
+ }
+
+
+ return ret;
+}
+
+static int extended_dn_init(struct ldb_module *module)
{
int ret;
_PUBLIC_ const struct ldb_module_ops ldb_extended_dn_module_ops = {
.name = "extended_dn",
- .search = extended_search,
- .init_context = extended_init
+ .search = extended_dn_search,
+ .init_context = extended_dn_init,
+ .add = extended_dn_add,
+ .modify = extended_dn_modify,
};