diff options
author | Matthias Dieter Wallnöfer <mdw@samba.org> | 2012-05-03 22:55:06 +0200 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2012-05-04 08:25:36 +1000 |
commit | d4391f77bf54ae94de9715bfbefcd545c556e55c (patch) | |
tree | 5b51b375cf6bbd777b518e95d8ee351f43844990 | |
parent | 299c13b7f60f2e3faaf73d6b3370acf99021963d (diff) | |
download | samba-d4391f77bf54ae94de9715bfbefcd545c556e55c.tar.gz samba-d4391f77bf54ae94de9715bfbefcd545c556e55c.tar.bz2 samba-d4391f77bf54ae94de9715bfbefcd545c556e55c.zip |
s4:samldb LDB module - make sure to not add identical "servicePrincipalName"s more than once
The service principal names need to be case-insensitively unique, otherwise we
end up in a LDB ERR_ATTRIBUTE_OR_VALUE_EXISTS error.
This issue has been discovered on the technical mailing list (thread:
cannot rename windows xp machine in samba4) when trying to rename a AD
client workstation.
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/samldb.c | 53 | ||||
-rwxr-xr-x | source4/dsdb/tests/python/sam.py | 51 |
2 files changed, 88 insertions, 16 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 168e55b2f7..d17d809d77 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -1816,7 +1816,7 @@ static int samldb_service_principal_names_change(struct samldb_ctx *ac) struct ldb_result *res; const char *dns_hostname = NULL, *old_dns_hostname = NULL, *sam_accountname = NULL, *old_sam_accountname = NULL; - unsigned int i; + unsigned int i, j; int ret; el = dsdb_get_single_valued_attr(ac->msg, "dNSHostName", @@ -1957,15 +1957,28 @@ static int samldb_service_principal_names_change(struct samldb_ctx *ac) } if (res->msgs[0]->num_elements == 1) { - /* Yes, we do have "servicePrincipalName"s. First we update them + /* + * Yes, we do have "servicePrincipalName"s. First we update them * locally, that means we do always substitute the current * "dNSHostName" with the new one and/or "sAMAccountName" - * without "$" with the new one and then we append this to the - * modification request (Windows behaviour). */ + * without "$" with the new one and then we append the + * modified "servicePrincipalName"s as a message element + * replace to the modification request (Windows behaviour). We + * need also to make sure that the values remain case- + * insensitively unique. + */ + + ret = ldb_msg_add_empty(ac->msg, "servicePrincipalName", + LDB_FLAG_MOD_REPLACE, &el); + if (ret != LDB_SUCCESS) { + return ret; + } for (i = 0; i < res->msgs[0]->elements[0].num_values; i++) { char *old_str, *new_str, *pos; const char *tok; + struct ldb_val *vals; + bool found = false; old_str = (char *) res->msgs[0]->elements[0].values[i].data; @@ -1993,16 +2006,32 @@ static int samldb_service_principal_names_change(struct samldb_ctx *ac) } } - ret = ldb_msg_add_string(ac->msg, - "servicePrincipalName", - new_str); - if (ret != LDB_SUCCESS) { - return ret; + /* Uniqueness check */ + for (j = 0; (!found) && (j < el->num_values); j++) { + if (strcasecmp((char *)el->values[j].data, + new_str) == 0) { + found = true; + } + } + if (found) { + continue; } - } - el = ldb_msg_find_element(ac->msg, "servicePrincipalName"); - el->flags = LDB_FLAG_MOD_REPLACE; + /* + * append the new "servicePrincipalName" - code derived + * from ldb_msg_add_value() + */ + vals = talloc_realloc(ac->msg, el->values, + struct ldb_val, + el->num_values + 1); + if (vals == NULL) { + return ldb_module_oom(ac->module); + } + el->values = vals; + el->values[el->num_values].data = (uint8_t *) new_str; + el->values[el->num_values].length = strlen(new_str); + ++(el->num_values); + } } talloc_free(res); diff --git a/source4/dsdb/tests/python/sam.py b/source4/dsdb/tests/python/sam.py index 8417b26cb7..c5727cd080 100755 --- a/source4/dsdb/tests/python/sam.py +++ b/source4/dsdb/tests/python/sam.py @@ -2432,10 +2432,53 @@ class SamTests(samba.tests.TestCase): self.assertTrue(len(res) == 1) self.assertEquals(res[0]["dNSHostName"][0], "testname2.testdom") self.assertEquals(res[0]["sAMAccountName"][0], "testname2$") - self.assertTrue(res[0]["servicePrincipalName"][0] == "HOST/testname2" or - res[0]["servicePrincipalName"][1] == "HOST/testname2") - self.assertTrue(res[0]["servicePrincipalName"][0] == "HOST/testname2.testdom" or - res[0]["servicePrincipalName"][1] == "HOST/testname2.testdom") + self.assertTrue(len(res[0]["servicePrincipalName"]) == 2) + self.assertTrue("HOST/testname2" in res[0]["servicePrincipalName"]) + self.assertTrue("HOST/testname2.testdom" in res[0]["servicePrincipalName"]) + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) + m["servicePrincipalName"] = MessageElement("HOST/testname2.testdom", + FLAG_MOD_ADD, "servicePrincipalName") + try: + ldb.modify(m) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) + m["servicePrincipalName"] = MessageElement("HOST/testname3", + FLAG_MOD_ADD, "servicePrincipalName") + ldb.modify(m) + + res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn, + scope=SCOPE_BASE, attrs=["dNSHostName", "sAMAccountName", "servicePrincipalName"]) + self.assertTrue(len(res) == 1) + self.assertEquals(res[0]["dNSHostName"][0], "testname2.testdom") + self.assertEquals(res[0]["sAMAccountName"][0], "testname2$") + self.assertTrue(len(res[0]["servicePrincipalName"]) == 3) + self.assertTrue("HOST/testname2" in res[0]["servicePrincipalName"]) + self.assertTrue("HOST/testname3" in res[0]["servicePrincipalName"]) + self.assertTrue("HOST/testname2.testdom" in res[0]["servicePrincipalName"]) + + m = Message() + m.dn = Dn(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) + m["dNSHostName"] = MessageElement("testname3.testdom", + FLAG_MOD_REPLACE, "dNSHostName") + m["servicePrincipalName"] = MessageElement("HOST/testname3.testdom", + FLAG_MOD_ADD, "servicePrincipalName") + ldb.modify(m) + + res = ldb.search("cn=ldaptestcomputer,cn=computers," + self.base_dn, + scope=SCOPE_BASE, attrs=["dNSHostName", "sAMAccountName", "servicePrincipalName"]) + self.assertTrue(len(res) == 1) + self.assertEquals(res[0]["dNSHostName"][0], "testname3.testdom") + self.assertEquals(res[0]["sAMAccountName"][0], "testname2$") + self.assertTrue(len(res[0]["servicePrincipalName"]) == 3) + self.assertTrue("HOST/testname2" in res[0]["servicePrincipalName"]) + self.assertTrue("HOST/testname3" in res[0]["servicePrincipalName"]) + self.assertTrue("HOST/testname3.testdom" in res[0]["servicePrincipalName"]) delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) |