Implementation of self membership validated right.
authorNadezhda Ivanova <nivanova@samba.org>
Mon, 28 Jun 2010 07:34:14 +0000 (10:34 +0300)
committerNadezhda Ivanova <nivanova@samba.org>
Mon, 28 Jun 2010 07:43:50 +0000 (10:43 +0300)
When this right is granted, the user can add or remove themselves from a group even
if they dont have write property right.

source4/dsdb/common/util.c
source4/dsdb/samdb/ldb_modules/acl.c
source4/lib/ldb/tests/python/acl.py

index 515d96d085e8b0eaba06b612f2bfdc1b1ebe1cca..80736b17124256641b1dc88d427da5a33567c389 100644 (file)
@@ -2514,6 +2514,37 @@ int dsdb_find_sid_by_dn(struct ldb_context *ldb,
        return LDB_SUCCESS;
 }
 
+/*
+  use a SID to find a DN
+ */
+int dsdb_find_dn_by_sid(struct ldb_context *ldb,
+                       TALLOC_CTX *mem_ctx,
+                       struct dom_sid *sid, struct ldb_dn **dn)
+{
+       int ret;
+       struct ldb_result *res;
+       const char *attrs[] = { NULL };
+       char *sid_str = dom_sid_string(mem_ctx, sid);
+
+       if (!sid_str) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       ret = dsdb_search(ldb, mem_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
+                         DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
+                         DSDB_SEARCH_SHOW_EXTENDED_DN |
+                         DSDB_SEARCH_ONE_ONLY,
+                         "objectSID=%s", sid_str);
+       talloc_free(sid_str);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       *dn = talloc_steal(mem_ctx, res->msgs[0]->dn);
+       talloc_free(res);
+
+       return LDB_SUCCESS;
+}
 
 /*
   load a repsFromTo blob list for a given partition GUID
index ccc7edf21848dc1cf46c29d6041bb07ab8b427ab..b2aeb2adb7680f485bf559a478612f0c5fe111c4 100644 (file)
@@ -654,6 +654,95 @@ static int acl_add(struct ldb_module *module, struct ldb_request *req)
        return ldb_next_request(module, req);
 }
 
+/* checks for validated writes */
+static int acl_check_self_write(struct ldb_request *req,
+                               struct security_descriptor *sd,
+                               struct security_token *token,
+                               const char *self_write,
+                               struct dom_sid *sid)
+{
+       struct GUID right;
+       NTSTATUS status;
+       uint32_t access_granted;
+       struct object_tree *root = NULL;
+       struct object_tree *new_node = NULL;
+       TALLOC_CTX *tmp_ctx = talloc_new(req);
+
+       GUID_from_string(self_write, &right);
+
+       if (!insert_in_object_tree(tmp_ctx, &right, SEC_ADS_SELF_WRITE,
+                                  &root, &new_node)) {
+               DEBUG(10, ("acl_modify: cannot add to object tree\n"));
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       status = sec_access_check_ds(sd, token,
+                                    SEC_ADS_SELF_WRITE,
+                                    &access_granted,
+                                    root,
+                                    sid);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("Object %s has no self membershipself write right\n",
+                          ldb_dn_get_linearized(req->op.mod.message->dn)));
+               dsdb_acl_debug(sd, token,
+                              req->op.mod.message->dn,
+                              true,
+                              10);
+               talloc_free(tmp_ctx);
+               return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+       }
+
+       return LDB_SUCCESS;
+}
+
+/* ckecks if modifications are allowed on "Member" attribute */
+static int acl_check_self_membership(struct ldb_module *module,
+                                    struct ldb_request *req,
+                                    struct security_descriptor *sd,
+                                    struct dom_sid *sid,
+                                    const struct GUID *oc_guid,
+                                    const struct dsdb_attribute *attr)
+{
+       int ret, i;
+       TALLOC_CTX *tmp_ctx = talloc_new(req);
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct ldb_dn *user_dn;
+       struct ldb_message_element *member_el;
+       /* if we have wp, we can do whatever we like */
+       if (acl_check_access_on_attribute(module,
+                                         req,
+                                         sd,
+                                         sid,
+                                         SEC_ADS_WRITE_PROP,
+                                         attr) == LDB_SUCCESS) {
+               return LDB_SUCCESS;
+       }
+       /* if we are adding/deleting ourselves, check for self membership */
+       ret = dsdb_find_dn_by_sid(ldb, req, acl_user_token(module)->user_sid, &user_dn);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+       member_el = ldb_msg_find_element(req->op.mod.message, "Member");
+       if (!member_el) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       /* user can only remove oneself */
+       if (member_el->num_values == 0) {
+               return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+       }
+       for (i = 0; i < member_el->num_values; i++) {
+               if (strcasecmp((const char *)member_el->values[i].data,
+                              ldb_dn_get_extended_linearized(tmp_ctx, user_dn, 1)) != 0) {
+                       return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+               }
+       }
+       talloc_free(tmp_ctx);
+       return acl_check_self_write(req, sd, acl_user_token(module),
+                                   GUID_DRS_SELF_MEMBERSHIP,
+                                   sid);
+}
+
 static int acl_modify(struct ldb_module *module, struct ldb_request *req)
 {
        int ret;
@@ -753,8 +842,18 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req)
 
                if (ldb_attr_cmp("nTSecurityDescriptor", req->op.mod.message->elements[i].name) == 0) {
                        modify_sd = true;
+               }
+               else if (ldb_attr_cmp("Member", req->op.mod.message->elements[i].name) == 0) {
+                       ret = acl_check_self_membership(module,
+                                                       req,
+                                                       sd,
+                                                       sid,
+                                                       guid,
+                                                       attr);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
                } else {
-
                        if (!insert_in_object_tree(tmp_ctx,
                                                   &attr->attributeSecurityGUID, SEC_ADS_WRITE_PROP,
                                                   &new_node, &new_node)) {
index cf061cfe9359c35db68d372c5cbd2b213b1e83fb..514edf87a22600056cdc5a8134f2515261ce8725 100755 (executable)
@@ -151,7 +151,7 @@ url: www.example.com
 dn: """ + group_dn + """
 objectClass: group
 sAMAccountName: """ + group_dn.split(",")[0][3:] + """
-groupType: 4
+groupType: 2147483650
 url: www.example.com
 """
         if desc:
@@ -345,23 +345,36 @@ class AclModifyTests(AclTests):
     def setUp(self):
         super(AclModifyTests, self).setUp()
         self.user_with_wp = "acl_mod_user1"
+        self.user_with_sm = "acl_mod_user2"
+        self.user_with_group_sm = "acl_mod_user3"
         self.create_enable_user(self.user_with_wp)
+        self.create_enable_user(self.user_with_sm)
+        self.create_enable_user(self.user_with_group_sm)
         self.ldb_user = self.get_ldb_connection(self.user_with_wp, self.user_pass)
-        self.user_sid = self.get_object_sid(self.get_user_dn(self.user_with_wp))
+        self.ldb_user2 = self.get_ldb_connection(self.user_with_sm, self.user_pass)
+        self.ldb_user3 = self.get_ldb_connection(self.user_with_group_sm, self.user_pass)
+        self.user_sid = self.get_object_sid( self.get_user_dn(self.user_with_wp))
+        self.create_group(self.ldb_admin, "CN=test_modify_group2,CN=Users," + self.base_dn)
+        self.create_group(self.ldb_admin, "CN=test_modify_group3,CN=Users," + self.base_dn)
+        self.create_test_user(self.ldb_admin, self.get_user_dn("test_modify_user2"))
 
     def tearDown(self):
         super(AclModifyTests, self).tearDown()
         self.delete_force(self.ldb_admin, self.get_user_dn("test_modify_user1"))
         self.delete_force(self.ldb_admin, "CN=test_modify_group1,CN=Users," + self.base_dn)
+        self.delete_force(self.ldb_admin, "CN=test_modify_group2,CN=Users," + self.base_dn)
+        self.delete_force(self.ldb_admin, "CN=test_modify_group3,CN=Users," + self.base_dn)
         self.delete_force(self.ldb_admin, "OU=test_modify_ou1," + self.base_dn)
         self.delete_force(self.ldb_admin, self.get_user_dn(self.user_with_wp))
+        self.delete_force(self.ldb_admin, self.get_user_dn(self.user_with_sm))
+        self.delete_force(self.ldb_admin, self.get_user_dn(self.user_with_group_sm))
+        self.delete_force(self.ldb_admin, self.get_user_dn("test_modify_user2"))
 
     def test_modify_u1(self):
         """5 Modify one attribute if you have DS_WRITE_PROPERTY for it"""
         mod = "(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.user_sid)
         # First test object -- User
         print "Testing modify on User object"
-        #self.delete_force(self.ldb_admin, self.get_user_dn("test_modify_user1"))
         self.create_test_user(self.ldb_admin, self.get_user_dn("test_modify_user1"))
         self.dacl_add_ace(self.get_user_dn("test_modify_user1"), mod)
         ldif = """
@@ -375,7 +388,6 @@ displayName: test_changed"""
         self.assertEqual(res[0]["displayName"][0], "test_changed")
         # Second test object -- Group
         print "Testing modify on Group object"
-        #self.delete_force(self.ldb_admin, "CN=test_modify_group1,CN=Users," + self.base_dn)
         self.create_group(self.ldb_admin, "CN=test_modify_group1,CN=Users," + self.base_dn)
         self.dacl_add_ace("CN=test_modify_group1,CN=Users," + self.base_dn, mod)
         ldif = """
@@ -400,7 +412,7 @@ displayName: test_changed"""
         res = self.ldb_admin.search(self.base_dn, expression="(distinguishedName=%s)" % str("OU=test_modify_ou1," + self.base_dn))
         self.assertEqual(res[0]["displayName"][0], "test_changed")
 
-    def test_modify_u2(self):
+    def _test_modify_u2(self):
         """6 Modify two attributes as you have DS_WRITE_PROPERTY granted only for one of them"""
         mod = "(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.user_sid)
         # First test object -- User
@@ -565,6 +577,92 @@ adminDescription: blah blah blah"""
                                     % self.get_user_dn(self.user_with_wp), attrs=["adminDescription"] )
         self.assertEqual(res[0]["adminDescription"][0], "blah blah blah")
 
+    def test_modify_u5(self):
+        """12 test self membership"""
+        ldif = """
+dn: CN=test_modify_group2,CN=Users,""" + self.base_dn + """
+changetype: modify
+add: Member
+Member: """ +  self.get_user_dn(self.user_with_sm)
+#the user has no rights granted, this should fail
+        try:
+            self.ldb_user2.modify_ldif(ldif)
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
+        else:
+            # This 'modify' operation should always throw ERR_INSUFFICIENT_ACCESS_RIGHTS
+            self.fail()
+
+#grant self-membership, should be able to add himself
+        user_sid = self.get_object_sid(self.get_user_dn(self.user_with_sm))
+        mod = "(OA;;SW;bf9679c0-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
+        self.dacl_add_ace("CN=test_modify_group2,CN=Users," + self.base_dn, mod)
+        self.ldb_user2.modify_ldif(ldif)
+        res = self.ldb_admin.search( self.base_dn, expression="(distinguishedName=%s)" \
+                                    % ("CN=test_modify_group2,CN=Users," + self.base_dn), attrs=["Member"])
+        self.assertEqual(res[0]["Member"][0], self.get_user_dn(self.user_with_sm))
+#but not other users
+        ldif = """
+dn: CN=test_modify_group2,CN=Users,""" + self.base_dn + """
+changetype: modify
+add: Member
+Member: CN=test_modify_user2,CN=Users,""" + self.base_dn
+        try:
+            self.ldb_user2.modify_ldif(ldif)
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
+        else:
+            self.fail()
+
+    def test_modify_u6(self):
+        """13 test self membership"""
+        ldif = """
+dn: CN=test_modify_group2,CN=Users,""" + self.base_dn + """
+changetype: modify
+add: Member
+Member: """ +  self.get_user_dn(self.user_with_sm) + """
+Member: CN=test_modify_user2,CN=Users,""" + self.base_dn
+
+#grant self-membership, should be able to add himself  but not others at the same time
+        user_sid = self.get_object_sid(self.get_user_dn(self.user_with_sm))
+        mod = "(OA;;SW;bf9679c0-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid)
+        self.dacl_add_ace("CN=test_modify_group2,CN=Users," + self.base_dn, mod)
+        try:
+            self.ldb_user2.modify_ldif(ldif)
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
+        else:
+            self.fail()
+
+    def test_modify_u7(self):
+        """13 User with WP modifying Member"""
+#a second user is given write property permission
+        user_sid = self.get_object_sid(self.get_user_dn(self.user_with_wp))
+        mod = "(OA;;WP;;;%s)" % str(user_sid)
+        self.dacl_add_ace("CN=test_modify_group2,CN=Users," + self.base_dn, mod)
+        ldif = """
+dn: CN=test_modify_group2,CN=Users,""" + self.base_dn + """
+changetype: modify
+add: Member
+Member: """ +  self.get_user_dn(self.user_with_wp)
+        self.ldb_user.modify_ldif(ldif)
+        res = self.ldb_admin.search( self.base_dn, expression="(distinguishedName=%s)" \
+                                    % ("CN=test_modify_group2,CN=Users," + self.base_dn), attrs=["Member"])
+        self.assertEqual(res[0]["Member"][0], self.get_user_dn(self.user_with_wp))
+        ldif = """
+dn: CN=test_modify_group2,CN=Users,""" + self.base_dn + """
+changetype: modify
+delete: Member"""
+        self.ldb_user.modify_ldif(ldif)
+        ldif = """
+dn: CN=test_modify_group2,CN=Users,""" + self.base_dn + """
+changetype: modify
+add: Member
+Member: CN=test_modify_user2,CN=Users,""" + self.base_dn
+        self.ldb_user.modify_ldif(ldif)
+        res = self.ldb_admin.search( self.base_dn, expression="(distinguishedName=%s)" \
+                                    % ("CN=test_modify_group2,CN=Users," + self.base_dn), attrs=["Member"])
+        self.assertEqual(res[0]["Member"][0], "CN=test_modify_user2,CN=Users," + self.base_dn)
 
 #enable these when we have search implemented
 class AclSearchTests(AclTests):