mirror of
https://github.com/samba-team/samba.git
synced 2025-02-03 13:47:25 +03:00
7143aed4e4
The next reload will take care of it. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12114 Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
346 lines
9.5 KiB
C
346 lines
9.5 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
|
|
dsdb module schema utility functions
|
|
|
|
Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2010
|
|
Copyright (C) Andrew Tridgell 2010
|
|
Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "dsdb/common/util.h"
|
|
#include "dsdb/samdb/samdb.h"
|
|
#include "dsdb/samdb/ldb_modules/util.h"
|
|
#include <ldb_module.h>
|
|
#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;
|
|
}
|