mirror of
https://github.com/samba-team/samba.git
synced 2025-08-03 04:22:09 +03:00
Changed the sec desc access checks to match the spec. Needs testing.
Jeremy.
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
||||
return *acc_desired == 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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));
|
||||
}
|
||||
|
||||
/* Only owner allowed write-owner rights */
|
||||
|
||||
if (!is_owner) {
|
||||
mask &= (~SEC_RIGHTS_WRITE_OWNER);
|
||||
}
|
||||
|
||||
/* 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)) {
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Unimplemented ACE types. These are ignored. */
|
||||
|
||||
case SEC_ACE_TYPE_SYSTEM_ALARM:
|
||||
case SEC_ACE_TYPE_SYSTEM_AUDIT: {
|
||||
*status = NT_STATUS_NOT_IMPLEMENTED;
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Unknown ACE type */
|
||||
|
||||
default: {
|
||||
*status = NT_STATUS_INVALID_PARAMETER;
|
||||
return False;
|
||||
for (i = 0; i < token->num_sids; i++) {
|
||||
if (sid_equal(&ace->sid, &token->user_sids[i])) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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 an ACE against a SID. We return the remaining needed permission
|
||||
bits not yet granted. Zero means permission allowed (no more needed bits).
|
||||
**********************************************************************************/
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
|
||||
return acc_desired;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If this ACE has no SID in common with the token,
|
||||
* ignore it as it cannot be used to make an access
|
||||
* determination.
|
||||
*/
|
||||
|
||||
if (!token_sid_in_ace( token, ace))
|
||||
return acc_desired;
|
||||
|
||||
switch (ace->type) {
|
||||
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;
|
||||
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;
|
||||
case SEC_ACE_TYPE_SYSTEM_ALARM:
|
||||
case SEC_ACE_TYPE_SYSTEM_AUDIT:
|
||||
*status = NT_STATUS_NOT_IMPLEMENTED;
|
||||
return 0xFFFFFFFF;
|
||||
default:
|
||||
*status = NT_STATUS_INVALID_PARAMETER;
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
return acc_desired;
|
||||
}
|
||||
|
||||
/*********************************************************************************
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we were granted no access, or we desired bits that we
|
||||
* didn't get, then deny.
|
||||
*/
|
||||
|
||||
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.
|
||||
**********************************************************************************/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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 < 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check access allowed ACEs */
|
||||
acl = sd->dacl;
|
||||
|
||||
if (check_ace_type == SEC_ACE_TYPE_ACCESS_DENIED) {
|
||||
check_ace_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
|
||||
goto check_aces;
|
||||
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);
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
/* 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return *status == NT_STATUS_NOPROBLEMO;
|
||||
/*
|
||||
* If there are no more desired permissions left then
|
||||
* access was allowed.
|
||||
*/
|
||||
|
||||
if (tmp_acc_desired == 0) {
|
||||
*acc_granted = acc_desired;
|
||||
*status = NT_STATUS_NOPROBLEMO;
|
||||
return True;
|
||||
}
|
||||
|
||||
*acc_granted = 0;
|
||||
*status = NT_STATUS_ACCESS_DENIED;
|
||||
return False;
|
||||
}
|
||||
|
Reference in New Issue
Block a user