summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Dieter Wallnöfer <mdw@samba.org>2012-04-21 18:16:43 +0200
committerAndrew Bartlett <abartlet@samba.org>2012-04-30 02:04:23 +0200
commit773304ec8b52d718bd3ca9e1b2543a50d7f4843e (patch)
tree78f429028074523ab0dce69070ee3d6e20594281
parentcd5d282a466981ce87f180e4828fdef678409194 (diff)
downloadsamba-773304ec8b52d718bd3ca9e1b2543a50d7f4843e.tar.gz
samba-773304ec8b52d718bd3ca9e1b2543a50d7f4843e.tar.bz2
samba-773304ec8b52d718bd3ca9e1b2543a50d7f4843e.zip
s4:samldb LDB module - implement "fSMORoleOwner" attribute protection
This is a very essential attribute since it references to various domain master roles (PDC emulator, schema...) depending on which entry it has been set. Incautious modifications can cause severe problems. Autobuild-User: Andrew Bartlett <abartlet@samba.org> Autobuild-Date: Mon Apr 30 02:04:24 CEST 2012 on sn-devel-104
-rw-r--r--source4/dsdb/samdb/ldb_modules/samldb.c76
-rwxr-xr-xsource4/dsdb/tests/python/sam.py77
2 files changed, 153 insertions, 0 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c
index 390d9213f5..3aa0c23959 100644
--- a/source4/dsdb/samdb/ldb_modules/samldb.c
+++ b/source4/dsdb/samdb/ldb_modules/samldb.c
@@ -2010,12 +2010,72 @@ static int samldb_service_principal_names_change(struct samldb_ctx *ac)
return LDB_SUCCESS;
}
+/* This checks the "fSMORoleOwner" attributes */
+static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
+ const char * const no_attrs[] = { NULL };
+ struct ldb_message_element *el;
+ struct ldb_message *tmp_msg;
+ struct ldb_dn *res_dn;
+ struct ldb_result *res;
+ int ret;
+
+ el = dsdb_get_single_valued_attr(ac->msg, "fSMORoleOwner",
+ ac->req->operation);
+ if (el == NULL) {
+ /* we are not affected */
+ return LDB_SUCCESS;
+ }
+
+ /* Create a temporary message for fetching the "fSMORoleOwner" */
+ tmp_msg = ldb_msg_new(ac->msg);
+ if (tmp_msg == NULL) {
+ return ldb_module_oom(ac->module);
+ }
+ ret = ldb_msg_add(tmp_msg, el, 0);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ res_dn = ldb_msg_find_attr_as_dn(ldb, ac, tmp_msg, "fSMORoleOwner");
+ talloc_free(tmp_msg);
+
+ if (res_dn == NULL) {
+ ldb_set_errstring(ldb,
+ "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
+ if (ac->req->operation == LDB_ADD) {
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ } else {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ }
+
+ /* Fetched DN has to reference a "nTDSDSA" entry */
+ ret = dsdb_module_search(ac->module, ac, &res, res_dn, LDB_SCOPE_BASE,
+ no_attrs,
+ DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
+ ac->req, "(objectClass=nTDSDSA)");
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (res->count != 1) {
+ ldb_set_errstring(ldb,
+ "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ talloc_free(res);
+
+ return LDB_SUCCESS;
+}
+
/* add */
static int samldb_add(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_context *ldb;
struct samldb_ctx *ac;
+ struct ldb_message_element *el;
int ret;
ldb = ldb_module_get_ctx(module);
@@ -2040,6 +2100,14 @@ static int samldb_add(struct ldb_module *module, struct ldb_request *req)
return ldb_operr(ldb);
}
+ el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
+ if (el != NULL) {
+ ret = samldb_fsmo_role_owner_check(ac);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
if (samdb_find_attribute(ldb, ac->msg,
"objectclass", "user") != NULL) {
ac->type = SAMLDB_TYPE_USER;
@@ -2231,6 +2299,14 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
}
}
+ el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
+ if (el != NULL) {
+ ret = samldb_fsmo_role_owner_check(ac);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
if (modified) {
struct ldb_request *child_req;
diff --git a/source4/dsdb/tests/python/sam.py b/source4/dsdb/tests/python/sam.py
index 7f5b74dd18..8417b26cb7 100755
--- a/source4/dsdb/tests/python/sam.py
+++ b/source4/dsdb/tests/python/sam.py
@@ -2607,6 +2607,83 @@ class SamTests(samba.tests.TestCase):
delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+ def test_fSMORoleOwner_attribute(self):
+ """Test fSMORoleOwner attribute"""
+ print "Test fSMORoleOwner attribute"""
+
+ ds_service_name = self.ldb.get_dsServiceName()
+
+ # The "fSMORoleOwner" attribute can only be set to "nTDSDSA" entries,
+ # invalid DNs return ERR_UNWILLING_TO_PERFORM
+
+ try:
+ self.ldb.add({
+ "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
+ "objectclass": "group",
+ "fSMORoleOwner": self.base_dn})
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+ try:
+ self.ldb.add({
+ "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
+ "objectclass": "group",
+ "fSMORoleOwner": [] })
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+ # We are able to set it to a valid "nTDSDSA" entry if the server is
+ # capable of handling the role
+
+ self.ldb.add({
+ "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
+ "objectclass": "group",
+ "fSMORoleOwner": ds_service_name })
+
+ 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.add(MessageElement(self.base_dn, FLAG_MOD_REPLACE, "fSMORoleOwner"))
+ 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.add(MessageElement([], FLAG_MOD_REPLACE, "fSMORoleOwner"))
+ try:
+ ldb.modify(m)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+ # We are able to set it to a valid "nTDSDSA" entry if the server is
+ # capable of handling the role
+
+ m = Message()
+ m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+ m.add(MessageElement(ds_service_name, FLAG_MOD_REPLACE, "fSMORoleOwner"))
+ ldb.modify(m)
+
+ # A clean-out works on plain entries, not master (schema, PDC...) DNs
+
+ m = Message()
+ m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+ m.add(MessageElement([], FLAG_MOD_DELETE, "fSMORoleOwner"))
+ ldb.modify(m)
+
+ delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+
+
if not "://" in host:
if os.path.isfile(host):
host = "tdb://%s" % host