diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source4/dsdb/samdb/ldb_modules/schema_data.c | |
parent | Initial commit. (diff) | |
download | samba-upstream.tar.xz samba-upstream.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/dsdb/samdb/ldb_modules/schema_data.c')
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/schema_data.c | 691 |
1 files changed, 691 insertions, 0 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/schema_data.c b/source4/dsdb/samdb/ldb_modules/schema_data.c new file mode 100644 index 0000000..697ce21 --- /dev/null +++ b/source4/dsdb/samdb/ldb_modules/schema_data.c @@ -0,0 +1,691 @@ +/* + Unix SMB/CIFS Implementation. + + The module that handles the Schema checkings and dynamic attributes + + Copyright (C) Stefan Metzmacher <metze@samba.org> 2007 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009 + + 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 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 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/>. + +*/ + +#include "includes.h" +#include "ldb_module.h" +#include "dsdb/samdb/samdb.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "librpc/gen_ndr/ndr_drsuapi.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "param/param.h" +#include "dsdb/samdb/ldb_modules/util.h" + +#undef strcasecmp + +static int generate_objectClasses(struct ldb_context *ldb, struct ldb_message *msg, + const struct dsdb_schema *schema); +static int generate_attributeTypes(struct ldb_context *ldb, struct ldb_message *msg, + const struct dsdb_schema *schema); +static int generate_dITContentRules(struct ldb_context *ldb, struct ldb_message *msg, + const struct dsdb_schema *schema); +static int generate_extendedAttributeInfo(struct ldb_context *ldb, struct ldb_message *msg, + const struct dsdb_schema *schema); +static int generate_extendedClassInfo(struct ldb_context *ldb, struct ldb_message *msg, + const struct dsdb_schema *schema); +static int generate_possibleInferiors(struct ldb_context *ldb, struct ldb_message *msg, + const struct dsdb_schema *schema); + +static const struct { + const char *attr; + int (*fn)(struct ldb_context *, struct ldb_message *, const struct dsdb_schema *); + bool aggregate; +} generated_attrs[] = { + { + .attr = "objectClasses", + .fn = generate_objectClasses, + .aggregate = true, + }, + { + .attr = "attributeTypes", + .fn = generate_attributeTypes, + .aggregate = true, + }, + { + .attr = "dITContentRules", + .fn = generate_dITContentRules, + .aggregate = true, + }, + { + .attr = "extendedAttributeInfo", + .fn = generate_extendedAttributeInfo, + .aggregate = true, + }, + { + .attr = "extendedClassInfo", + .fn = generate_extendedClassInfo, + .aggregate = true, + }, + { + .attr = "possibleInferiors", + .fn = generate_possibleInferiors, + .aggregate = false, + } +}; + +struct schema_data_private_data { + struct ldb_dn *aggregate_dn; + struct ldb_dn *schema_dn; +}; + +struct schema_data_search_data { + struct ldb_module *module; + struct ldb_request *req; + + const struct dsdb_schema *schema; +}; + +static int schema_data_init(struct ldb_module *module) +{ + struct ldb_context *ldb; + struct ldb_dn *schema_dn; + int ret; + struct schema_data_private_data *data; + + ret = ldb_next_init(module); + if (ret != LDB_SUCCESS) { + return ret; + } + + ldb = ldb_module_get_ctx(module); + schema_dn = ldb_get_schema_basedn(ldb); + if (!schema_dn) { + ldb_reset_err_string(ldb); + ldb_debug(ldb, LDB_DEBUG_WARNING, + "schema_data_init: no schema dn present: (skip schema loading)\n"); + return LDB_SUCCESS; + } + + data = talloc(module, struct schema_data_private_data); + if (data == NULL) { + return ldb_oom(ldb); + } + + data->schema_dn = schema_dn; + + /* Used to check to see if this is a result on the CN=Aggregate schema */ + data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data); + if (!data->aggregate_dn) { + ldb_asprintf_errstring(ldb, "schema_data_init: Could not build aggregate schema DN for schema in %s", ldb_dn_get_linearized(schema_dn)); + return LDB_ERR_OPERATIONS_ERROR; + } + + ldb_module_set_private(module, data); + return LDB_SUCCESS; +} + +static int schema_data_add(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_context *ldb; + struct dsdb_schema *schema; + const struct ldb_val *attributeID = NULL; + const struct ldb_val *governsID = NULL; + const char *oid_attr = NULL; + const char *oid = NULL; + struct ldb_dn *parent_dn = NULL; + int cmp; + WERROR status; + bool rodc = false; + int ret; + struct schema_data_private_data *mc; + mc = talloc_get_type(ldb_module_get_private(module), struct schema_data_private_data); + + ldb = ldb_module_get_ctx(module); + + /* special objects should always go through */ + if (ldb_dn_is_special(req->op.add.message->dn)) { + return ldb_next_request(module, req); + } + + /* replicated update should always go through */ + if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) { + return ldb_next_request(module, req); + } + + schema = dsdb_get_schema(ldb, req); + if (!schema) { + return ldb_next_request(module, req); + } + + ret = samdb_rodc(ldb, &rodc); + if (ret != LDB_SUCCESS) { + DEBUG(4, (__location__ ": unable to tell if we are an RODC \n")); + } + + if (!schema->fsmo.we_are_master && !rodc) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "schema_data_add: we are not master: reject add request\n"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + if (!schema->fsmo.update_allowed && !rodc) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "schema_data_add: updates are not allowed: reject add request\n"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) { + /* + * the provision code needs to create + * the schema root object. + */ + cmp = ldb_dn_compare(req->op.add.message->dn, mc->schema_dn); + if (cmp == 0) { + return ldb_next_request(module, req); + } + } + + parent_dn = ldb_dn_get_parent(req, req->op.add.message->dn); + if (!parent_dn) { + return ldb_oom(ldb); + } + + cmp = ldb_dn_compare(parent_dn, mc->schema_dn); + if (cmp != 0) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "schema_data_add: no direct child :%s\n", + ldb_dn_get_linearized(req->op.add.message->dn)); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + attributeID = ldb_msg_find_ldb_val(req->op.add.message, "attributeID"); + governsID = ldb_msg_find_ldb_val(req->op.add.message, "governsID"); + + if (attributeID) { + oid_attr = "attributeID"; + oid = talloc_strndup(req, (const char *)attributeID->data, attributeID->length); + } else if (governsID) { + oid_attr = "governsID"; + oid = talloc_strndup(req, (const char *)governsID->data, governsID->length); + } else { + return ldb_next_request(module, req); + } + + if (!oid) { + return ldb_oom(ldb); + } + + status = dsdb_schema_pfm_find_oid(schema->prefixmap, oid, NULL); + if (!W_ERROR_IS_OK(status)) { + /* check for internal errors */ + if (!W_ERROR_EQUAL(status, WERR_NOT_FOUND)) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "schema_data_add: failed to map %s[%s]: %s\n", + oid_attr, oid, win_errstr(status)); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + /* Update prefixMap and save it */ + status = dsdb_create_prefix_mapping(ldb, schema, oid); + if (!W_ERROR_IS_OK(status)) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "schema_data_add: failed to create prefix mapping for %s[%s]: %s\n", + oid_attr, oid, win_errstr(status)); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + } + + return ldb_next_request(module, req); +} + +static int schema_data_modify(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_context *ldb; + struct dsdb_schema *schema; + int cmp; + bool rodc = false; + int ret; + struct ldb_control *sd_propagation_control; + struct schema_data_private_data *mc; + mc = talloc_get_type(ldb_module_get_private(module), struct schema_data_private_data); + + ldb = ldb_module_get_ctx(module); + + /* special objects should always go through */ + if (ldb_dn_is_special(req->op.mod.message->dn)) { + return ldb_next_request(module, req); + } + + /* replicated update should always go through */ + if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) { + return ldb_next_request(module, req); + } + + /* dbcheck should be able to fix things */ + if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) { + return ldb_next_request(module, req); + } + + sd_propagation_control = ldb_request_get_control(req, + DSDB_CONTROL_SEC_DESC_PROPAGATION_OID); + if (sd_propagation_control != NULL) { + if (req->op.mod.message->num_elements != 1) { + return ldb_module_operr(module); + } + ret = strcmp(req->op.mod.message->elements[0].name, + "nTSecurityDescriptor"); + if (ret != 0) { + return ldb_module_operr(module); + } + + return ldb_next_request(module, req); + } + + schema = dsdb_get_schema(ldb, req); + if (!schema) { + return ldb_next_request(module, req); + } + + cmp = ldb_dn_compare(req->op.mod.message->dn, mc->schema_dn); + if (cmp == 0) { + static const char * const constrained_attrs[] = { + "schemaInfo", + "prefixMap", + "msDs-Schema-Extensions", + "msDS-IntId", + NULL + }; + size_t i; + struct ldb_message_element *el; + + if (ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID)) { + return ldb_next_request(module, req); + } + + for (i=0; constrained_attrs[i]; i++) { + el = ldb_msg_find_element(req->op.mod.message, + constrained_attrs[i]); + if (el == NULL) { + continue; + } + + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "schema_data_modify: reject update " + "of attribute[%s]\n", + constrained_attrs[i]); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + + return ldb_next_request(module, req); + } + + ret = samdb_rodc(ldb, &rodc); + if (ret != LDB_SUCCESS) { + DEBUG(4, (__location__ ": unable to tell if we are an RODC \n")); + } + + if (!schema->fsmo.we_are_master && !rodc) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "schema_data_modify: we are not master: reject modify request\n"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + if (!schema->fsmo.update_allowed && !rodc) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "schema_data_modify: updates are not allowed: reject modify request\n"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + return ldb_next_request(module, req); +} + +static int schema_data_del(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_context *ldb; + struct dsdb_schema *schema; + bool rodc = false; + int ret; + + ldb = ldb_module_get_ctx(module); + + /* special objects should always go through */ + if (ldb_dn_is_special(req->op.del.dn)) { + return ldb_next_request(module, req); + } + + /* replicated update should always go through */ + if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) { + return ldb_next_request(module, req); + } + + /* dbcheck should be able to fix things */ + if (ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) { + return ldb_next_request(module, req); + } + + schema = dsdb_get_schema(ldb, req); + if (!schema) { + return ldb_next_request(module, req); + } + + ret = samdb_rodc(ldb, &rodc); + if (ret != LDB_SUCCESS) { + DEBUG(4, (__location__ ": unable to tell if we are an RODC \n")); + } + + if (!schema->fsmo.we_are_master && !rodc) { + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "schema_data_modify: we are not master: reject request\n"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + /* + * normaly the DACL will prevent delete + * with LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS + * above us. + */ + ldb_debug_set(ldb, LDB_DEBUG_ERROR, + "schema_data_del: delete is not allowed in the schema\n"); + return LDB_ERR_UNWILLING_TO_PERFORM; +} + +static int generate_objectClasses(struct ldb_context *ldb, struct ldb_message *msg, + const struct dsdb_schema *schema) +{ + const struct dsdb_class *sclass; + int ret; + + for (sclass = schema->classes; sclass; sclass = sclass->next) { + char *v = schema_class_to_description(msg, sclass); + if (v == NULL) { + return ldb_oom(ldb); + } + ret = ldb_msg_add_steal_string(msg, "objectClasses", v); + if (ret != LDB_SUCCESS) { + return ret; + } + } + return LDB_SUCCESS; +} +static int generate_attributeTypes(struct ldb_context *ldb, struct ldb_message *msg, + const struct dsdb_schema *schema) +{ + const struct dsdb_attribute *attribute; + int ret; + + for (attribute = schema->attributes; attribute; attribute = attribute->next) { + char *v = schema_attribute_to_description(msg, attribute); + if (v == NULL) { + return ldb_oom(ldb); + } + ret = ldb_msg_add_steal_string(msg, "attributeTypes", v); + if (ret != LDB_SUCCESS) { + return ret; + } + } + return LDB_SUCCESS; +} + +static int generate_dITContentRules(struct ldb_context *ldb, struct ldb_message *msg, + const struct dsdb_schema *schema) +{ + const struct dsdb_class *sclass; + int ret; + + for (sclass = schema->classes; sclass; sclass = sclass->next) { + if (sclass->auxiliaryClass || sclass->systemAuxiliaryClass) { + char *ditcontentrule = schema_class_to_dITContentRule(msg, sclass, schema); + if (!ditcontentrule) { + return ldb_oom(ldb); + } + ret = ldb_msg_add_steal_string(msg, "dITContentRules", ditcontentrule); + if (ret != LDB_SUCCESS) { + return ret; + } + } + } + return LDB_SUCCESS; +} + +static int generate_extendedAttributeInfo(struct ldb_context *ldb, + struct ldb_message *msg, + const struct dsdb_schema *schema) +{ + const struct dsdb_attribute *attribute; + int ret; + + for (attribute = schema->attributes; attribute; attribute = attribute->next) { + char *val = schema_attribute_to_extendedInfo(msg, attribute); + if (!val) { + return ldb_oom(ldb); + } + + ret = ldb_msg_add_steal_string(msg, "extendedAttributeInfo", val); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return LDB_SUCCESS; +} + +static int generate_extendedClassInfo(struct ldb_context *ldb, + struct ldb_message *msg, + const struct dsdb_schema *schema) +{ + const struct dsdb_class *sclass; + int ret; + + for (sclass = schema->classes; sclass; sclass = sclass->next) { + char *val = schema_class_to_extendedInfo(msg, sclass); + if (!val) { + return ldb_oom(ldb); + } + + ret = ldb_msg_add_steal_string(msg, "extendedClassInfo", val); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return LDB_SUCCESS; +} + + +static int generate_possibleInferiors(struct ldb_context *ldb, struct ldb_message *msg, + const struct dsdb_schema *schema) +{ + struct ldb_dn *dn = msg->dn; + unsigned int i; + int ret; + const char *first_component_name = ldb_dn_get_component_name(dn, 0); + const struct ldb_val *first_component_val; + const struct dsdb_class *schema_class; + const char **possibleInferiors; + + if (strcasecmp(first_component_name, "cn") != 0) { + return LDB_SUCCESS; + } + + first_component_val = ldb_dn_get_component_val(dn, 0); + + schema_class = dsdb_class_by_cn_ldb_val(schema, first_component_val); + if (schema_class == NULL) { + return LDB_SUCCESS; + } + + possibleInferiors = schema_class->possibleInferiors; + if (possibleInferiors == NULL) { + return LDB_SUCCESS; + } + + for (i=0;possibleInferiors[i];i++) { + char *v = talloc_strdup(msg, possibleInferiors[i]); + if (v == NULL) { + return ldb_oom(ldb); + } + ret = ldb_msg_add_steal_string(msg, "possibleInferiors", v); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return LDB_SUCCESS; +} + + +/* Add objectClasses, attributeTypes and dITContentRules from the + schema object (they are not stored in the database) + */ +static int schema_data_search_callback(struct ldb_request *req, struct ldb_reply *ares) +{ + struct ldb_context *ldb; + struct schema_data_search_data *ac; + struct schema_data_private_data *mc; + unsigned int i; + int ret; + + ac = talloc_get_type(req->context, struct schema_data_search_data); + mc = talloc_get_type(ldb_module_get_private(ac->module), struct schema_data_private_data); + ldb = ldb_module_get_ctx(ac->module); + + if (!ares) { + return ldb_module_done(ac->req, NULL, NULL, + LDB_ERR_OPERATIONS_ERROR); + } + if (ares->error != LDB_SUCCESS) { + return ldb_module_done(ac->req, ares->controls, + ares->response, ares->error); + } + /* Only entries are interesting, and we handle the case of the parent seperatly */ + + switch (ares->type) { + case LDB_REPLY_ENTRY: + + if (ldb_dn_compare(ares->message->dn, mc->aggregate_dn) == 0) { + for (i=0; i < ARRAY_SIZE(generated_attrs); i++) { + if (generated_attrs[i].aggregate && + ldb_attr_in_list(ac->req->op.search.attrs, generated_attrs[i].attr)) { + ret = generated_attrs[i].fn(ldb, ares->message, ac->schema); + if (ret != LDB_SUCCESS) { + return ret; + } + } + } + } else if ((ldb_dn_compare_base(mc->schema_dn, ares->message->dn) == 0) + && (ldb_dn_compare(mc->schema_dn, ares->message->dn) != 0)) { + for (i=0; i < ARRAY_SIZE(generated_attrs); i++) { + if (!generated_attrs[i].aggregate && + ldb_attr_in_list(ac->req->op.search.attrs, generated_attrs[i].attr)) { + ret = generated_attrs[i].fn(ldb, ares->message, ac->schema); + if (ret != LDB_SUCCESS) { + return ret; + } + } + } + } + + + return ldb_module_send_entry(ac->req, ares->message, ares->controls); + + 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, ares->error); + } + + return LDB_SUCCESS; +} + +/* search */ +static int schema_data_search(struct ldb_module *module, struct ldb_request *req) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + unsigned int i; + int ret; + struct schema_data_search_data *search_context; + struct ldb_request *down_req; + const struct dsdb_schema *schema; + if (!ldb_module_get_private(module)) { + /* If there is no module data, there is little we can do */ + return ldb_next_request(module, req); + } + + /* The schema manipulation does not apply to special DNs */ + if (ldb_dn_is_special(req->op.search.base)) { + return ldb_next_request(module, req); + } + + for (i=0; i < ARRAY_SIZE(generated_attrs); i++) { + if (ldb_attr_in_list(req->op.search.attrs, generated_attrs[i].attr)) { + break; + } + } + if (i == ARRAY_SIZE(generated_attrs)) { + /* No request for a generated attr found, nothing to + * see here, move along... */ + return ldb_next_request(module, req); + } + + schema = dsdb_get_schema(ldb, NULL); + if (!schema || !ldb_module_get_private(module)) { + /* If there is no schema, there is little we can do */ + return ldb_next_request(module, req); + } + + search_context = talloc(req, struct schema_data_search_data); + if (!search_context) { + return ldb_oom(ldb); + } + + search_context->module = module; + search_context->req = req; + search_context->schema = talloc_reference(search_context, schema); + if (!search_context->schema) { + return ldb_oom(ldb); + } + + ret = ldb_build_search_req_ex(&down_req, ldb, search_context, + req->op.search.base, + req->op.search.scope, + req->op.search.tree, + req->op.search.attrs, + req->controls, + search_context, schema_data_search_callback, + req); + LDB_REQ_SET_LOCATION(down_req); + if (ret != LDB_SUCCESS) { + return ldb_operr(ldb); + } + + return ldb_next_request(module, down_req); +} + + +static const struct ldb_module_ops ldb_schema_data_module_ops = { + .name = "schema_data", + .init_context = schema_data_init, + .add = schema_data_add, + .modify = schema_data_modify, + .del = schema_data_del, + .search = schema_data_search +}; + +int ldb_schema_data_module_init(const char *version) +{ + LDB_MODULE_CHECK_VERSION(version); + return ldb_register_module(&ldb_schema_data_module_ops); +} |