/* ldb database library Copyright (C) Andrew Bartlett 2007 Copyright (C) Simo Sorce 2008 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 . */ /* * Name: ldb * * Component: ldb linked_attributes module * * Description: Module to ensure linked attribute pairs remain in sync * * Author: Andrew Bartlett */ #include "includes.h" #include "ldb_module.h" #include "dlinklist.h" #include "dsdb/samdb/samdb.h" #include "librpc/gen_ndr/ndr_misc.h" #include "dsdb/samdb/ldb_modules/util.h" static int linked_attributes_fix_links(struct ldb_module *module, struct ldb_dn *old_dn, struct ldb_dn *new_dn, struct ldb_message_element *el, struct dsdb_schema *schema, const struct dsdb_attribute *schema_attr) { unsigned int i; TALLOC_CTX *tmp_ctx = talloc_new(module); struct ldb_context *ldb = ldb_module_get_ctx(module); const struct dsdb_attribute *target; const char *attrs[2]; target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1); if (target == NULL) { /* there is no counterpart link to change */ return LDB_SUCCESS; } attrs[0] = target->lDAPDisplayName; attrs[1] = NULL; for (i=0; inum_values; i++) { struct dsdb_dn *dsdb_dn; unsigned int j; int ret; struct ldb_result *res; struct ldb_message *msg; struct ldb_message_element *el2; dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid); if (dsdb_dn == NULL) { talloc_free(tmp_ctx); return LDB_ERR_INVALID_DN_SYNTAX; } ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn, attrs, DSDB_SEARCH_SHOW_DELETED | DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT | DSDB_SEARCH_REVEAL_INTERNALS); if (ret != LDB_SUCCESS) { ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s", el->name, target->lDAPDisplayName, ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(dsdb_dn->dn), ldb_errstring(ldb)); talloc_free(tmp_ctx); return ret; } msg = res->msgs[0]; if (msg->num_elements != 1 || ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) { ldb_set_errstring(ldb, "Bad msg elements in linked_attributes_fix_links"); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } el2 = &msg->elements[0]; el2->flags = LDB_FLAG_MOD_REPLACE; /* find our DN in the values */ for (j=0; jnum_values; j++) { struct dsdb_dn *dsdb_dn2; dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid); if (dsdb_dn2 == NULL) { talloc_free(tmp_ctx); return LDB_ERR_INVALID_DN_SYNTAX; } if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) { continue; } ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; } el2->values[j] = data_blob_string_const( dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1)); } ret = dsdb_check_single_valued_link(target, el2); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; } ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX); if (ret != LDB_SUCCESS) { ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s", el->name, target->lDAPDisplayName, ldb_dn_get_linearized(old_dn), ldb_dn_get_linearized(dsdb_dn->dn), ldb_errstring(ldb)); talloc_free(tmp_ctx); return ret; } } talloc_free(tmp_ctx); return LDB_SUCCESS; } /* rename */ static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req) { struct ldb_result *res; struct ldb_message *msg; unsigned int i; int ret; struct ldb_context *ldb = ldb_module_get_ctx(module); struct dsdb_schema *schema = dsdb_get_schema(ldb); /* - load the current msg - find any linked attributes - if its a link then find the target object - modify the target linked attributes with the new DN */ ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn, NULL, DSDB_SEARCH_SHOW_DELETED); if (ret != LDB_SUCCESS) { return ret; } msg = res->msgs[0]; for (i=0; inum_elements; i++) { struct ldb_message_element *el = &msg->elements[i]; const struct dsdb_attribute *schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, el->name); if (!schema_attr || schema_attr->linkID == 0) { continue; } ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el, schema, schema_attr); if (ret != LDB_SUCCESS) { talloc_free(res); return ret; } } talloc_free(res); return ldb_next_request(module, req); } _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = { .name = "linked_attributes", .rename = linked_attributes_rename, };