The branch, master has been updated via 37bd313 s4:objectclass LDB module - we should not simply ignore additional "objectClass" attribute changes via 584a2d1 s4:repl_meta_data LDB module - convert two debug messages into error messages via 5585591 s4:samldb/objectclass_attrs LDB modules - move "description" logic from "objectclass_attrs" into "samldb" from e4eba98 waf/samba_version: Simplify git show command.
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 37bd3133049eba7ad26aeb74864e732c6efe70e5 Author: Matthias Dieter Wallnöfer <m...@samba.org> Date: Fri Nov 12 19:49:47 2010 +0100 s4:objectclass LDB module - we should not simply ignore additional "objectClass" attribute changes There first one we perform all other tentatives are terminated with ERR_ATTRIBUTE_OR_VALUE_EXISTS (tested against Windows). Autobuild-User: Matthias Dieter Wallnöfer <m...@samba.org> Autobuild-Date: Fri Nov 12 19:39:07 UTC 2010 on sn-devel-104 commit 584a2d125ec564ffaa019aba53a2d3b29d4c09c3 Author: Matthias Dieter Wallnöfer <m...@samba.org> Date: Fri Nov 12 19:28:48 2010 +0100 s4:repl_meta_data LDB module - convert two debug messages into error messages These regarding "objectGUID". commit 5585591b2e6959bebb005966ad72eb7af4bf1760 Author: Matthias Dieter Wallnöfer <m...@samba.org> Date: Fri Nov 12 18:57:57 2010 +0100 s4:samldb/objectclass_attrs LDB modules - move "description" logic from "objectclass_attrs" into "samldb" This according to an answer from dochelp is SAM specific behaviour. ----------------------------------------------------------------------- Summary of changes: source4/dsdb/samdb/ldb_modules/objectclass.c | 18 ++- source4/dsdb/samdb/ldb_modules/objectclass_attrs.c | 43 ----- source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 8 +- source4/dsdb/samdb/ldb_modules/samldb.c | 81 +++++++++ source4/dsdb/tests/python/ldap.py | 176 +++----------------- source4/dsdb/tests/python/sam.py | 156 +++++++++++++++++- 6 files changed, 278 insertions(+), 204 deletions(-) Changeset truncated at 500 lines: diff --git a/source4/dsdb/samdb/ldb_modules/objectclass.c b/source4/dsdb/samdb/ldb_modules/objectclass.c index 2e95eb5..d2b4f10 100644 --- a/source4/dsdb/samdb/ldb_modules/objectclass.c +++ b/source4/dsdb/samdb/ldb_modules/objectclass.c @@ -1082,12 +1082,26 @@ static int objectclass_do_mod(struct oc_context *ac) break; } + /* Only one "objectclass" attribute change element per modify request + * allowed! */ + for (i = 0; i < ac->req->op.mod.message->num_elements; i++) { + if (ldb_attr_cmp(ac->req->op.mod.message->elements[i].name, + "objectClass") != 0) continue; + + if (ldb_msg_element_compare(&ac->req->op.mod.message->elements[i], + oc_el_change) != 0) { + ldb_set_errstring(ldb, + "objectclass: only one 'objectClass' attribute change per modify request allowed!"); + talloc_free(mem_ctx); + return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + } + } + ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, &oc_el_change); if (ret != LDB_SUCCESS) { - ldb_oom(ldb); talloc_free(mem_ctx); - return ret; + return ldb_oom(ldb); } /* Move from the linked list back into an ldb msg */ diff --git a/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c b/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c index 26eaaea..67d11b3 100644 --- a/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c +++ b/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c @@ -158,49 +158,6 @@ static int attr_handler(struct oc_context *ac) } } - /* "description" on AD is very special: it's nearly single- - * valued (only on add operations it isn't). */ - if ((ac->req->operation == LDB_MODIFY) && - (ldb_attr_cmp(attr->lDAPDisplayName, "description") == 0)) { - /* Multi-valued add or replace operations are always - * denied */ - if ((LDB_FLAG_MOD_TYPE(msg->elements[i].flags) - != LDB_FLAG_MOD_DELETE) && - (msg->elements[i].num_values > 1)) { - ldb_asprintf_errstring(ldb, - "objectclass_attrs: attribute '%s' on entry '%s' is changed using a multi-valued add or replace operation!", - msg->elements[i].name, - ldb_dn_get_linearized(msg->dn)); - return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; - } - - /* Add operations are only allowed if no value exists */ - if (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) - == LDB_FLAG_MOD_ADD) { - const char *attrs[] = { attr->lDAPDisplayName, - NULL }; - struct ldb_result *res; - struct ldb_message_element *el; - - ret = ldb_search(ldb, ac, &res, msg->dn, - LDB_SCOPE_BASE, attrs, NULL); - if (ret != LDB_SUCCESS) { - return ret; - } - - el = ldb_msg_find_element(res->msgs[0], - attr->lDAPDisplayName); - if (el != NULL) { - ldb_asprintf_errstring(ldb, - "objectclass_attrs: attribute '%s' on entry '%s' is changed using an add operation, but there a value already exists!", - msg->elements[i].name, - ldb_dn_get_linearized(msg->dn)); - return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; - } - talloc_free(res); - } - } - /* "dSHeuristics" syntax check */ if (ldb_attr_cmp(attr->lDAPDisplayName, "dSHeuristics") == 0) { ret = oc_validate_dsheuristics(&(msg->elements[i])); diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c index b2d5292..957ca3c 100644 --- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c +++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c @@ -770,8 +770,8 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req) guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID"); if ( guid_blob != NULL ) { if( !allow_add_guid ) { - ldb_debug_set(ldb, LDB_DEBUG_ERROR, - "replmd_add: it's not allowed to add an object with objectGUID\n"); + ldb_set_errstring(ldb, + "replmd_add: it's not allowed to add an object with objectGUID!"); talloc_free(ac); return LDB_ERR_UNWILLING_TO_PERFORM; } else { @@ -2169,8 +2169,8 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req) guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID"); if ( guid_blob != NULL ) { - ldb_debug_set(ldb, LDB_DEBUG_ERROR, - "replmd_modify: it's not allowed to change the objectGUID\n"); + ldb_set_errstring(ldb, + "replmd_modify: it's not allowed to change the objectGUID!"); talloc_free(ac); return LDB_ERR_CONSTRAINT_VIOLATION; } diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 0cd8bc9..4b8a303 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -1496,6 +1496,79 @@ static int samldb_member_check(struct samldb_ctx *ac) } } + talloc_free(res); + + return LDB_SUCCESS; +} + +/* SAM objects have special rules regarding the "description" attribute on + * modify operations. */ +static int samldb_description_check(struct samldb_ctx *ac) +{ + struct ldb_context *ldb = ldb_module_get_ctx(ac->module); + const char * const attrs[] = { "objectClass", "description", NULL }; + struct ldb_message_element *el; + struct ldb_result *res; + unsigned int i; + int ret; + + /* Fetch informations from the existing object */ + + ret = ldb_search(ldb, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs, + NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + if (res->count != 1) { + return ldb_operr(ldb); + } + + /* if it's not a SAM object then please skip the constraints */ + if ((samdb_find_attribute(ldb, res->msgs[0], "objectClass", + "group") == NULL) && + (samdb_find_attribute(ldb, res->msgs[0], "objectClass", + "samDomain") == NULL) && + (samdb_find_attribute(ldb, res->msgs[0], "objectClass", + "samServer") == NULL) && + (samdb_find_attribute(ldb, res->msgs[0], "objectClass", + "user") == NULL)) { + talloc_free(res); + return LDB_SUCCESS; + } + + /* We've to walk over all modification entries and consider the + * "description" ones. */ + for (i = 0; i < ac->msg->num_elements; i++) { + if (ldb_attr_cmp(ac->msg->elements[i].name, + "description") != 0) { + continue; + } + + el = &ac->msg->elements[i]; + + /* Multi-valued add or replace operations are always denied */ + if ((LDB_FLAG_MOD_TYPE(el->flags) != LDB_FLAG_MOD_DELETE) && + (el->num_values > 1)) { + ldb_asprintf_errstring(ldb, + "samldb: Description on SAM entry '%s' is changed using a multi-valued add or replace operation!", + ldb_dn_get_linearized(ac->msg->dn)); + return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + } + + /* Add operations are only allowed if no value exists */ + if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_ADD) { + if (ldb_msg_find_element(res->msgs[0], "description") + != NULL) { + ldb_asprintf_errstring(ldb, + "samldb: Description on SAM entry '%s' is changed using an add operation while a value already exists!", + ldb_dn_get_linearized(ac->msg->dn)); + return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + } + } + } + + talloc_free(res); + return LDB_SUCCESS; } @@ -1894,6 +1967,14 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req) } } + el = ldb_msg_find_element(ac->msg, "description"); + if (el != NULL) { + ret = samldb_description_check(ac); + if (ret != LDB_SUCCESS) { + return ret; + } + } + el = ldb_msg_find_element(ac->msg, "dNSHostName"); el2 = ldb_msg_find_element(ac->msg, "sAMAccountName"); if ((el != NULL) || (el2 != NULL)) { diff --git a/source4/dsdb/tests/python/ldap.py b/source4/dsdb/tests/python/ldap.py index 833e141..0ac57d5 100755 --- a/source4/dsdb/tests/python/ldap.py +++ b/source4/dsdb/tests/python/ldap.py @@ -310,6 +310,19 @@ class BasicTests(unittest.TestCase): except LdbError, (num, _): self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION) + # More than one change operation is not allowed + m = Message() + m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + m["objectClass"] = MessageElement("bootableDevice", FLAG_MOD_DELETE, + "objectClass") + m["objectClass"] = MessageElement("bootableDevice", FLAG_MOD_ADD, + "objectClass") + try: + ldb.modify(m) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) + # We cannot remove all object classes by an empty replace m = Message() m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) @@ -618,160 +631,6 @@ class BasicTests(unittest.TestCase): self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - def test_description_attribute(self): - """Test description attribute""" - print "Test description attribute""" - - self.ldb.add({ - "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, - "description": "desc2", - "objectclass": "group", - "description": "desc1"}) - - res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, - scope=SCOPE_BASE, attrs=["description"]) - self.assertTrue(len(res) == 1) - self.assertTrue("description" in res[0]) - self.assertTrue(len(res[0]["description"]) == 1) - self.assertEquals(res[0]["description"][0], "desc1") - - self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - - self.ldb.add({ - "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, - "objectclass": "group", - "description": ["desc1", "desc2"]}) - - res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, - scope=SCOPE_BASE, attrs=["description"]) - self.assertTrue(len(res) == 1) - self.assertTrue("description" in res[0]) - self.assertTrue(len(res[0]["description"]) == 2) - self.assertTrue(res[0]["description"][0] == "desc1" or - res[0]["description"][1] == "desc1") - self.assertTrue(res[0]["description"][0] == "desc2" or - res[0]["description"][1] == "desc2") - - m = Message() - m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_REPLACE, - "description") - try: - ldb.modify(m) - self.fail() - except LdbError, (num, _): - self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) - - m = Message() - m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_DELETE, - "description") - ldb.modify(m) - - self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - - self.ldb.add({ - "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, - "objectclass": "group" }) - - m = Message() - m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - m["description"] = MessageElement("desc1", FLAG_MOD_REPLACE, - "description") - ldb.modify(m) - - res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, - scope=SCOPE_BASE, attrs=["description"]) - self.assertTrue(len(res) == 1) - self.assertTrue("description" in res[0]) - self.assertTrue(len(res[0]["description"]) == 1) - self.assertEquals(res[0]["description"][0], "desc1") - - self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - - self.ldb.add({ - "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, - "objectclass": "group", - "description": ["desc1", "desc2"]}) - - m = Message() - m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - m["description"] = MessageElement("desc1", FLAG_MOD_REPLACE, - "description") - ldb.modify(m) - - res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, - scope=SCOPE_BASE, attrs=["description"]) - self.assertTrue(len(res) == 1) - self.assertTrue("description" in res[0]) - self.assertTrue(len(res[0]["description"]) == 1) - self.assertEquals(res[0]["description"][0], "desc1") - - m = Message() - m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - m["description"] = MessageElement("desc3", FLAG_MOD_ADD, - "description") - try: - ldb.modify(m) - self.fail() - except LdbError, (num, _): - self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) - - m = Message() - m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_DELETE, - "description") - try: - ldb.modify(m) - self.fail() - except LdbError, (num, _): - self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE) - - m = Message() - m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - m["description"] = MessageElement("desc1", FLAG_MOD_DELETE, - "description") - ldb.modify(m) - res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, - scope=SCOPE_BASE, attrs=["description"]) - self.assertTrue(len(res) == 1) - self.assertFalse("description" in res[0]) - - m = Message() - m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_REPLACE, - "description") - try: - ldb.modify(m) - self.fail() - except LdbError, (num, _): - self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) - - m = Message() - m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - m["description"] = MessageElement(["desc3", "desc4"], FLAG_MOD_ADD, - "description") - try: - ldb.modify(m) - self.fail() - except LdbError, (num, _): - self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) - - m = Message() - m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - m["description"] = MessageElement("desc1", FLAG_MOD_ADD, - "description") - ldb.modify(m) - - res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, - scope=SCOPE_BASE, attrs=["description"]) - self.assertTrue(len(res) == 1) - self.assertTrue("description" in res[0]) - self.assertTrue(len(res[0]["description"]) == 1) - self.assertEquals(res[0]["description"][0], "desc1") - - self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) - def test_attribute_ranges(self): """Test attribute ranges""" print "Test attribute ranges""" @@ -1943,6 +1802,15 @@ servicePrincipalName: host/ldaptest2computer29 "objectClass": "user", "cn": "LDAPtestUSER4"}) + # Here we don't enforce these hard "description" constraints + ldb.modify_ldif(""" +dn: cn=ldaptestcontainer,""" + self.base_dn + """ +changetype: modify +replace: description +description: desc1 +description: desc2 +""") + ldb.modify_ldif(""" dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ changetype: modify diff --git a/source4/dsdb/tests/python/sam.py b/source4/dsdb/tests/python/sam.py index 245d051..b64cce1 100755 --- a/source4/dsdb/tests/python/sam.py +++ b/source4/dsdb/tests/python/sam.py @@ -17,7 +17,7 @@ from samba.auth import system_session from ldb import SCOPE_BASE, LdbError from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS from ldb import ERR_ENTRY_ALREADY_EXISTS, ERR_UNWILLING_TO_PERFORM -from ldb import ERR_OTHER +from ldb import ERR_OTHER, ERR_NO_SUCH_ATTRIBUTE from ldb import ERR_OBJECT_CLASS_VIOLATION from ldb import ERR_CONSTRAINT_VIOLATION from ldb import ERR_UNDEFINED_ATTRIBUTE_TYPE @@ -2251,6 +2251,160 @@ class SamTests(unittest.TestCase): self.delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) + def test_sam_description_attribute(self): + """Test SAM description attribute""" + print "Test SAM description attribute""" + + self.ldb.add({ + "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, + "description": "desc2", + "objectclass": "group", + "description": "desc1"}) + + res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, + scope=SCOPE_BASE, attrs=["description"]) + self.assertTrue(len(res) == 1) + self.assertTrue("description" in res[0]) + self.assertTrue(len(res[0]["description"]) == 1) + self.assertEquals(res[0]["description"][0], "desc1") + + self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + + self.ldb.add({ + "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, + "objectclass": "group", + "description": ["desc1", "desc2"]}) + + res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, + scope=SCOPE_BASE, attrs=["description"]) + self.assertTrue(len(res) == 1) + self.assertTrue("description" in res[0]) + self.assertTrue(len(res[0]["description"]) == 2) + self.assertTrue(res[0]["description"][0] == "desc1" or + res[0]["description"][1] == "desc1") + self.assertTrue(res[0]["description"][0] == "desc2" or + res[0]["description"][1] == "desc2") + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_REPLACE, + "description") + try: + ldb.modify(m) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_DELETE, + "description") + ldb.modify(m) + + self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + + self.ldb.add({ + "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, + "objectclass": "group" }) + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + m["description"] = MessageElement("desc1", FLAG_MOD_REPLACE, + "description") + ldb.modify(m) + + res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, + scope=SCOPE_BASE, attrs=["description"]) + self.assertTrue(len(res) == 1) + self.assertTrue("description" in res[0]) + self.assertTrue(len(res[0]["description"]) == 1) + self.assertEquals(res[0]["description"][0], "desc1") + + self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) -- Samba Shared Repository