diff options
-rw-r--r-- | source4/dsdb/common/util.c | 31 | ||||
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/acl.c | 101 | ||||
-rwxr-xr-x | source4/lib/ldb/tests/python/acl.py | 108 |
3 files changed, 234 insertions, 6 deletions
diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c index 515d96d085..80736b1712 100644 --- a/source4/dsdb/common/util.c +++ b/source4/dsdb/common/util.c @@ -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 diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c index ccc7edf218..b2aeb2adb7 100644 --- a/source4/dsdb/samdb/ldb_modules/acl.c +++ b/source4/dsdb/samdb/ldb_modules/acl.c @@ -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)) { diff --git a/source4/lib/ldb/tests/python/acl.py b/source4/lib/ldb/tests/python/acl.py index cf061cfe93..514edf87a2 100755 --- a/source4/lib/ldb/tests/python/acl.py +++ b/source4/lib/ldb/tests/python/acl.py @@ -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): |