diff options
Diffstat (limited to 'source4/dsdb/samdb/ldb_modules/extended_dn.c')
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/extended_dn.c | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn.c b/source4/dsdb/samdb/ldb_modules/extended_dn.c new file mode 100644 index 0000000000..84bf5e4843 --- /dev/null +++ b/source4/dsdb/samdb/ldb_modules/extended_dn.c @@ -0,0 +1,369 @@ +/* + ldb database library + + Copyright (C) Simo Sorce 2005 + + ** NOTE! The following LGPL license applies to the ldb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + 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, + 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/>. +*/ + +/* + * Name: ldb + * + * Component: ldb extended dn control module + * + * Description: this module builds a special dn + * + * Author: Simo Sorce + */ + +#include "includes.h" +#include "ldb/include/ldb.h" +#include "ldb/include/ldb_errors.h" +#include "ldb/include/ldb_private.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "dsdb/samdb/samdb.h" +#include "libcli/security/security.h" + +#include <time.h> + +static bool is_attr_in_list(const char * const * attrs, const char *attr) +{ + int i; + + for (i = 0; attrs[i]; i++) { + if (strcasecmp(attrs[i], attr) == 0) + return true; + } + + return false; +} + +static char **copy_attrs(void *mem_ctx, const char * const * attrs) +{ + char **new; + int i, num; + + for (num = 0; attrs[num]; num++); + + new = talloc_array(mem_ctx, char *, num + 1); + if (!new) return NULL; + + for(i = 0; i < num; i++) { + new[i] = talloc_strdup(new, attrs[i]); + if (!new[i]) { + talloc_free(new); + return NULL; + } + } + new[i] = NULL; + + return new; +} + +static bool add_attrs(void *mem_ctx, char ***attrs, const char *attr) +{ + char **new; + int num; + + for (num = 0; (*attrs)[num]; num++); + + new = talloc_realloc(mem_ctx, *attrs, char *, num + 2); + if (!new) return false; + + *attrs = new; + + new[num] = talloc_strdup(new, attr); + if (!new[num]) return false; + + new[num + 1] = NULL; + + return true; +} + +static bool inject_extended_dn(struct ldb_message *msg, + struct ldb_context *ldb, + int type, + bool remove_guid, + bool remove_sid) +{ + 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"); + + if (!guid_blob) + return false; + + 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 false; + } + 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 false; + } + 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 false; + + } + + /* 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 false; + } + + if (!new_dn) { + return false; + } + + if (remove_guid) { + ldb_msg_remove_attr(msg, "objectGUID"); + } + + if (sid_blob && remove_sid) { + ldb_msg_remove_attr(msg, "objectSID"); + } + + msg->dn = ldb_dn_new(msg, ldb, new_dn); + if (! ldb_dn_validate(msg->dn)) + return false; + + val = ldb_msg_find_ldb_val(msg, "distinguishedName"); + if (val) { + ldb_msg_remove_attr(msg, "distinguishedName"); + if (ldb_msg_add_steal_string(msg, "distinguishedName", new_dn)) + return false; + } + + return true; +} + +/* search */ +struct extended_context { + + struct ldb_module *module; + void *up_context; + int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *); + + const char * const *attrs; + bool remove_guid; + bool remove_sid; + int extended_type; +}; + +static int extended_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) +{ + struct extended_context *ac; + + ac = talloc_get_type(context, struct extended_context); + + if (ares->type == LDB_REPLY_ENTRY) { + /* for each record returned post-process to add any derived + attributes that have been asked for */ + if (!inject_extended_dn(ares->message, ldb, ac->extended_type, ac->remove_guid, ac->remove_sid)) { + goto error; + } + } + + return ac->up_callback(ldb, ac->up_context, ares); + +error: + talloc_free(ares); + return LDB_ERR_OPERATIONS_ERROR; +} + +static int extended_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 ldb_request *down_req; + char **new_attrs; + int ret; + + /* check if there's an extended dn control */ + control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID); + if (control == NULL) { + /* not found go on */ + return ldb_next_request(module, req); + } + + if (control->data) { + extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control); + if (!extended_ctrl) { + return LDB_ERR_PROTOCOL_ERROR; + } + } + + ac = talloc(req, struct extended_context); + if (ac == NULL) { + ldb_oom(module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + ac->module = module; + ac->up_context = req->context; + ac->up_callback = req->callback; + ac->attrs = req->op.search.attrs; + ac->remove_guid = false; + ac->remove_sid = false; + if (extended_ctrl) { + ac->extended_type = extended_ctrl->type; + } else { + ac->extended_type = 0; + } + + down_req = talloc_zero(req, struct ldb_request); + if (down_req == NULL) { + ldb_oom(module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + down_req->operation = req->operation; + down_req->op.search.base = req->op.search.base; + down_req->op.search.scope = req->op.search.scope; + down_req->op.search.tree = req->op.search.tree; + + /* check if attrs only is specified, in that case check wether we need to modify them */ + if (req->op.search.attrs) { + if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) { + ac->remove_guid = true; + } + if (! is_attr_in_list(req->op.search.attrs, "objectSID")) { + ac->remove_sid = true; + } + if (ac->remove_guid || ac->remove_sid) { + new_attrs = copy_attrs(down_req, req->op.search.attrs); + if (new_attrs == NULL) { + ldb_oom(module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (ac->remove_guid) { + if (!add_attrs(down_req, &new_attrs, "objectGUID")) + return LDB_ERR_OPERATIONS_ERROR; + } + if (ac->remove_sid) { + if (!add_attrs(down_req, &new_attrs, "objectSID")) + return LDB_ERR_OPERATIONS_ERROR; + } + + down_req->op.search.attrs = (const char * const *)new_attrs; + } + } + + down_req->controls = req->controls; + + /* save it locally and remove it from the list */ + /* we do not need to replace them later as we + * are keeping the original req intact */ + if (!save_controls(control, down_req, &saved_controls)) { + return LDB_ERR_OPERATIONS_ERROR; + } + + down_req->context = ac; + down_req->callback = extended_callback; + ldb_set_timeout_from_prev_req(module->ldb, req, down_req); + + /* perform the search */ + ret = ldb_next_request(module, down_req); + + /* do not free down_req as the call results may be linked to it, + * it will be freed when the upper level request get freed */ + if (ret == LDB_SUCCESS) { + req->handle = down_req->handle; + } + + return ret; +} + +static int extended_init(struct ldb_module *module) +{ + struct ldb_request *req; + int ret; + + req = talloc(module, struct ldb_request); + if (req == NULL) { + ldb_oom(module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + req->operation = LDB_REQ_REGISTER_CONTROL; + req->op.reg_control.oid = LDB_CONTROL_EXTENDED_DN_OID; + req->controls = NULL; + + ret = ldb_request(module->ldb, req); + if (ret != LDB_SUCCESS) { + ldb_debug(module->ldb, LDB_DEBUG_ERROR, "extended_dn: Unable to register control with rootdse!\n"); + talloc_free(req); + return LDB_ERR_OPERATIONS_ERROR; + } + + talloc_free(req); + return ldb_next_init(module); +} + +_PUBLIC_ const struct ldb_module_ops ldb_extended_dn_module_ops = { + .name = "extended_dn", + .search = extended_search, + .init_context = extended_init +}; |