From 0cabe327ef1f3e5439384b5e73e328404e869a92 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 8 Aug 2000 19:34:34 +0000 Subject: Changed the sec desc access checks to match the spec. Needs testing. Jeremy. (This used to be commit 5a4a7cd4727df5d1b5e71d343e776c7df52dc515) --- source3/lib/util_seaccess.c | 369 ++++++++++++++++++-------------------------- 1 file changed, 149 insertions(+), 220 deletions(-) (limited to 'source3/lib/util_seaccess.c') diff --git a/source3/lib/util_seaccess.c b/source3/lib/util_seaccess.c index 28e8a43c4e..e9de51632d 100644 --- a/source3/lib/util_seaccess.c +++ b/source3/lib/util_seaccess.c @@ -3,7 +3,7 @@ Version 2.0 Copyright (C) Luke Kenneth Casson Leighton 1996-2000. Copyright (C) Tim Potter 2000. - Copyright (C) Jeremy Allison 2000. + Copyright (C) Re-written by Jeremy Allison 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 @@ -42,198 +42,161 @@ static NT_USER_TOKEN guest_token = { &builtin_guest }; -/* Process an access allowed ACE */ +/********************************************************************************** + Check if this ACE has a SID in common with the token. +**********************************************************************************/ -static BOOL ace_grant(uint32 mask, uint32 *acc_desired, uint32 *acc_granted) +static BOOL token_sid_in_ace( NT_USER_TOKEN *token, SEC_ACE *ace) { - uint32 matches; + size_t i; - /* 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; - } - - if (matches) { - *acc_desired = *acc_desired & ~matches; - *acc_granted = *acc_granted | matches; - } - - 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; + for (i = 0; i < token->num_sids; i++) { + if (sid_equal(&ace->sid, &token->user_sids[i])) { + return True; + } } - return *acc_desired == 0; + return False; } -/* 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. */ +/********************************************************************************* + Check an ACE against a SID. We return the remaining needed permission + bits not yet granted. Zero means permission allowed (no more needed bits). +**********************************************************************************/ -static BOOL check_ace(SEC_ACE *ace, BOOL is_owner, DOM_SID *sid, - uint32 *acc_desired, uint32 *acc_granted, - uint32 *status) +static uint32 check_ace(SEC_ACE *ace, NT_USER_TOKEN *token, uint32 acc_desired, uint32 *status) { uint32 mask = ace->info.mask; - /* Inherit only is ignored */ + /* + * Inherit only is ignored. + */ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) { - return False; - } - - /* Some debugging stuff */ - - if (DEBUGLEVEL >= 3) { - fstring ace_sid_str, sid_str; - fstring ace_name, ace_name_dom, name, name_dom; - uint8 name_type; - - sid_to_string(sid_str, sid); - sid_to_string(ace_sid_str, &ace->sid); - - if (!lookup_sid(sid, name_dom, name, &name_type)) { - fstrcpy(name_dom, "UNKNOWN"); - fstrcpy(name, "UNKNOWN"); - } - - if (!lookup_sid(&ace->sid, ace_name_dom, ace_name, - &name_type)) { - fstrcpy(ace_name_dom, "UNKNOWN"); - fstrcpy(ace_name, "UNKNOWN"); - } - - DEBUG(3, ("checking %s ACE sid %s (%s%s%s) mask 0x%08x " - "against sid %s (%s%s%s)\n", - (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? - "allowed" : ((ace->type == - SEC_ACE_TYPE_ACCESS_DENIED) ? - "denied" : "unknown"), - ace_sid_str, ace_name_dom, lp_winbind_separator(), - ace_name, mask, sid_str, name_dom, - lp_winbind_separator(), name)); + return acc_desired; } - /* Only owner allowed write-owner rights */ - if (!is_owner) { - mask &= (~SEC_RIGHTS_WRITE_OWNER); - } + /* + * If this ACE has no SID in common with the token, + * ignore it as it cannot be used to make an access + * determination. + */ - /* Check the ACE value. This updates the access_desired and - access_granted values appropriately. */ + if (!token_sid_in_ace( token, ace)) + return acc_desired; 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)) { - - /* Return true if access has been allowed */ - - if (ace_grant(mask, acc_desired, - acc_granted)) { - *status = NT_STATUS_NO_PROBLEMO; - DEBUG(3, ("access granted by ace\n")); - return True; - } - } - + case SEC_ACE_TYPE_ACCESS_ALLOWED: + /* + * This is explicitly allowed. + * Remove the bits from the remaining + * access required. Return the remaining + * bits needed. + */ + acc_desired &= ~mask; break; - } - - /* 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)) { - - /* Return false if access has been denied */ - - if (ace_deny(mask, acc_desired, - acc_granted)) { - *status = NT_STATUS_ACCESS_DENIED; - DEBUG(3, ("access denied by ace\n")); - return True; - } + case SEC_ACE_TYPE_ACCESS_DENIED: + /* + * This is explicitly denied. + * If any bits match terminate here, + * we are denied. + */ + if (acc_desired & mask) { + *status = NT_STATUS_ACCESS_DENIED; + return 0xFFFFFFFF; } - break; - } - - /* Unimplemented ACE types. These are ignored. */ - case SEC_ACE_TYPE_SYSTEM_ALARM: - case SEC_ACE_TYPE_SYSTEM_AUDIT: { + case SEC_ACE_TYPE_SYSTEM_AUDIT: *status = NT_STATUS_NOT_IMPLEMENTED; - return False; - } + return 0xFFFFFFFF; + default: + *status = NT_STATUS_INVALID_PARAMETER; + return 0xFFFFFFFF; + } - /* Unknown ACE type */ + return acc_desired; +} - default: { - *status = NT_STATUS_INVALID_PARAMETER; - return False; - } +/********************************************************************************* + Maximum access was requested. Calculate the max possible. Fail if it doesn't + include other bits requested. +**********************************************************************************/ + +static BOOL get_max_access( SEC_ACL *acl, NT_USER_TOKEN *token, uint32 *granted, uint32 desired, uint32 *status) +{ + uint32 acc_denied = 0; + uint32 acc_granted = 0; + size_t i; + + for ( i = 0 ; i < acl->num_aces; i++) { + SEC_ACE *ace = &acl->ace[i]; + uint32 mask = ace->info.mask; + + if (!token_sid_in_ace( token, ace)) + continue; + + switch (ace->type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED: + acc_granted |= (mask & ~acc_denied); + break; + case SEC_ACE_TYPE_ACCESS_DENIED: + acc_denied |= (mask & ~acc_granted); + break; + case SEC_ACE_TYPE_SYSTEM_ALARM: + case SEC_ACE_TYPE_SYSTEM_AUDIT: + *status = NT_STATUS_NOT_IMPLEMENTED; + *granted = 0; + return False; + default: + *status = NT_STATUS_INVALID_PARAMETER; + *granted = 0; + 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. */ + /* + * If we were granted no access, or we desired bits that we + * didn't get, then deny. + */ - return False; + if ((acc_granted == 0) || ((acc_granted & desired) != desired)) { + *status = NT_STATUS_ACCESS_DENIED; + *granted = 0; + return False; + } + + /* + * Return the access we did get. + */ + + *granted = acc_granted; + *status = NT_STATUS_NOPROBLEMO; + return True; } -/* 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. */ +/********************************************************************************* + 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, struct current_user *user, uint32 acc_desired, uint32 *acc_granted, uint32 *status) { - int i, j; + size_t i; SEC_ACL *acl; - uint8 check_ace_type; fstring sid_str; NT_USER_TOKEN *token = user->nt_user_token ? user->nt_user_token : &guest_token; + uint32 tmp_acc_desired = acc_desired; if (!status || !acc_granted) return False; - *status = NT_STATUS_ACCESS_DENIED; + *status = NT_STATUS_NOPROBLEMO; *acc_granted = 0; /* @@ -241,102 +204,68 @@ BOOL se_access_check(SEC_DESC *sd, struct current_user *user, * present allows all access. */ + /* ACL must have something in it */ + if (!sd || (sd && (!(sd->type & SEC_DESC_DACL_PRESENT) || sd->dacl == NULL))) { *status = NT_STATUS_NOPROBLEMO; *acc_granted = acc_desired; - acc_desired = 0; DEBUG(3, ("se_access_check: no sd or blank DACL, access allowed\n")); - goto done; + return True; } - /* If desired access mask is empty then no access is allowed */ - - if (acc_desired == 0) { - *status = NT_STATUS_ACCESS_DENIED; - *acc_granted = 0; - goto done; - } /* We must know the owner sid */ if (sd->owner_sid == NULL) { DEBUG(1, ("no owner for security descriptor\n")); - goto done; + *acc_granted = 0; + *status = NT_STATUS_ACCESS_DENIED; + return False; } /* The user sid is the first in the token */ DEBUG(3, ("se_access_check: user sid is %s\n", sid_to_string(sid_str, &token->user_sids[0]) )); - /* If we're the owner, then we can do anything */ + /* Is the token the owner of the SID ? */ - if (sid_equal(&token->user_sids[0], sd->owner_sid)) { - *status = NT_STATUS_NOPROBLEMO; - *acc_granted = acc_desired; - acc_desired = 0; - DEBUG(3, ("is owner, access allowed\n")); - goto done; + for (i = 0; i < token->num_sids; i++) { + if (sid_equal(&token->user_sids[i], sd->owner_sid)) { + /* + * The owner always has SEC_RIGHTS_WRITE_DAC. + */ + if (tmp_acc_desired & SEC_RIGHTS_WRITE_DAC) + tmp_acc_desired &= ~SEC_RIGHTS_WRITE_DAC; + } } - /* 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; - DEBUG(3, ("null ace, access allowed\n")); - - goto done; + if (tmp_acc_desired & SEC_RIGHTS_MAXIMUM_ALLOWED) { + tmp_acc_desired &= ~SEC_RIGHTS_MAXIMUM_ALLOWED; + return get_max_access( acl, token, acc_granted, tmp_acc_desired, status); } - /* 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]; - - /* Check sids */ - - for (j = 0; j < token->num_sids; j++) { - BOOL is_owner = sid_equal(&token->user_sids[j], sd->owner_sid); - - if (ace->type == check_ace_type && check_ace(ace, is_owner, &token->user_sids[j], &acc_desired, acc_granted, status)) { - goto done; - } + for ( i = 0 ; i < acl->num_aces && tmp_acc_desired != 0; i++) { + tmp_acc_desired = check_ace( &acl->ace[i], token, tmp_acc_desired, status); + if (*status != NT_STATUS_NOPROBLEMO) { + *acc_granted = 0; + return False; } } - /* Check access allowed ACEs */ - - if (check_ace_type == SEC_ACE_TYPE_ACCESS_DENIED) { - check_ace_type = SEC_ACE_TYPE_ACCESS_ALLOWED; - goto check_aces; - } - - done: - - /* If any access desired bits are still on, return access denied - and turn off any bits already granted. */ + /* + * If there are no more desired permissions left then + * access was allowed. + */ - if (acc_desired) { - *acc_granted = 0; - *status = NT_STATUS_ACCESS_DENIED; + if (tmp_acc_desired == 0) { + *acc_granted = acc_desired; + *status = NT_STATUS_NOPROBLEMO; + return True; } - - return *status == NT_STATUS_NOPROBLEMO; + + *acc_granted = 0; + *status = NT_STATUS_ACCESS_DENIED; + return False; } -- cgit