summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Potter <tpot@samba.org>2000-07-06 06:57:22 +0000
committerTim Potter <tpot@samba.org>2000-07-06 06:57:22 +0000
commitcfc7266a7b5e3cb0170b48d121677434c7443c3d (patch)
treeed505d8d2701ffbaa5449af37a9559de0a750a16
parentd01839e49ec6858c3b0929ad6038a3ff62ac4271 (diff)
downloadsamba-cfc7266a7b5e3cb0170b48d121677434c7443c3d.tar.gz
samba-cfc7266a7b5e3cb0170b48d121677434c7443c3d.tar.bz2
samba-cfc7266a7b5e3cb0170b48d121677434c7443c3d.zip
Rewrite of se_access_check() function. Added comments and fixed a bunch of
bugs. I think there is a problem though with the permissions granted when SEC_RIGHTS_MAXIMUM_ALLOWED is passed as the permissions requested. (This used to be commit 27d821913c87dddd44a0690f4b191c9d2445817e)
-rw-r--r--source3/lib/util_seaccess.c476
1 files changed, 287 insertions, 189 deletions
diff --git a/source3/lib/util_seaccess.c b/source3/lib/util_seaccess.c
index 028b876fa7..f9cfcb835b 100644
--- a/source3/lib/util_seaccess.c
+++ b/source3/lib/util_seaccess.c
@@ -2,6 +2,7 @@
Unix SMB/Netbios implementation.
Version 2.0
Copyright (C) Luke Kenneth Casson Leighton 1996-2000.
+ Copyright (C) Tim Potter 2000.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,256 +25,353 @@
extern int DEBUGLEVEL;
-static uint32 acegrant(uint32 mask, uint32 *acc_req, uint32 *acc_grant, uint32 *acc_deny)
+/* Call winbindd to convert uid to sid */
+
+BOOL winbind_uid_to_sid(uid_t uid, DOM_SID *sid)
{
- /* maximum allowed: grant what's in the ace */
- if ((*acc_req) == SEC_RIGHTS_MAXIMUM_ALLOWED)
- {
- (*acc_grant) |= mask & ~(*acc_deny);
- }
- else
- {
- (*acc_grant) |= (*acc_req) & mask;
- (*acc_req) &= ~(*acc_grant);
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ if (!sid) return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = uid;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_UID_TO_SID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ string_to_sid(sid, response.data.sid.sid);
+ } else {
+ sid_copy(sid, &global_sid_NULL);
}
- if ((*acc_req) == 0x0)
- {
- return NT_STATUS_ACCESS_DENIED;
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert uid to sid */
+
+BOOL winbind_gid_to_sid(gid_t gid, DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ if (!sid) return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.gid = gid;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_GID_TO_SID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ string_to_sid(sid, response.data.sid.sid);
+ } else {
+ sid_copy(sid, &global_sid_NULL);
}
- return NT_STATUS_NOPROBLEMO;
+
+ return (result == NSS_STATUS_SUCCESS);
}
-static uint32 acedeny(uint32 mask, uint32 *acc_req, uint32 *acc_grant, uint32 *acc_deny)
+/* Process an access allowed ACE */
+
+static BOOL ace_grant(uint32 mask, uint32 *acc_desired, uint32 *acc_granted)
{
- /* maximum allowed: grant what's in the ace */
- if ((*acc_req) == SEC_RIGHTS_MAXIMUM_ALLOWED)
- {
- (*acc_deny) |= mask & ~(*acc_grant);
+ uint32 matches;
+
+ /* If there are any matches in the ACE mask and desired access,
+ turn them off in the desired access and on in the granted
+ mask. */
+
+ if (*acc_desired == SEC_RIGHTS_MAXIMUM_ALLOWED) {
+ matches = mask;
+ *acc_desired = mask;
+ } else {
+ matches = mask & *acc_desired;
}
- else
- {
- if ((*acc_req) & mask)
- {
- return NT_STATUS_ACCESS_DENIED;
- }
-#if 0
- (*acc_deny) |= (*acc_req) & mask;
- (*acc_req) &= ~(*acc_deny);
-#endif
+
+ if (matches) {
+ *acc_desired = *acc_desired & ~matches;
+ *acc_granted = *acc_granted | matches;
}
- if ((*acc_req) == 0x0)
- {
- return NT_STATUS_ACCESS_DENIED;
+
+ return *acc_desired == 0;
+}
+
+/* Process an access denied ACE */
+
+static BOOL ace_deny(uint32 mask, uint32 *acc_desired, uint32 *acc_granted)
+{
+ uint32 matches;
+
+ /* If there are any matches in the ACE mask and the desired access,
+ all bits are turned off in the desired and granted mask. */
+
+ if (*acc_desired == SEC_RIGHTS_MAXIMUM_ALLOWED) {
+ matches = mask;
+ } else {
+ matches = mask & *acc_desired;
+ }
+
+ if (matches) {
+ *acc_desired = *acc_granted = 0;
}
- return NT_STATUS_NOPROBLEMO;
+
+ return *acc_desired == 0;
}
-static BOOL check_ace(const SEC_ACE *ace, BOOL is_owner,
- const DOM_SID *sid,
- uint32 *acc_req,
- uint32 *acc_grant,
- uint32 *acc_deny,
- uint32 *status)
+/* Check an ACE against a SID. We return true if the ACE clears all the
+ permission bits in the access desired mask. This indicates that we have
+ make a decision to deny or allow access and the status is updated
+ accordingly. */
+
+static BOOL check_ace(SEC_ACE *ace, BOOL is_owner, DOM_SID *sid,
+ uint32 *acc_desired, uint32 *acc_granted,
+ uint32 *status)
{
uint32 mask = ace->info.mask;
- if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY)
- {
- /* inherit only is ignored */
+ /* Inherit only is ignored */
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
return False;
}
- /* only owner allowed write-owner rights */
- if (!is_owner)
- {
+ /* Only owner allowed write-owner rights */
+
+ if (!is_owner) {
mask &= (~SEC_RIGHTS_WRITE_OWNER);
}
- switch (ace->type)
- {
- case SEC_ACE_TYPE_ACCESS_ALLOWED:
- {
- /* everyone - or us */
+ /* Check the ACE value. This updates the access_desired and
+ access_granted values appropriately. */
+
+ switch (ace->type) {
+
+ /* Access allowed ACE */
+
+ case SEC_ACE_TYPE_ACCESS_ALLOWED: {
+
+ /* Everyone - or us */
+
if (sid_equal(&ace->sid, global_sid_everyone) ||
- sid_equal(&ace->sid, sid))
- {
- (*status) = acegrant(mask, acc_req, acc_grant, acc_deny);
- if ((*status) != NT_STATUS_NOPROBLEMO)
- {
+ sid_equal(&ace->sid, sid)) {
+
+ /* Return true if access has been allowed */
+
+ if (ace_grant(mask, acc_desired,
+ acc_granted)) {
+ *status = NT_STATUS_NO_PROBLEMO;
return True;
}
-
}
+
break;
}
- case SEC_ACE_TYPE_ACCESS_DENIED:
- {
- /* everyone - or us */
+
+ /* Access denied ACE */
+
+ case SEC_ACE_TYPE_ACCESS_DENIED: {
+
+ /* Everyone - or us */
+
if (sid_equal(&ace->sid, global_sid_everyone) ||
- sid_equal(&ace->sid, sid))
- {
- (*status) = acedeny(mask, acc_req, acc_grant, acc_deny);
- if ((*status) != NT_STATUS_NOPROBLEMO)
- {
+ sid_equal(&ace->sid, sid)) {
+
+ /* Return false if access has been denied */
+
+ if (ace_deny(mask, acc_desired,
+ acc_granted)) {
+ *status = NT_STATUS_ACCESS_DENIED;
return True;
}
}
+
break;
}
- case SEC_ACE_TYPE_SYSTEM_AUDIT:
- {
- (*status) = NT_STATUS_NOT_IMPLEMENTED;
- return True;
- }
+
+ /* Unimplemented ACE types. These are ignored. */
+
case SEC_ACE_TYPE_SYSTEM_ALARM:
- {
- (*status) = NT_STATUS_NOT_IMPLEMENTED;
- return True;
+ case SEC_ACE_TYPE_SYSTEM_AUDIT: {
+ *status = NT_STATUS_NOT_IMPLEMENTED;
+ return False;
}
- default:
- {
- (*status) = NT_STATUS_INVALID_PARAMETER;
- return True;
+
+ /* Unknown ACE type */
+
+ default: {
+ *status = NT_STATUS_INVALID_PARAMETER;
+ return False;
}
}
+
+ /* There are still some bits set in the access desired mask that
+ haven't been cleared by an ACE. More checking is required. */
+
return False;
}
-/***********************************************************************
- checks access_requested rights of user against sd. returns access granted
- and a status code if the grant succeeded, error message if it failed.
-
- the previously_granted access rights requires some explanation: if you
- open a policy handle with a set of permissions, you cannot then perform
- operations that require more privileges than those requested. pass in
- the [previously granted] permissions from the open_policy_hnd call as
- prev_grant_acc, and this function will do the checking for you.
- ***********************************************************************/
-BOOL se_access_check(const SEC_DESC * sd, const NET_USER_INFO_3 * user,
- uint32 acc_req, uint32 prev_grant_acc,
- uint32 * acc_grant,
- uint32 * status)
+/* Check access rights of a user against a security descriptor. Look at
+ each ACE in the security descriptor until an access denied ACE denies
+ any of the desired rights to the user or any of the users groups, or one
+ or more ACEs explicitly grant all requested access rights. See
+ "Access-Checking" document in MSDN. */
+
+BOOL se_access_check(SEC_DESC *sd, uid_t uid, gid_t gid, int ngroups,
+ gid_t *groups, uint32 acc_desired,
+ uint32 *acc_granted, uint32 *status)
{
- int num_aces;
- int num_groups;
- DOM_SID usr_sid;
- DOM_SID grp_sid;
- DOM_SID **grp_sids = NULL;
- uint32 ngrp_sids = 0;
+ DOM_SID user_sid, group_sid;
+ DOM_SID **group_sids = NULL;
BOOL is_owner;
- BOOL is_system;
- const SEC_ACL *acl = NULL;
- uint32 grnt;
- uint32 deny;
+ int i, j, ngroup_sids = 0;
+ SEC_ACL *acl;
+ uint8 check_ace_type;
- if (status == NULL)
- {
- return False;
- }
+ if (!status || !acc_granted) return False;
- (*status) = NT_STATUS_ACCESS_DENIED;
+ *status = NT_STATUS_ACCESS_DENIED;
+ *acc_granted = 0;
- if (prev_grant_acc == SEC_RIGHTS_MAXIMUM_ALLOWED)
- {
- prev_grant_acc = 0xffffffff;
- }
-
- /* cannot request any more than previously requested access */
- acc_req &= prev_grant_acc;
+ /* No security descriptor allows all access */
- if (acc_req == 0x0)
- {
- goto end;
- }
+ if (!sd) {
+ *status = NT_STATUS_NOPROBLEMO;
+ *acc_granted = acc_desired;
+ acc_desired = 0;
- /* we must know the owner sid */
- if (sd->owner_sid == NULL)
- {
- goto end;
+ goto done;
}
- (*status) = NT_STATUS_NOPROBLEMO;
-
- /* create group sid */
- sid_copy(&grp_sid, &user->dom_sid.sid);
- sid_append_rid(&grp_sid, user->group_id);
+ /* If desired access mask is empty then no access is allowed */
- /* create user sid */
- sid_copy(&usr_sid, &user->dom_sid.sid);
- sid_append_rid(&usr_sid, user->user_id);
+ if (acc_desired == 0) {
+ goto done;
+ }
- /* preparation: check owner sid, create array of group sids */
- is_owner = sid_equal(&usr_sid, sd->owner_sid);
- add_sid_to_array(&ngrp_sids, &grp_sids, &grp_sid);
+ /* We must know the owner sid */
- for (num_groups = 0; num_groups < user->num_groups; num_groups++)
- {
- sid_copy(&grp_sid, &user->dom_sid.sid);
- sid_append_rid(&grp_sid, user->gids[num_groups].g_rid);
- add_sid_to_array(&ngrp_sids, &grp_sids, &grp_sid);
+ if (sd->owner_sid == NULL) {
+ DEBUG(1, ("no owner for security descriptor\n"));
+ goto done;
}
-#ifdef SAMBA_MAIN_DOES_NOT_HAVE_GLOBAL_SID_SYSTEM
- /* check for system acl or user (discretionary) acl */
- is_system = sid_equal(&usr_sid, global_sid_system);
- if (is_system)
- {
- acl = sd->sacl;
- }
- else
-#endif
- {
- acl = sd->dacl;
+ /* Create user sid */
+
+ if (!winbind_uid_to_sid(uid, &user_sid)) {
+ DEBUG(3, ("could not lookup sid for uid %d\n", uid));
}
- /* acl must have something in it */
- if (acl == NULL || acl->ace == NULL || acl->num_aces == 0)
- {
- goto end;
+ /* Create group sid */
+
+ if (!winbind_gid_to_sid(gid, &group_sid)) {
+ DEBUG(3, ("could not lookup sid for gid %d\n", gid));
}
- /*
- * OK! we have an ACE, it has at least one thing in it,
- * we have a user sid, we have an array of group sids.
- * let's go!
- */
-
- deny = 0;
- grnt = 0;
-
- /* check each ace */
- for (num_aces = 0; num_aces < acl->num_aces; num_aces++)
- {
- const SEC_ACE *ace = &acl->ace[num_aces];
-
- /* first check the user sid */
- if (check_ace(ace, is_owner, &usr_sid, &acc_req,
- &grnt, &deny, status))
- {
- goto end;
- }
- /* now check the group sids */
- for (num_groups = 0; num_groups < ngrp_sids; num_groups++)
- {
- if (check_ace(ace, False, grp_sids[num_groups],
- &acc_req, &grnt, &deny, status))
- {
- goto end;
- }
+ /* Preparation: check owner sid, create array of group sids */
+
+ is_owner = sid_equal(&user_sid, sd->owner_sid);
+ add_sid_to_array(&ngroup_sids, &group_sids, &group_sid);
+
+ for (i = 0; i < ngroups; i++) {
+ if (groups[i] != gid &&
+ winbind_gid_to_sid(groups[i], &group_sid)) {
+ add_sid_to_array(&ngroup_sids, &group_sids,
+ &group_sid);
+ } else {
+ DEBUG(3, ("could not lookup sid for gid %d\n", gid));
}
}
- if (grnt == 0x0 && (*status) == NT_STATUS_NOPROBLEMO)
- {
- (*status) = NT_STATUS_ACCESS_DENIED;
+ /* ACL must have something in it */
+
+ acl = sd->dacl;
+
+ if (acl == NULL || acl->ace == NULL || acl->num_aces == 0) {
+
+ /* Checks against a NULL ACL succeed and return access
+ granted = access requested. */
+
+ *status = NT_STATUS_NOPROBLEMO;
+ *acc_granted = acc_desired;
+ acc_desired = 0;
+
+ goto done;
+ }
+
+ /* Check each ACE in ACL. We break out of the loop if an ACE is
+ either explicitly denied or explicitly allowed by the
+ check_ace2() function. We also check the Access Denied ACEs
+ before Access allowed ones as the Platform SDK documentation is
+ unclear whether ACEs in a ACL are necessarily always in this
+ order. See the discussion on "Order of ACEs in a DACL" in
+ MSDN. */
+
+ check_ace_type = SEC_ACE_TYPE_ACCESS_DENIED;
+
+ check_aces:
+
+ for (i = 0; i < acl->num_aces; i++) {
+ SEC_ACE *ace = &acl->ace[i];
+ BOOL is_group_owner;
+
+ /* Check user sid */
+
+ if (ace->type == check_ace_type &&
+ check_ace(ace, is_owner, &user_sid, &acc_desired,
+ acc_granted, status)) {
+ goto done;
+ }
+
+ /* Check group sids */
+
+ for (j = 0; j < ngroup_sids; j++) {
+
+ is_group_owner = sd->grp_sid ?
+ sid_equal(group_sids[j], sd->grp_sid) : False;
+
+ if (ace->type == check_ace_type &&
+ check_ace(ace, is_group_owner, group_sids[j],
+ &acc_desired, acc_granted, status)) {
+ goto done;
+ }
+ }
+ }
+
+ /* Check access allowed ACEs */
+
+ if (check_ace_type == SEC_ACE_TYPE_ACCESS_DENIED) {
+ check_ace_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ goto check_aces;
}
- else if (acc_grant != NULL)
- {
- (*acc_grant) = grnt;
+
+ done:
+ free_sid_array(ngroup_sids, group_sids);
+
+ /* If any access desired bits are still on, return access denied
+ and turn off any bits already granted. */
+
+ if (acc_desired) {
+ *acc_granted = 0;
+ *status = NT_STATUS_ACCESS_DENIED;
}
-end:
- free_sid_array(ngrp_sids, grp_sids);
- return (*status) != NT_STATUS_NOPROBLEMO;
+ return *status == NT_STATUS_NOPROBLEMO;
}
-