diff options
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/linked_attributes.c | 92 | ||||
-rw-r--r-- | source4/lib/ldb-samba/ldif_handlers.c | 78 | ||||
-rwxr-xr-x | source4/lib/ldb/tests/python/ldap.py | 91 | ||||
-rw-r--r-- | source4/scripting/python/samba/provision.py | 3 |
4 files changed, 213 insertions, 51 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/linked_attributes.c b/source4/dsdb/samdb/ldb_modules/linked_attributes.c index c21fda5135..c204571133 100644 --- a/source4/dsdb/samdb/ldb_modules/linked_attributes.c +++ b/source4/dsdb/samdb/ldb_modules/linked_attributes.c @@ -1,4 +1,4 @@ -/* +/* ldb database library Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007 @@ -8,12 +8,12 @@ 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/>. */ @@ -93,8 +93,8 @@ static struct la_context *linked_attributes_init(struct ldb_module *module, */ static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid) { - int ret; NTSTATUS status; + int ret; status = dsdb_get_extended_dn_guid(dn, guid, "GUID"); if (NT_STATUS_IS_OK(status)) { @@ -131,7 +131,7 @@ static int la_store_op(struct la_context *ac, op_dn = ldb_dn_from_ldb_val(ac, ldb, dn); if (!op_dn) { - ldb_asprintf_errstring(ldb, + ldb_asprintf_errstring(ldb, "could not parse attribute as a DN"); return LDB_ERR_INVALID_DN_SYNTAX; } @@ -191,8 +191,8 @@ static int linked_attributes_add(struct ldb_module *module, struct ldb_request * struct la_context *ac; const char *attr_name; struct ldb_control *ctrl; + unsigned int i, j; int ret; - int i, j; ldb = ldb_module_get_ctx(module); @@ -224,9 +224,9 @@ static int linked_attributes_add(struct ldb_module *module, struct ldb_request * const struct dsdb_attribute *schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name); if (!schema_attr) { - ldb_asprintf_errstring(ldb, + ldb_asprintf_errstring(ldb, "attribute %s is not a valid attribute in schema", el->name); - return LDB_ERR_OBJECT_CLASS_VIOLATION; + return LDB_ERR_OBJECT_CLASS_VIOLATION; } /* We have a valid attribute, now find out if it is a forward link */ if ((schema_attr->linkID == 0)) { @@ -235,11 +235,11 @@ static int linked_attributes_add(struct ldb_module *module, struct ldb_request * if ((schema_attr->linkID & 1) == 1) { unsigned int functional_level; - + functional_level = dsdb_functional_level(ldb); SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000); } - + /* Even link IDs are for the originating attribute */ target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1); if (!target_attr) { @@ -289,7 +289,7 @@ static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *are struct replace_context *rc; struct la_context *ac; const char *attr_name; - int i, j; + unsigned int i, j; int ret = LDB_SUCCESS; ac = talloc_get_type(req->context, struct la_context); @@ -310,8 +310,8 @@ static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *are case LDB_REPLY_ENTRY: if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) { - ldb_asprintf_errstring(ldb, - "linked_attributes: %s is not the DN we were looking for", + ldb_asprintf_errstring(ldb, + "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn)); /* Guh? We only asked for this DN */ talloc_free(ares); @@ -412,12 +412,11 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques /* Apply the modify to the linked entry */ struct ldb_context *ldb; - int i, j; + unsigned int i, j; struct la_context *ac; struct ldb_request *search_req; const char **attrs; struct ldb_control *ctrl; - int ret; ldb = ldb_module_get_ctx(module); @@ -457,9 +456,9 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques const struct dsdb_attribute *schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name); if (!schema_attr) { - ldb_asprintf_errstring(ldb, + ldb_asprintf_errstring(ldb, "attribute %s is not a valid attribute in schema", el->name); - return LDB_ERR_OBJECT_CLASS_VIOLATION; + return LDB_ERR_OBJECT_CLASS_VIOLATION; } /* We have a valid attribute, now find out if it is a forward link (Even link IDs are for the originating attribute) */ @@ -469,7 +468,7 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques if ((schema_attr->linkID & 1) == 1) { unsigned int functional_level; - + functional_level = dsdb_functional_level(ldb); SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000); } @@ -487,7 +486,7 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques } attr_name = target_attr->lDAPDisplayName; - + switch (el->flags & LDB_FLAG_MOD_MASK) { case LDB_FLAG_MOD_REPLACE: /* treat as just a normal add the delete part is handled by the callback */ @@ -546,11 +545,11 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques ac->rc->num_elements++; } } - + if (ac->ops || ac->rc->el) { /* both replace and delete without values are handled in the callback * after the search on the entry to be modified is performed */ - + attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1); if (!attrs) { ldb_oom(ldb); @@ -560,7 +559,7 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques attrs[i] = ac->rc->el[i].name; } attrs[i] = NULL; - + /* The callback does all the hard work here */ ret = ldb_build_search_req(&search_req, ldb, ac, req->op.mod.message->dn, @@ -578,7 +577,7 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques } if (ret == LDB_SUCCESS) { talloc_steal(search_req, attrs); - + ret = ldb_next_request(module, search_req); } @@ -596,11 +595,12 @@ static int linked_attributes_fix_links(struct ldb_module *module, struct ldb_message_element *el, struct dsdb_schema *schema, const struct dsdb_attribute *schema_attr) { - unsigned int i; + unsigned int i, j; 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]; + int ret; target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1); if (target == NULL) { @@ -613,8 +613,6 @@ static int linked_attributes_fix_links(struct ldb_module *module, for (i=0; i<el->num_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; @@ -645,7 +643,7 @@ static int linked_attributes_fix_links(struct ldb_module *module, /* Forward link without backlink remaining - nothing to do here */ continue; } else if (msg->num_elements != 1) { - ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s", + ldb_asprintf_errstring(ldb, "Bad msg elements - got %u elements, expected one element to be returned in linked_attributes_fix_links for %s", msg->num_elements, ldb_dn_get_linearized(msg->dn)); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; @@ -709,9 +707,9 @@ static int linked_attributes_rename(struct ldb_module *module, struct ldb_reques 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; + int ret; /* - load the current msg - find any linked attributes @@ -757,7 +755,7 @@ static int linked_attributes_rename(struct ldb_module *module, struct ldb_reques structure */ static int la_queue_mod_request(struct la_context *ac) { - struct la_private *la_private = + struct la_private *la_private = talloc_get_type(ldb_module_get_private(ac->module), struct la_private); if (la_private == NULL) { @@ -775,9 +773,9 @@ static int la_queue_mod_request(struct la_context *ac) /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */ static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares) { - int ret; struct la_context *ac; struct ldb_context *ldb; + int ret; ac = talloc_get_type(req->context, struct la_context); ldb = ldb_module_get_ctx(ac->module); @@ -798,13 +796,13 @@ static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares) return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); } - + ac->op_controls = talloc_steal(ac, ares->controls); ac->op_response = talloc_steal(ac, ares->response); /* If we have modfies to make, this is the time to do them for modify and delete */ ret = la_queue_mod_request(ac); - + if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); } @@ -821,9 +819,9 @@ static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares) */ static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) { - int ret; struct la_context *ac; struct ldb_context *ldb; + int ret; ac = talloc_get_type(req->context, struct la_context); ldb = ldb_module_get_ctx(ac->module); @@ -844,11 +842,11 @@ static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); } - + if (ac->ops) { struct ldb_request *search_req; static const char *attrs[] = { NULL }; - + /* The callback does all the hard work here - we need * the objectGUID and SID of the added record */ ret = ldb_build_search_req(&search_req, ldb, ac, @@ -858,7 +856,7 @@ static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) NULL, ac, la_mod_search_callback, ac->req); - + if (ret == LDB_SUCCESS) { ret = ldb_request_add_control(search_req, LDB_CONTROL_EXTENDED_DN_OID, @@ -873,7 +871,7 @@ static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) ac->op_response = talloc_steal(ac, ares->response); return ldb_next_request(ac->module, search_req); - + } else { return ldb_module_done(ac->req, ares->controls, ares->response, ares->error); @@ -884,8 +882,8 @@ static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares) static int la_down_req(struct la_context *ac) { struct ldb_request *down_req; - int ret; struct ldb_context *ldb; + int ret; ldb = ldb_module_get_ctx(ac->module); @@ -918,7 +916,7 @@ static int la_down_req(struct la_context *ac) use the GUID part of an extended DN to find the target DN, in case it has moved */ -static int la_find_dn_target(struct ldb_module *module, struct la_context *ac, +static int la_find_dn_target(struct ldb_module *module, struct la_context *ac, struct GUID *guid, struct ldb_dn **dn) { return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn); @@ -970,10 +968,10 @@ static int la_do_op_request(struct ldb_module *module, struct la_context *ac, st #if 0 ldb_debug(ldb, LDB_DEBUG_WARNING, - "link on %s %s: %s %s\n", - ldb_dn_get_linearized(new_msg->dn), ret_el->name, + "link on %s %s: %s %s\n", + ldb_dn_get_linearized(new_msg->dn), ret_el->name, ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted"); -#endif +#endif if (DEBUGLVL(4)) { DEBUG(4,("Applying linked attribute change:\n%s\n", @@ -1009,7 +1007,7 @@ static int la_do_mod_request(struct ldb_module *module, struct la_context *ac) /* - we hook into the transaction operations to allow us to + we hook into the transaction operations to allow us to perform the linked attribute updates at the end of the whole transaction. This allows a forward linked attribute to be created before the target is created, as long as the target is created @@ -1036,7 +1034,7 @@ static int linked_attributes_start_transaction(struct ldb_module *module) */ static int linked_attributes_prepare_commit(struct ldb_module *module) { - struct la_private *la_private = + struct la_private *la_private = talloc_get_type(ldb_module_get_private(module), struct la_private); struct la_context *ac; @@ -1060,20 +1058,20 @@ static int linked_attributes_prepare_commit(struct ldb_module *module) if (ret != LDB_SUCCESS) { DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret)); talloc_free(la_private); - ldb_module_set_private(module, NULL); + ldb_module_set_private(module, NULL); return ret; } } talloc_free(la_private); - ldb_module_set_private(module, NULL); + ldb_module_set_private(module, NULL); return ldb_next_prepare_commit(module); } static int linked_attributes_del_transaction(struct ldb_module *module) { - struct la_private *la_private = + struct la_private *la_private = talloc_get_type(ldb_module_get_private(module), struct la_private); talloc_free(la_private); ldb_module_set_private(module, NULL); diff --git a/source4/lib/ldb-samba/ldif_handlers.c b/source4/lib/ldb-samba/ldif_handlers.c index 480335f411..2ce1055d0d 100644 --- a/source4/lib/ldb-samba/ldif_handlers.c +++ b/source4/lib/ldb-samba/ldif_handlers.c @@ -878,6 +878,78 @@ static int extended_dn_write_hex(struct ldb_context *ldb, void *mem_ctx, return 0; } +/* + compare two dns +*/ +static int samba_ldb_dn_link_comparison(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2) +{ + struct ldb_dn *dn1 = NULL, *dn2 = NULL; + int ret; + + if (dsdb_dn_is_deleted_val(v1)) { + /* If the DN is deleted, then we can't search for it */ + return -1; + } + + if (dsdb_dn_is_deleted_val(v2)) { + /* If the DN is deleted, then we can't search for it */ + return -1; + } + + dn1 = ldb_dn_from_ldb_val(mem_ctx, ldb, v1); + if ( ! ldb_dn_validate(dn1)) return -1; + + dn2 = ldb_dn_from_ldb_val(mem_ctx, ldb, v2); + if ( ! ldb_dn_validate(dn2)) { + talloc_free(dn1); + return -1; + } + + ret = ldb_dn_compare(dn1, dn2); + + talloc_free(dn1); + talloc_free(dn2); + return ret; +} + +static int samba_ldb_dn_link_canonicalise(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out) +{ + struct ldb_dn *dn; + int ret = -1; + + out->length = 0; + out->data = NULL; + + dn = ldb_dn_from_ldb_val(mem_ctx, ldb, in); + if ( ! ldb_dn_validate(dn)) { + return LDB_ERR_INVALID_DN_SYNTAX; + } + + /* By including the RMD_FLAGS of a deleted DN, we ensure it + * does not casually match a not deleted DN */ + if (dsdb_dn_is_deleted_val(in)) { + out->data = talloc_asprintf(mem_ctx, "<RMD_FLAGS=%u>%s", + dsdb_dn_val_rmd_flags(in), + ldb_dn_get_casefold(dn)); + } else { + out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn); + } + + if (out->data == NULL) { + goto done; + } + out->length = strlen((char *)out->data); + + ret = 0; + +done: + talloc_free(dn); + + return ret; +} + /* write a 64 bit 2-part range @@ -1010,6 +1082,12 @@ static const struct ldb_schema_syntax samba_syntaxes[] = { .canonicalise_fn = dsdb_dn_string_canonicalise, .comparison_fn = dsdb_dn_string_comparison },{ + .name = LDB_SYNTAX_DN, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = samba_ldb_dn_link_canonicalise, + .comparison_fn = samba_ldb_dn_link_comparison, + },{ .name = LDB_SYNTAX_SAMBA_RANGE64, .ldif_read_fn = ldif_read_range64, .ldif_write_fn = ldif_write_range64, diff --git a/source4/lib/ldb/tests/python/ldap.py b/source4/lib/ldb/tests/python/ldap.py index ddf0254c21..40cbb9feb3 100755 --- a/source4/lib/ldb/tests/python/ldap.py +++ b/source4/lib/ldb/tests/python/ldap.py @@ -969,6 +969,95 @@ objectClass: container self.assertEquals(res1[0]["groupType"][0], "-2147483643") + def test_linked_attributes(self): + """This tests the linked attribute behaviour""" + print "Testing linked attribute behaviour\n" + + ldb.add({ + "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, + "objectclass": "group"}) + + # This should not work since "memberOf" is linked to "member" + try: + ldb.add({ + "dn": "cn=ldaptestuser,cn=users," + self.base_dn, + "objectclass": ["user", "person"], + "memberOf": "cn=ldaptestgroup,cn=users," + self.base_dn}) + except LdbError, (num, _): + self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) + + ldb.add({ + "dn": "cn=ldaptestuser,cn=users," + self.base_dn, + "objectclass": ["user", "person"]}) + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + m["memberOf"] = MessageElement("cn=ldaptestgroup,cn=users," + self.base_dn, + FLAG_MOD_ADD, "memberOf") + try: + ldb.modify(m) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn, + FLAG_MOD_ADD, "member") + ldb.modify(m) + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + m["memberOf"] = MessageElement("cn=ldaptestgroup,cn=users," + self.base_dn, + FLAG_MOD_REPLACE, "memberOf") + try: + ldb.modify(m) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + m["memberOf"] = MessageElement("cn=ldaptestgroup,cn=users," + self.base_dn, + FLAG_MOD_DELETE, "memberOf") + try: + ldb.modify(m) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn, + FLAG_MOD_DELETE, "member") + ldb.modify(m) + + # This should yield no results since the member attribute for + # "ldaptestuser" should have been deleted + res1 = ldb.search("cn=ldaptestgroup, cn=users," + self.base_dn, + scope=SCOPE_BASE, + expression="(member=cn=ldaptestuser,cn=users," + self.base_dn + ")", + attrs=[]) + self.assertTrue(len(res1) == 0) + + self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + + ldb.add({ + "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, + "objectclass": "group", + "member": "cn=ldaptestuser,cn=users," + self.base_dn}) + + self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + + # Make sure that the "member" attribute for "ldaptestuser" has been + # removed + res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, + scope=SCOPE_BASE, attrs=["member"]) + self.assertTrue(len(res) == 1) + self.assertFalse("member" in res[0]) + + self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + def test_groups(self): """This tests the group behaviour (setting, changing) of a user account""" print "Testing group behaviour\n" @@ -2072,7 +2161,7 @@ member: cn=ldaptestuser2,cn=users,""" + self.base_dn + """ self.assertTrue(("<GUID=" + ldb.schema_format_value("objectGUID", ldaptestuser2_guid) + ">;<SID=" + ldb.schema_format_value("objectSid", ldaptestuser2_sid) + ">;CN=ldaptestuser2,CN=Users," + self.base_dn).upper() in memberUP) - print "Testing Linked attribute behaviours" + print "Quicktest for linked attributes" ldb.modify_ldif(""" dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ changetype: modify diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index 8b07f892e9..7fa5dbbdd7 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -856,9 +856,6 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp, names, if dom_for_fun_level is None: dom_for_fun_level = DS_DOMAIN_FUNCTION_2003 - if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003: - logger.warning("Running SAMBA 4 on a domain and forest function level" - " lower than Windows 2003 (Native) is not recommended.") if dom_for_fun_level > domainControllerFunctionality: raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008). This won't work!") |