/* Unix SMB/CIFS implementation. dsdb module schema utility functions Copyright (C) Kamen Mazdrashki 2010 Copyright (C) Andrew Tridgell 2010 Copyright (C) Andrew Bartlett 2010 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 . */ #include "includes.h" #include "dsdb/common/util.h" #include "dsdb/samdb/samdb.h" #include "dsdb/samdb/ldb_modules/util.h" #include #include "librpc/gen_ndr/ndr_drsuapi.h" #include "librpc/gen_ndr/ndr_drsblobs.h" #include "param/param.h" /** * Reads schema_info structure from schemaInfo * attribute on SCHEMA partition * * @param dsdb_flags DSDB_FLAG_... flag of 0 */ int dsdb_module_schema_info_blob_read(struct ldb_module *ldb_module, uint32_t dsdb_flags, TALLOC_CTX *mem_ctx, struct ldb_val *schema_info_blob, struct ldb_request *parent) { int ldb_err; const struct ldb_val *blob_val; struct ldb_dn *schema_dn; struct ldb_result *schema_res = NULL; static const char *schema_attrs[] = { "schemaInfo", NULL }; schema_dn = ldb_get_schema_basedn(ldb_module_get_ctx(ldb_module)); if (!schema_dn) { DEBUG(0,("dsdb_module_schema_info_blob_read: no schema dn present!\n")); return ldb_operr(ldb_module_get_ctx(ldb_module)); } ldb_err = dsdb_module_search(ldb_module, mem_ctx, &schema_res, schema_dn, LDB_SCOPE_BASE, schema_attrs, dsdb_flags, parent, NULL); if (ldb_err == LDB_ERR_NO_SUCH_OBJECT) { DEBUG(0,("dsdb_module_schema_info_blob_read: Schema DN not found!\n")); talloc_free(schema_res); return ldb_err; } else if (ldb_err != LDB_SUCCESS) { DEBUG(0,("dsdb_module_schema_info_blob_read: failed to find schemaInfo attribute\n")); talloc_free(schema_res); return ldb_err; } blob_val = ldb_msg_find_ldb_val(schema_res->msgs[0], "schemaInfo"); if (!blob_val) { ldb_asprintf_errstring(ldb_module_get_ctx(ldb_module), "dsdb_module_schema_info_blob_read: no schemaInfo attribute found"); talloc_free(schema_res); return LDB_ERR_NO_SUCH_ATTRIBUTE; } /* transfer .data ownership to mem_ctx */ schema_info_blob->length = blob_val->length; schema_info_blob->data = talloc_steal(mem_ctx, blob_val->data); talloc_free(schema_res); return LDB_SUCCESS; } /** * Prepares ldb_msg to be used for updating schemaInfo value in DB */ static int dsdb_schema_info_write_prepare(struct ldb_context *ldb, struct ldb_val *schema_info_blob, TALLOC_CTX *mem_ctx, struct ldb_message **_msg) { int ldb_err; struct ldb_message *msg; struct ldb_dn *schema_dn; struct ldb_message_element *return_el; schema_dn = ldb_get_schema_basedn(ldb); if (!schema_dn) { DEBUG(0,("dsdb_schema_info_write_prepare: no schema dn present\n")); return ldb_operr(ldb); } /* prepare ldb_msg to update schemaInfo */ msg = ldb_msg_new(mem_ctx); if (msg == NULL) { return ldb_oom(ldb); } msg->dn = schema_dn; ldb_err = ldb_msg_add_value(msg, "schemaInfo", schema_info_blob, &return_el); if (ldb_err != 0) { ldb_asprintf_errstring(ldb, "dsdb_schema_info_write_prepare: ldb_msg_add_value failed - %s\n", ldb_strerror(ldb_err)); talloc_free(msg); return ldb_err; } /* mark schemaInfo element for replacement */ return_el->flags = LDB_FLAG_MOD_REPLACE; *_msg = msg; return LDB_SUCCESS; } /** * Writes schema_info structure into schemaInfo * attribute on SCHEMA partition * * @param dsdb_flags DSDB_FLAG_... flag of 0 */ int dsdb_module_schema_info_blob_write(struct ldb_module *ldb_module, uint32_t dsdb_flags, struct ldb_val *schema_info_blob, struct ldb_request *parent) { int ldb_err; struct ldb_message *msg = NULL; TALLOC_CTX *temp_ctx; temp_ctx = talloc_new(ldb_module); if (temp_ctx == NULL) { return ldb_module_oom(ldb_module); } /* write serialized schemaInfo into LDB */ ldb_err = dsdb_schema_info_write_prepare(ldb_module_get_ctx(ldb_module), schema_info_blob, temp_ctx, &msg); if (ldb_err != LDB_SUCCESS) { talloc_free(temp_ctx); return ldb_err; } ldb_err = dsdb_module_modify(ldb_module, msg, dsdb_flags, parent); talloc_free(temp_ctx); if (ldb_err != LDB_SUCCESS) { ldb_asprintf_errstring(ldb_module_get_ctx(ldb_module), "dsdb_module_schema_info_blob_write: dsdb_replace failed: %s (%s)\n", ldb_strerror(ldb_err), ldb_errstring(ldb_module_get_ctx(ldb_module))); return ldb_err; } return LDB_SUCCESS; } /** * Reads schema_info structure from schemaInfo * attribute on SCHEMA partition */ static int dsdb_module_schema_info_read(struct ldb_module *ldb_module, uint32_t dsdb_flags, TALLOC_CTX *mem_ctx, struct dsdb_schema_info **_schema_info, struct ldb_request *parent) { int ret; DATA_BLOB ndr_blob; TALLOC_CTX *temp_ctx; WERROR werr; temp_ctx = talloc_new(mem_ctx); if (temp_ctx == NULL) { return ldb_module_oom(ldb_module); } /* read serialized schemaInfo from LDB */ ret = dsdb_module_schema_info_blob_read(ldb_module, dsdb_flags, temp_ctx, &ndr_blob, parent); if (ret != LDB_SUCCESS) { talloc_free(temp_ctx); return ret; } /* convert NDR blob to dsdb_schema_info object */ werr = dsdb_schema_info_from_blob(&ndr_blob, mem_ctx, _schema_info); talloc_free(temp_ctx); if (W_ERROR_EQUAL(werr, WERR_DS_NO_ATTRIBUTE_OR_VALUE)) { return LDB_ERR_NO_SUCH_ATTRIBUTE; } if (!W_ERROR_IS_OK(werr)) { ldb_asprintf_errstring(ldb_module_get_ctx(ldb_module), __location__ ": failed to get schema_info"); return ldb_operr(ldb_module_get_ctx(ldb_module)); } return LDB_SUCCESS; } /** * Writes schema_info structure into schemaInfo * attribute on SCHEMA partition * * @param dsdb_flags DSDB_FLAG_... flag of 0 */ static int dsdb_module_schema_info_write(struct ldb_module *ldb_module, uint32_t dsdb_flags, const struct dsdb_schema_info *schema_info, struct ldb_request *parent) { WERROR werr; int ret; DATA_BLOB ndr_blob; TALLOC_CTX *temp_ctx; temp_ctx = talloc_new(ldb_module); if (temp_ctx == NULL) { return ldb_module_oom(ldb_module); } /* convert schema_info to a blob */ werr = dsdb_blob_from_schema_info(schema_info, temp_ctx, &ndr_blob); if (!W_ERROR_IS_OK(werr)) { talloc_free(temp_ctx); ldb_asprintf_errstring(ldb_module_get_ctx(ldb_module), __location__ ": failed to get schema_info"); return ldb_operr(ldb_module_get_ctx(ldb_module)); } /* write serialized schemaInfo into LDB */ ret = dsdb_module_schema_info_blob_write(ldb_module, dsdb_flags, &ndr_blob, parent); talloc_free(temp_ctx); return ret; } /** * Increments schemaInfo revision and save it to DB * setting our invocationID in the process * NOTE: this function should be called in a transaction * much in the same way prefixMap update function is called * * @param ldb_module current module * @param schema schema cache * @param dsdb_flags DSDB_FLAG_... flag of 0 */ int dsdb_module_schema_info_update(struct ldb_module *ldb_module, struct dsdb_schema *schema, int dsdb_flags, struct ldb_request *parent) { int ret; const struct GUID *invocation_id; struct dsdb_schema_info *schema_info; WERROR werr; TALLOC_CTX *temp_ctx = talloc_new(schema); if (temp_ctx == NULL) { return ldb_module_oom(ldb_module); } invocation_id = samdb_ntds_invocation_id(ldb_module_get_ctx(ldb_module)); if (!invocation_id) { talloc_free(temp_ctx); return ldb_operr(ldb_module_get_ctx(ldb_module)); } /* read serialized schemaInfo from LDB */ ret = dsdb_module_schema_info_read(ldb_module, dsdb_flags, temp_ctx, &schema_info, parent); if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) { /* make default value in case * we have no schemaInfo value yet */ werr = dsdb_schema_info_new(temp_ctx, &schema_info); if (!W_ERROR_IS_OK(werr)) { talloc_free(temp_ctx); return ldb_module_oom(ldb_module); } ret = LDB_SUCCESS; } if (ret != LDB_SUCCESS) { talloc_free(temp_ctx); return ret; } /* update schemaInfo */ schema_info->revision++; schema_info->invocation_id = *invocation_id; ret = dsdb_module_schema_info_write(ldb_module, dsdb_flags, schema_info, parent); if (ret != LDB_SUCCESS) { ldb_asprintf_errstring(ldb_module_get_ctx(ldb_module), "dsdb_module_schema_info_update: failed to save schemaInfo - %s\n", ldb_strerror(ret)); talloc_free(temp_ctx); return ret; } /* * We don't update the schema->schema_info! * as that would not represent the other information * in schema->* * * We're not sure if the current transaction will go through! * E.g. schema changes are only allowed on the schema master, * otherwise they result in a UNWILLING_TO_PERFORM and a * * Note that schema might a global variable shared between * multiple ldb_contexts. With process model "single" it * means the drsuapi server also uses it. * * We keep it simple and just try to update the * stored value. * * The next schema reload will pick it up, which * then works for originating and replicated changes * in the same way. */ talloc_free(temp_ctx); return LDB_SUCCESS; }