X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source4%2Fdsdb%2Fsamdb%2Fldb_modules%2Futil.c;h=7f601afa33a60beb454211d46318e9dadb802e05;hb=d40fe50a6755ab701f2fe8a434f1d3331769a4c0;hp=b0f22de2559cc313e5933e0a0cfe5e9658f0baa0;hpb=41ce3dc0c3cbfdf06f0ffd3738c34ff8c22f450e;p=nivanova%2Fsamba-autobuild%2F.git diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c index b0f22de2559..7f601afa33a 100644 --- a/source4/dsdb/samdb/ldb_modules/util.c +++ b/source4/dsdb/samdb/ldb_modules/util.c @@ -4,6 +4,7 @@ Copyright (C) Andrew Tridgell 2009 Copyright (C) Andrew Bartlett 2009 + Copyright (C) Matthieu Patou 2011 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 @@ -22,60 +23,11 @@ #include "includes.h" #include "ldb.h" #include "ldb_module.h" +#include "librpc/ndr/libndr.h" #include "dsdb/samdb/ldb_modules/util.h" #include "dsdb/samdb/samdb.h" - -int dsdb_module_search_handle_flags(struct ldb_module *module, struct ldb_request *req, int 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) { - ldb_module_oom(module); - 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) { - ldb_module_oom(module); - 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; - } - } - - return LDB_SUCCESS; -} +#include "dsdb/common/util.h" +#include "libcli/security/security.h" /* search for attrs on one DN, in the modules below @@ -85,7 +37,8 @@ int dsdb_module_search_dn(struct ldb_module *module, struct ldb_result **_res, struct ldb_dn *basedn, const char * const *attrs, - int dsdb_flags) + uint32_t dsdb_flags, + struct ldb_request *parent) { int ret; struct ldb_request *req; @@ -96,7 +49,8 @@ int dsdb_module_search_dn(struct ldb_module *module, res = talloc_zero(tmp_ctx, struct ldb_result); if (!res) { - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(tmp_ctx); + return ldb_oom(ldb_module_get_ctx(module)); } ret = ldb_build_search_req(&req, ldb_module_get_ctx(module), tmp_ctx, @@ -107,19 +61,33 @@ int dsdb_module_search_dn(struct ldb_module *module, NULL, res, ldb_search_default_callback, - NULL); + parent); + LDB_REQ_SET_LOCATION(req); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; } - ret = dsdb_module_search_handle_flags(module, req, dsdb_flags); + ret = dsdb_request_add_controls(req, dsdb_flags); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; } - ret = ldb_next_request(module, req); + if (dsdb_flags & DSDB_FLAG_TRUSTED) { + ldb_req_mark_trusted(req); + } + + /* Run the new request */ + if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) { + ret = ldb_next_request(module, req); + } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) { + ret = ldb_request(ldb_module_get_ctx(module), req); + } else { + const struct ldb_module_ops *ops = ldb_module_get_ops(module); + SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE); + ret = ops->search(module, req); + } if (ret == LDB_SUCCESS) { ret = ldb_wait(req->handle, LDB_WAIT_ALL); } @@ -142,16 +110,15 @@ int dsdb_module_search_dn(struct ldb_module *module, return ret; } -/* - search for attrs in the modules below - */ -int dsdb_module_search(struct ldb_module *module, +int dsdb_module_search_tree(struct ldb_module *module, TALLOC_CTX *mem_ctx, struct ldb_result **_res, - struct ldb_dn *basedn, enum ldb_scope scope, + struct ldb_dn *basedn, + enum ldb_scope scope, + struct ldb_parse_tree *tree, const char * const *attrs, - int dsdb_flags, - const char *expression) + int dsdb_flags, + struct ldb_request *parent) { int ret; struct ldb_request *req; @@ -160,32 +127,49 @@ int dsdb_module_search(struct ldb_module *module, tmp_ctx = talloc_new(mem_ctx); + /* cross-partitions searches with a basedn break multi-domain support */ + SMB_ASSERT(basedn == NULL || (dsdb_flags & DSDB_SEARCH_SEARCH_ALL_PARTITIONS) == 0); + res = talloc_zero(tmp_ctx, struct ldb_result); if (!res) { - return LDB_ERR_OPERATIONS_ERROR; + talloc_free(tmp_ctx); + return ldb_oom(ldb_module_get_ctx(module)); } - ret = ldb_build_search_req(&req, ldb_module_get_ctx(module), tmp_ctx, + ret = ldb_build_search_req_ex(&req, ldb_module_get_ctx(module), tmp_ctx, basedn, scope, - expression, + tree, attrs, NULL, res, ldb_search_default_callback, - NULL); + parent); + LDB_REQ_SET_LOCATION(req); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; } - ret = dsdb_module_search_handle_flags(module, req, dsdb_flags); + ret = dsdb_request_add_controls(req, dsdb_flags); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; } - ret = ldb_next_request(module, req); + if (dsdb_flags & DSDB_FLAG_TRUSTED) { + ldb_req_mark_trusted(req); + } + + if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) { + ret = ldb_next_request(module, req); + } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) { + ret = ldb_request(ldb_module_get_ctx(module), req); + } else { + const struct ldb_module_ops *ops = ldb_module_get_ops(module); + SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE); + ret = ops->search(module, req); + } if (ret == LDB_SUCCESS) { ret = ldb_wait(req->handle, LDB_WAIT_ALL); } @@ -198,3 +182,1161 @@ int dsdb_module_search(struct ldb_module *module, return ret; } +/* + search for attrs in the modules below + */ +int dsdb_module_search(struct ldb_module *module, + TALLOC_CTX *mem_ctx, + struct ldb_result **_res, + struct ldb_dn *basedn, enum ldb_scope scope, + const char * const *attrs, + int dsdb_flags, + struct ldb_request *parent, + const char *format, ...) _PRINTF_ATTRIBUTE(9, 10) +{ + int ret; + TALLOC_CTX *tmp_ctx; + va_list ap; + char *expression; + struct ldb_parse_tree *tree; + + /* cross-partitions searches with a basedn break multi-domain support */ + SMB_ASSERT(basedn == NULL || (dsdb_flags & DSDB_SEARCH_SEARCH_ALL_PARTITIONS) == 0); + + tmp_ctx = talloc_new(mem_ctx); + + if (format) { + va_start(ap, format); + expression = talloc_vasprintf(tmp_ctx, format, ap); + va_end(ap); + + if (!expression) { + talloc_free(tmp_ctx); + return ldb_oom(ldb_module_get_ctx(module)); + } + } else { + expression = NULL; + } + + tree = ldb_parse_tree(tmp_ctx, expression); + if (tree == NULL) { + talloc_free(tmp_ctx); + ldb_set_errstring(ldb_module_get_ctx(module), + "Unable to parse search expression"); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = dsdb_module_search_tree(module, + mem_ctx, + _res, + basedn, + scope, + tree, + attrs, + dsdb_flags, + parent); + + talloc_free(tmp_ctx); + return ret; +} + +/* + find a DN given a GUID. This searches across all partitions + */ +int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx, + const struct GUID *guid, struct ldb_dn **dn, + struct ldb_request *parent) +{ + struct ldb_result *res; + const char *attrs[] = { NULL }; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + int ret; + + ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, + attrs, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_RECYCLED | + DSDB_SEARCH_SEARCH_ALL_PARTITIONS | + DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT, + parent, + "objectGUID=%s", GUID_string(tmp_ctx, guid)); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + if (res->count == 0) { + talloc_free(tmp_ctx); + return LDB_ERR_NO_SUCH_OBJECT; + } + if (res->count != 1) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n", + GUID_string(tmp_ctx, guid)); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + *dn = talloc_steal(mem_ctx, res->msgs[0]->dn); + + talloc_free(tmp_ctx); + return LDB_SUCCESS; +} + +/* + find a GUID given a DN. + */ +int dsdb_module_guid_by_dn(struct ldb_module *module, struct ldb_dn *dn, struct GUID *guid, + struct ldb_request *parent) +{ + const char *attrs[] = { NULL }; + struct ldb_result *res; + TALLOC_CTX *tmp_ctx = talloc_new(module); + int ret; + NTSTATUS status; + + ret = dsdb_module_search_dn(module, tmp_ctx, &res, dn, attrs, + DSDB_FLAG_NEXT_MODULE | + DSDB_SEARCH_SHOW_RECYCLED | + DSDB_SEARCH_SHOW_EXTENDED_DN, + parent); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to find GUID for %s", + ldb_dn_get_linearized(dn)); + talloc_free(tmp_ctx); + return ret; + } + + status = dsdb_get_extended_dn_guid(res->msgs[0]->dn, guid, "GUID"); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return ldb_operr(ldb_module_get_ctx(module)); + } + + talloc_free(tmp_ctx); + return LDB_SUCCESS; +} +/* + a ldb_extended request operating on modules below the + current module + */ +int dsdb_module_extended(struct ldb_module *module, + const char* oid, void* data, + uint32_t dsdb_flags, + struct ldb_request *parent) +{ + struct ldb_request *req; + int ret; + struct ldb_context *ldb = ldb_module_get_ctx(module); + TALLOC_CTX *tmp_ctx = talloc_new(module); + struct ldb_result *res; + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + talloc_free(tmp_ctx); + return ldb_oom(ldb_module_get_ctx(module)); + } + + ret = ldb_build_extended_req(&req, ldb, + tmp_ctx, + oid, + data, + NULL, + res, ldb_extended_default_callback, + parent); + + LDB_REQ_SET_LOCATION(req); + 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; + } + + if (dsdb_flags & DSDB_FLAG_TRUSTED) { + ldb_req_mark_trusted(req); + } + + /* Run the new request */ + if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) { + ret = ldb_next_request(module, req); + } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) { + ret = ldb_request(ldb_module_get_ctx(module), req); + } else { + const struct ldb_module_ops *ops = ldb_module_get_ops(module); + SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE); + ret = ops->extended(module, req); + } + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + talloc_free(tmp_ctx); + return ret; +} +/* + a ldb_modify request operating on modules below the + current module + */ +int dsdb_module_modify(struct ldb_module *module, + const struct ldb_message *message, + uint32_t dsdb_flags, + struct ldb_request *parent) +{ + struct ldb_request *mod_req; + int ret; + struct ldb_context *ldb = ldb_module_get_ctx(module); + TALLOC_CTX *tmp_ctx = talloc_new(module); + struct ldb_result *res; + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + talloc_free(tmp_ctx); + return ldb_oom(ldb_module_get_ctx(module)); + } + + ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx, + message, + NULL, + res, + ldb_modify_default_callback, + parent); + LDB_REQ_SET_LOCATION(mod_req); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + ret = dsdb_request_add_controls(mod_req, dsdb_flags); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + if (dsdb_flags & DSDB_FLAG_TRUSTED) { + ldb_req_mark_trusted(mod_req); + } + + /* Run the new request */ + if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) { + ret = ldb_next_request(module, mod_req); + } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) { + ret = ldb_request(ldb_module_get_ctx(module), mod_req); + } else { + const struct ldb_module_ops *ops = ldb_module_get_ops(module); + SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE); + ret = ops->modify(module, mod_req); + } + if (ret == LDB_SUCCESS) { + ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL); + } + + talloc_free(tmp_ctx); + return ret; +} + + + +/* + a ldb_rename request operating on modules below the + current module + */ +int dsdb_module_rename(struct ldb_module *module, + struct ldb_dn *olddn, struct ldb_dn *newdn, + uint32_t dsdb_flags, + struct ldb_request *parent) +{ + struct ldb_request *req; + int ret; + struct ldb_context *ldb = ldb_module_get_ctx(module); + TALLOC_CTX *tmp_ctx = talloc_new(module); + struct ldb_result *res; + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + talloc_free(tmp_ctx); + return ldb_oom(ldb_module_get_ctx(module)); + } + + ret = ldb_build_rename_req(&req, ldb, tmp_ctx, + olddn, + newdn, + NULL, + res, + ldb_modify_default_callback, + parent); + LDB_REQ_SET_LOCATION(req); + 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; + } + + if (dsdb_flags & DSDB_FLAG_TRUSTED) { + ldb_req_mark_trusted(req); + } + + /* Run the new request */ + if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) { + ret = ldb_next_request(module, req); + } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) { + ret = ldb_request(ldb_module_get_ctx(module), req); + } else { + const struct ldb_module_ops *ops = ldb_module_get_ops(module); + SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE); + ret = ops->rename(module, req); + } + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + talloc_free(tmp_ctx); + return ret; +} + +/* + a ldb_add request operating on modules below the + current module + */ +int dsdb_module_add(struct ldb_module *module, + const struct ldb_message *message, + uint32_t dsdb_flags, + struct ldb_request *parent) +{ + struct ldb_request *req; + int ret; + struct ldb_context *ldb = ldb_module_get_ctx(module); + TALLOC_CTX *tmp_ctx = talloc_new(module); + struct ldb_result *res; + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + talloc_free(tmp_ctx); + return ldb_oom(ldb_module_get_ctx(module)); + } + + ret = ldb_build_add_req(&req, ldb, tmp_ctx, + message, + NULL, + res, + ldb_modify_default_callback, + parent); + LDB_REQ_SET_LOCATION(req); + 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; + } + + if (dsdb_flags & DSDB_FLAG_TRUSTED) { + ldb_req_mark_trusted(req); + } + + /* Run the new request */ + if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) { + ret = ldb_next_request(module, req); + } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) { + ret = ldb_request(ldb_module_get_ctx(module), req); + } else { + const struct ldb_module_ops *ops = ldb_module_get_ops(module); + SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE); + ret = ops->add(module, req); + } + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + talloc_free(tmp_ctx); + return ret; +} + +/* + a ldb_delete request operating on modules below the + current module + */ +int dsdb_module_del(struct ldb_module *module, + struct ldb_dn *dn, + uint32_t dsdb_flags, + struct ldb_request *parent) +{ + struct ldb_request *req; + int ret; + struct ldb_context *ldb = ldb_module_get_ctx(module); + TALLOC_CTX *tmp_ctx = talloc_new(module); + struct ldb_result *res; + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + talloc_free(tmp_ctx); + return ldb_oom(ldb); + } + + ret = ldb_build_del_req(&req, ldb, tmp_ctx, + dn, + NULL, + res, + ldb_modify_default_callback, + parent); + LDB_REQ_SET_LOCATION(req); + 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; + } + + if (dsdb_flags & DSDB_FLAG_TRUSTED) { + ldb_req_mark_trusted(req); + } + + /* Run the new request */ + if (dsdb_flags & DSDB_FLAG_NEXT_MODULE) { + ret = ldb_next_request(module, req); + } else if (dsdb_flags & DSDB_FLAG_TOP_MODULE) { + ret = ldb_request(ldb_module_get_ctx(module), req); + } else { + const struct ldb_module_ops *ops = ldb_module_get_ops(module); + SMB_ASSERT(dsdb_flags & DSDB_FLAG_OWN_MODULE); + ret = ops->del(module, req); + } + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + talloc_free(tmp_ctx); + return ret; +} + +/* + check if a single valued link has multiple non-deleted values + + This is needed when we will be using the RELAX control to stop + ldb_tdb from checking single valued links + */ +int dsdb_check_single_valued_link(const struct dsdb_attribute *attr, + const struct ldb_message_element *el) +{ + bool found_active = false; + unsigned int i; + + if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE) || + el->num_values < 2) { + return LDB_SUCCESS; + } + + for (i=0; inum_values; i++) { + if (!dsdb_dn_is_deleted_val(&el->values[i])) { + if (found_active) { + return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + } + found_active = true; + } + } + + return LDB_SUCCESS; +} + +int dsdb_check_optional_feature(struct ldb_module *module, struct ldb_dn *scope, + struct GUID op_feature_guid, bool *feature_enabled) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct ldb_result *res; + struct ldb_dn *search_dn; + struct GUID search_guid; + const char *attrs[] = {"msDS-EnabledFeature", NULL}; + int ret; + unsigned int i; + struct ldb_message_element *el; + + *feature_enabled = false; + + tmp_ctx = talloc_new(ldb); + + ret = ldb_search(ldb, tmp_ctx, &res, + scope, LDB_SCOPE_BASE, attrs, + NULL); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb, + "Could no find the scope object - dn: %s\n", + ldb_dn_get_linearized(scope)); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + if (res->msgs[0]->num_elements > 0) { + + el = ldb_msg_find_element(res->msgs[0],"msDS-EnabledFeature"); + + attrs[0] = "msDS-OptionalFeatureGUID"; + + for (i=0; inum_values; i++) { + search_dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &el->values[i]); + + ret = ldb_search(ldb, tmp_ctx, &res, + search_dn, LDB_SCOPE_BASE, attrs, + NULL); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb, + "Could no find object dn: %s\n", + ldb_dn_get_linearized(search_dn)); + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + + search_guid = samdb_result_guid(res->msgs[0], "msDS-OptionalFeatureGUID"); + + if (GUID_compare(&search_guid, &op_feature_guid) == 0){ + *feature_enabled = true; + break; + } + } + } + talloc_free(tmp_ctx); + return LDB_SUCCESS; +} + +/* + find the NTDS GUID from a computers DN record + */ +int dsdb_module_find_ntdsguid_for_computer(struct ldb_module *module, + TALLOC_CTX *mem_ctx, + struct ldb_dn *computer_dn, + struct GUID *ntds_guid, + struct ldb_request *parent) +{ + int ret; + struct ldb_dn *dn; + + *ntds_guid = GUID_zero(); + + ret = dsdb_module_reference_dn(module, mem_ctx, computer_dn, + "serverReferenceBL", &dn, parent); + if (ret != LDB_SUCCESS) { + return ret; + } + + if (!ldb_dn_add_child_fmt(dn, "CN=NTDS Settings")) { + talloc_free(dn); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = dsdb_module_guid_by_dn(module, dn, ntds_guid, parent); + talloc_free(dn); + return ret; +} + +/* + find a 'reference' DN that points at another object + (eg. serverReference, rIDManagerReference etc) + */ +int dsdb_module_reference_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, struct ldb_dn *base, + const char *attribute, struct ldb_dn **dn, struct ldb_request *parent) +{ + const char *attrs[2]; + struct ldb_result *res; + int ret; + + attrs[0] = attribute; + attrs[1] = NULL; + + ret = dsdb_module_search_dn(module, mem_ctx, &res, base, attrs, + DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_EXTENDED_DN, parent); + if (ret != LDB_SUCCESS) { + return ret; + } + + *dn = ldb_msg_find_attr_as_dn(ldb_module_get_ctx(module), + mem_ctx, res->msgs[0], attribute); + if (!*dn) { + ldb_reset_err_string(ldb_module_get_ctx(module)); + talloc_free(res); + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + + talloc_free(res); + return LDB_SUCCESS; +} + +/* + find the RID Manager$ DN via the rIDManagerReference attribute in the + base DN + */ +int dsdb_module_rid_manager_dn(struct ldb_module *module, TALLOC_CTX *mem_ctx, struct ldb_dn **dn, + struct ldb_request *parent) +{ + return dsdb_module_reference_dn(module, mem_ctx, + ldb_get_default_basedn(ldb_module_get_ctx(module)), + "rIDManagerReference", dn, parent); +} + +/* + used to chain to the callers callback + */ +int dsdb_next_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct ldb_request *up_req = talloc_get_type(req->context, struct ldb_request); + + talloc_steal(up_req, req); + return up_req->callback(up_req, ares); +} + +/* + load the uSNHighest and the uSNUrgent attributes from the @REPLCHANGED + object for a partition + */ +int dsdb_module_load_partition_usn(struct ldb_module *module, struct ldb_dn *dn, + uint64_t *uSN, uint64_t *urgent_uSN, struct ldb_request *parent) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct ldb_request *req; + int ret; + TALLOC_CTX *tmp_ctx = talloc_new(module); + struct dsdb_control_current_partition *p_ctrl; + struct ldb_result *res; + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + talloc_free(tmp_ctx); + return ldb_module_oom(module); + } + + ret = ldb_build_search_req(&req, ldb, tmp_ctx, + ldb_dn_new(tmp_ctx, ldb, "@REPLCHANGED"), + LDB_SCOPE_BASE, + NULL, NULL, + NULL, + res, ldb_search_default_callback, + parent); + LDB_REQ_SET_LOCATION(req); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + p_ctrl = talloc(req, struct dsdb_control_current_partition); + if (p_ctrl == NULL) { + talloc_free(tmp_ctx); + return ldb_module_oom(module); + } + p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION; + p_ctrl->dn = dn; + + + ret = ldb_request_add_control(req, + DSDB_CONTROL_CURRENT_PARTITION_OID, + false, p_ctrl); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + /* Run the new request */ + ret = ldb_next_request(module, req); + + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + if (ret == LDB_ERR_NO_SUCH_OBJECT || ret == LDB_ERR_INVALID_DN_SYNTAX) { + /* it hasn't been created yet, which means + an implicit value of zero */ + *uSN = 0; + talloc_free(tmp_ctx); + ldb_reset_err_string(ldb); + return LDB_SUCCESS; + } + + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return 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 uSNHighest and uSNUrgent attributes in the @REPLCHANGED object for a + partition + */ +int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn, + uint64_t uSN, uint64_t urgent_uSN, + struct ldb_request *parent) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct ldb_request *req; + struct ldb_message *msg; + struct dsdb_control_current_partition *p_ctrl; + int ret; + struct ldb_result *res; + + msg = ldb_msg_new(module); + if (msg == NULL) { + return ldb_module_oom(module); + } + + msg->dn = ldb_dn_new(msg, ldb, "@REPLCHANGED"); + if (msg->dn == NULL) { + talloc_free(msg); + return ldb_operr(ldb_module_get_ctx(module)); + } + + res = talloc_zero(msg, struct ldb_result); + if (!res) { + talloc_free(msg); + return ldb_module_oom(module); + } + + ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNHighest", uSN); + if (ret != LDB_SUCCESS) { + talloc_free(msg); + return ret; + } + msg->elements[0].flags = LDB_FLAG_MOD_REPLACE; + + /* urgent_uSN is optional so may not be stored */ + if (urgent_uSN) { + ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNUrgent", + 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) { + talloc_free(msg); + return ldb_oom(ldb); + } + p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION; + p_ctrl->dn = dn; + ret = ldb_build_mod_req(&req, ldb, msg, + msg, + NULL, + res, + ldb_modify_default_callback, + parent); + LDB_REQ_SET_LOCATION(req); +again: + if (ret != LDB_SUCCESS) { + talloc_free(msg); + return ret; + } + + ret = ldb_request_add_control(req, + DSDB_CONTROL_CURRENT_PARTITION_OID, + false, p_ctrl); + if (ret != LDB_SUCCESS) { + talloc_free(msg); + return ret; + } + + /* Run the new request */ + ret = ldb_next_request(module, req); + + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + ret = ldb_build_add_req(&req, ldb, msg, + msg, + NULL, + res, + ldb_modify_default_callback, + parent); + LDB_REQ_SET_LOCATION(req); + goto again; + } + + talloc_free(msg); + + return ret; +} + +bool dsdb_module_am_system(struct ldb_module *module) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct auth_session_info *session_info + = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), struct auth_session_info); + return security_session_user_level(session_info, NULL) == SECURITY_SYSTEM; +} + +bool dsdb_module_am_administrator(struct ldb_module *module) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct auth_session_info *session_info + = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), struct auth_session_info); + return security_session_user_level(session_info, NULL) == SECURITY_ADMINISTRATOR; +} + +/* + check if the recyclebin is enabled + */ +int dsdb_recyclebin_enabled(struct ldb_module *module, bool *enabled) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct ldb_dn *partitions_dn; + struct GUID recyclebin_guid; + int ret; + + partitions_dn = samdb_partitions_dn(ldb, module); + + GUID_from_string(DS_GUID_FEATURE_RECYCLE_BIN, &recyclebin_guid); + + ret = dsdb_check_optional_feature(module, partitions_dn, recyclebin_guid, enabled); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb, "Could not verify if Recycle Bin is enabled \n"); + talloc_free(partitions_dn); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + talloc_free(partitions_dn); + return LDB_SUCCESS; +} + +int dsdb_msg_constrainted_update_int32(struct ldb_module *module, + struct ldb_message *msg, + const char *attr, + const int32_t *old_val, + const int32_t *new_val) +{ + struct ldb_message_element *el; + int ret; + char *vstring; + + if (old_val) { + ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, &el); + if (ret != LDB_SUCCESS) { + return ret; + } + el->num_values = 1; + el->values = talloc_array(msg, struct ldb_val, el->num_values); + if (!el->values) { + return ldb_module_oom(module); + } + vstring = talloc_asprintf(el->values, "%ld", (long)*old_val); + if (!vstring) { + return ldb_module_oom(module); + } + *el->values = data_blob_string_const(vstring); + } + + if (new_val) { + ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_ADD, &el); + if (ret != LDB_SUCCESS) { + return ret; + } + el->num_values = 1; + el->values = talloc_array(msg, struct ldb_val, el->num_values); + if (!el->values) { + return ldb_module_oom(module); + } + vstring = talloc_asprintf(el->values, "%ld", (long)*new_val); + if (!vstring) { + return ldb_module_oom(module); + } + *el->values = data_blob_string_const(vstring); + } + + return LDB_SUCCESS; +} + +int dsdb_msg_constrainted_update_uint32(struct ldb_module *module, + struct ldb_message *msg, + const char *attr, + const uint32_t *old_val, + const uint32_t *new_val) +{ + return dsdb_msg_constrainted_update_int32(module, msg, attr, + (const int32_t *)old_val, + (const int32_t *)new_val); +} + +int dsdb_msg_constrainted_update_int64(struct ldb_module *module, + struct ldb_message *msg, + const char *attr, + const int64_t *old_val, + const int64_t *new_val) +{ + struct ldb_message_element *el; + int ret; + char *vstring; + + if (old_val) { + ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, &el); + if (ret != LDB_SUCCESS) { + return ret; + } + el->num_values = 1; + el->values = talloc_array(msg, struct ldb_val, el->num_values); + if (!el->values) { + return ldb_module_oom(module); + } + vstring = talloc_asprintf(el->values, "%lld", (long long)*old_val); + if (!vstring) { + return ldb_module_oom(module); + } + *el->values = data_blob_string_const(vstring); + } + + if (new_val) { + ret = ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_ADD, &el); + if (ret != LDB_SUCCESS) { + return ret; + } + el->num_values = 1; + el->values = talloc_array(msg, struct ldb_val, el->num_values); + if (!el->values) { + return ldb_module_oom(module); + } + vstring = talloc_asprintf(el->values, "%lld", (long long)*new_val); + if (!vstring) { + return ldb_module_oom(module); + } + *el->values = data_blob_string_const(vstring); + } + + return LDB_SUCCESS; +} + +int dsdb_msg_constrainted_update_uint64(struct ldb_module *module, + struct ldb_message *msg, + const char *attr, + const uint64_t *old_val, + const uint64_t *new_val) +{ + return dsdb_msg_constrainted_update_int64(module, msg, attr, + (const int64_t *)old_val, + (const int64_t *)new_val); +} + +/* + update an int32 attribute safely via a constrained delete/add + */ +int dsdb_module_constrainted_update_int32(struct ldb_module *module, + struct ldb_dn *dn, + const char *attr, + const int32_t *old_val, + const int32_t *new_val, + struct ldb_request *parent) +{ + struct ldb_message *msg; + int ret; + + msg = ldb_msg_new(module); + msg->dn = dn; + + ret = dsdb_msg_constrainted_update_int32(module, + msg, attr, + old_val, + new_val); + if (ret != LDB_SUCCESS) { + talloc_free(msg); + return ret; + } + + ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent); + talloc_free(msg); + return ret; +} + +int dsdb_module_constrainted_update_uint32(struct ldb_module *module, + struct ldb_dn *dn, + const char *attr, + const uint32_t *old_val, + const uint32_t *new_val, + struct ldb_request *parent) +{ + return dsdb_module_constrainted_update_int32(module, dn, attr, + (const int32_t *)old_val, + (const int32_t *)new_val, parent); +} + +/* + update an int64 attribute safely via a constrained delete/add + */ +int dsdb_module_constrainted_update_int64(struct ldb_module *module, + struct ldb_dn *dn, + const char *attr, + const int64_t *old_val, + const int64_t *new_val, + struct ldb_request *parent) +{ + struct ldb_message *msg; + int ret; + + msg = ldb_msg_new(module); + msg->dn = dn; + + ret = dsdb_msg_constrainted_update_int64(module, + msg, attr, + old_val, + new_val); + if (ret != LDB_SUCCESS) { + talloc_free(msg); + return ret; + } + + ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent); + talloc_free(msg); + return ret; +} + +int dsdb_module_constrainted_update_uint64(struct ldb_module *module, + struct ldb_dn *dn, + const char *attr, + const uint64_t *old_val, + const uint64_t *new_val, + struct ldb_request *parent) +{ + return dsdb_module_constrainted_update_int64(module, dn, attr, + (const int64_t *)old_val, + (const int64_t *)new_val, + parent); +} + + +const struct ldb_val *dsdb_module_find_dsheuristics(struct ldb_module *module, + TALLOC_CTX *mem_ctx, struct ldb_request *parent) +{ + int ret; + struct ldb_dn *new_dn; + struct ldb_context *ldb = ldb_module_get_ctx(module); + static const char *attrs[] = { "dSHeuristics", NULL }; + struct ldb_result *res; + + new_dn = ldb_dn_copy(mem_ctx, ldb_get_config_basedn(ldb)); + if (!ldb_dn_add_child_fmt(new_dn, + "CN=Directory Service,CN=Windows NT,CN=Services")) { + talloc_free(new_dn); + return NULL; + } + ret = dsdb_module_search_dn(module, mem_ctx, &res, + new_dn, + attrs, + DSDB_FLAG_NEXT_MODULE, + parent); + if (ret == LDB_SUCCESS && res->count == 1) { + talloc_free(new_dn); + return ldb_msg_find_ldb_val(res->msgs[0], + "dSHeuristics"); + } + talloc_free(new_dn); + return NULL; +} + +bool dsdb_block_anonymous_ops(struct ldb_module *module, struct ldb_request *parent) +{ + TALLOC_CTX *tmp_ctx = talloc_new(module); + bool result; + const struct ldb_val *hr_val = dsdb_module_find_dsheuristics(module, + tmp_ctx, parent); + if (hr_val == NULL || hr_val->length < DS_HR_BLOCK_ANONYMOUS_OPS) { + result = true; + } else if (hr_val->data[DS_HR_BLOCK_ANONYMOUS_OPS -1] == '2') { + result = false; + } else { + result = true; + } + + talloc_free(tmp_ctx); + return result; +} + +bool dsdb_user_password_support(struct ldb_module *module, + TALLOC_CTX *mem_ctx, + struct ldb_request *parent) +{ + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + bool result; + const struct ldb_val *hr_val = dsdb_module_find_dsheuristics(module, + tmp_ctx, + parent); + if (hr_val == NULL || hr_val->length < DS_HR_USER_PASSWORD_SUPPORT) { + result = false; + } else if ((hr_val->data[DS_HR_USER_PASSWORD_SUPPORT -1] == '2') || + (hr_val->data[DS_HR_USER_PASSWORD_SUPPORT -1] == '0')) { + result = false; + } else { + result = true; + } + + talloc_free(tmp_ctx); + return result; +} + +/* + show the chain of requests, useful for debugging async requests + */ +void dsdb_req_chain_debug(struct ldb_request *req, int level) +{ + char *s = ldb_module_call_chain(req, req); + DEBUG(level, ("%s\n", s)); + talloc_free(s); +} + +/* + * Gets back a single-valued attribute by the rules of the DSDB triggers when + * performing a modify operation. + * + * In order that the constraint checking by the "objectclass_attrs" LDB module + * does work properly, the change request should remain similar or only be + * enhanced (no other modifications as deletions, variations). + */ +struct ldb_message_element *dsdb_get_single_valued_attr(const struct ldb_message *msg, + const char *attr_name, + enum ldb_request_type operation) +{ + struct ldb_message_element *el = NULL; + unsigned int i; + + /* We've to walk over all modification entries and consider the last + * non-delete one which belongs to "attr_name". + * + * If "el" is NULL afterwards then that means there was no interesting + * change entry. */ + for (i = 0; i < msg->num_elements; i++) { + if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) { + if ((operation == LDB_MODIFY) && + (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) + == LDB_FLAG_MOD_DELETE)) { + continue; + } + el = &msg->elements[i]; + } + } + + return el; +}