2000-06-08 12:41:28 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
2008-10-31 20:51:45 +03:00
Copyright ( C ) Andrew Tridgell 2004
Copyright ( C ) Gerald Carter 2005
Copyright ( C ) Volker Lendecke 2007
Copyright ( C ) Jeremy Allison 2008
2010-09-18 05:06:02 +04:00
Copyright ( C ) Andrew Bartlett 2010
2000-06-08 12:41:28 +04:00
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2000-06-08 12:41:28 +04:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2000-06-08 12:41:28 +04:00
*/
2023-03-03 20:41:33 +03:00
# include "replace.h"
# include "lib/util/debug.h"
2010-10-12 08:27:50 +04:00
# include "libcli/security/security.h"
2023-06-29 06:15:43 +03:00
# include "librpc/gen_ndr/conditional_ace.h"
# include "libcli/security/conditional_ace.h"
2000-06-08 12:41:28 +04:00
2001-01-04 22:27:08 +03:00
/* Map generic access rights to object specific rights. This technique is
used to give meaning to assigning read , write , execute and all access to
objects . Each type of object has its own mapping of generic to object
specific access rights . */
2010-09-18 05:06:02 +04:00
void se_map_generic ( uint32_t * access_mask , const struct generic_mapping * mapping )
2001-01-04 22:27:08 +03:00
{
2010-09-18 05:06:02 +04:00
uint32_t old_mask = * access_mask ;
2001-01-04 22:27:08 +03:00
if ( * access_mask & GENERIC_READ_ACCESS ) {
* access_mask & = ~ GENERIC_READ_ACCESS ;
* access_mask | = mapping - > generic_read ;
}
if ( * access_mask & GENERIC_WRITE_ACCESS ) {
* access_mask & = ~ GENERIC_WRITE_ACCESS ;
* access_mask | = mapping - > generic_write ;
}
if ( * access_mask & GENERIC_EXECUTE_ACCESS ) {
* access_mask & = ~ GENERIC_EXECUTE_ACCESS ;
* access_mask | = mapping - > generic_execute ;
}
if ( * access_mask & GENERIC_ALL_ACCESS ) {
* access_mask & = ~ GENERIC_ALL_ACCESS ;
* access_mask | = mapping - > generic_all ;
}
if ( old_mask ! = * access_mask ) {
DEBUG ( 10 , ( " se_map_generic(): mapped mask 0x%08x to 0x%08x \n " ,
old_mask , * access_mask ) ) ;
}
}
2008-10-09 05:06:58 +04:00
/* Map generic access rights to object specific rights for all the ACE's
* in a security_acl .
*/
void security_acl_map_generic ( struct security_acl * sa ,
const struct generic_mapping * mapping )
{
unsigned int i ;
if ( ! sa ) {
return ;
}
for ( i = 0 ; i < sa - > num_aces ; i + + ) {
se_map_generic ( & sa - > aces [ i ] . access_mask , mapping ) ;
}
}
2002-03-15 11:14:10 +03:00
/* Map standard access rights to object specific rights. This technique is
used to give meaning to assigning read , write , execute and all access to
objects . Each type of object has its own mapping of standard to object
specific access rights . */
2010-09-18 05:06:02 +04:00
void se_map_standard ( uint32_t * access_mask , const struct standard_mapping * mapping )
2002-03-15 11:14:10 +03:00
{
2010-09-18 05:06:02 +04:00
uint32_t old_mask = * access_mask ;
2002-03-15 11:14:10 +03:00
2008-10-31 20:51:45 +03:00
if ( * access_mask & SEC_STD_READ_CONTROL ) {
* access_mask & = ~ SEC_STD_READ_CONTROL ;
2002-03-15 11:14:10 +03:00
* access_mask | = mapping - > std_read ;
}
2008-10-31 20:51:45 +03:00
if ( * access_mask & ( SEC_STD_DELETE | SEC_STD_WRITE_DAC | SEC_STD_WRITE_OWNER | SEC_STD_SYNCHRONIZE ) ) {
* access_mask & = ~ ( SEC_STD_DELETE | SEC_STD_WRITE_DAC | SEC_STD_WRITE_OWNER | SEC_STD_SYNCHRONIZE ) ;
2002-03-15 11:14:10 +03:00
* access_mask | = mapping - > std_all ;
}
if ( old_mask ! = * access_mask ) {
DEBUG ( 10 , ( " se_map_standard(): mapped mask 0x%08x to 0x%08x \n " ,
old_mask , * access_mask ) ) ;
}
}
libcli/security: access_check with MAXIMUM_ALLOWED checks callbacks
To help clarify the logic, we make new functions that separate the
deny and allow cases, which helps keep track of what 'yes' and 'no'
mean and which incorporate the logic of token->evaluate_claims
handling, which determines when we want to run a conditional ACE, when
we want to ignore it, and when we want to take offence. In the case
when we decide to run it, we then need to decide whether to apply it
or ignore it based on the result. This last bit differs between allow
and deny aces, hence the two functions.
These functions will replace check_callback_ace_access() over the next
few commits.
In the case where token->evaluate_claims is
CLAIMS_EVALUATION_INVALID_STATE and the DACL contains a conditional
ACE, the maximum allowed is 0, as if it was a "deny everything" ACE.
This is an unexpected case. Most likely the evaluate_claims state
will be NEVER or ALWAYS. In the NEVER case the conditional ACE is
skipped, as would have happened in all cases before 4.20, while in the
ALWAYS case the conditional ACE is run and applied if successful.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2023-09-13 08:25:52 +03:00
enum ace_callback_result {
ACE_CALLBACK_DENY ,
ACE_CALLBACK_ALLOW ,
ACE_CALLBACK_SKIP , /* do not apply this ACE */
ACE_CALLBACK_INVALID /* we don't want to process the conditional ACE */
} ;
static enum ace_callback_result check_callback_ace_allow (
const struct security_ace * ace ,
const struct security_token * token ,
const struct security_descriptor * sd )
{
bool ok ;
int result ;
switch ( token - > evaluate_claims ) {
case CLAIMS_EVALUATION_ALWAYS :
break ;
case CLAIMS_EVALUATION_INVALID_STATE :
DBG_WARNING ( " Refusing to evaluate ACL with "
" conditional ACE against security "
" token with CLAIMS_EVALUATION_INVALID_STATE \n " ) ;
return ACE_CALLBACK_INVALID ;
case CLAIMS_EVALUATION_NEVER :
default :
/*
* We are asked to pretend we never understood this
* ACE type .
*
* By returning SKIP , this ACE will not adjust any
* permission bits making it an effective no - op , which
* was the default behaviour up to Samba 4.19 .
*/
return ACE_CALLBACK_SKIP ;
}
if ( ace - > type ! = SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK & &
ace - > type ! = SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT ) {
/* This indicates a programming error */
DBG_ERR ( " bad conditional allow ACE type: %u \n " , ace - > type ) ;
return ACE_CALLBACK_INVALID ;
}
/*
* Until we discover otherwise , we assume all callback ACEs
* are conditional ACEs .
*/
ok = access_check_conditional_ace ( ace , token , sd , & result ) ;
if ( ! ok ) {
/*
* An error in processing the conditional ACE is
* treated as UNKNOWN , which amounts to a DENY / SKIP
* result .
*
* This is different from the INVALID result which
* means we should not be thinking about conditional
* ACES at all , and will abort the whole access check .
*/
DBG_WARNING ( " callback ACE was not a valid conditional ACE \n " ) ;
return ACE_CALLBACK_SKIP ;
}
if ( result = = ACE_CONDITION_TRUE ) {
return ACE_CALLBACK_ALLOW ;
}
/* UNKNOWN means do not allow */
return ACE_CALLBACK_SKIP ;
}
static enum ace_callback_result check_callback_ace_deny (
const struct security_ace * ace ,
const struct security_token * token ,
const struct security_descriptor * sd )
{
bool ok ;
int result ;
switch ( token - > evaluate_claims ) {
case CLAIMS_EVALUATION_ALWAYS :
break ;
case CLAIMS_EVALUATION_INVALID_STATE :
DBG_WARNING ( " Refusing to evaluate ACL with "
" conditional ACE against security "
" token with CLAIMS_EVALUATION_INVALID_STATE \n " ) ;
return ACE_CALLBACK_INVALID ;
case CLAIMS_EVALUATION_NEVER :
default :
/*
* We are asked to pretend we never understood this
* ACE type .
*/
return ACE_CALLBACK_SKIP ;
}
if ( ace - > type ! = SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK & &
ace - > type ! = SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT ) {
DBG_ERR ( " bad conditional deny ACE type: %u \n " , ace - > type ) ;
return ACE_CALLBACK_INVALID ;
}
/*
* Until we discover otherwise , we assume all callback ACEs
* are conditional ACEs .
*/
ok = access_check_conditional_ace ( ace , token , sd , & result ) ;
if ( ! ok ) {
/*
* An error in processing the conditional ACE is
* treated as UNKNOWN , which means DENY .
*/
DBG_WARNING ( " callback ACE was not a valid conditional ACE \n " ) ;
return ACE_CALLBACK_DENY ;
}
if ( result ! = ACE_CONDITION_FALSE ) {
/* UNKNOWN means deny */
return ACE_CALLBACK_DENY ;
}
return ACE_CALLBACK_SKIP ;
}
2008-10-31 20:51:45 +03:00
/*
perform a SEC_FLAG_MAXIMUM_ALLOWED access check
*/
2010-09-18 05:06:02 +04:00
static uint32_t access_check_max_allowed ( const struct security_descriptor * sd ,
2021-10-22 21:33:03 +03:00
const struct security_token * token ,
enum implicit_owner_rights implicit_owner_rights )
2000-06-08 12:41:28 +04:00
{
2008-10-31 20:51:45 +03:00
uint32_t denied = 0 , granted = 0 ;
2019-03-01 20:20:35 +03:00
bool am_owner = false ;
bool have_owner_rights_ace = false ;
2008-10-31 20:51:45 +03:00
unsigned i ;
if ( sd - > dacl = = NULL ) {
2019-02-27 20:07:03 +03:00
if ( security_token_has_sid ( token , sd - > owner_sid ) ) {
2021-10-22 21:33:03 +03:00
switch ( implicit_owner_rights ) {
case IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS :
granted | = SEC_STD_WRITE_DAC ;
FALL_THROUGH ;
case IMPLICIT_OWNER_READ_CONTROL_RIGHTS :
granted | = SEC_STD_READ_CONTROL ;
break ;
}
2019-02-27 20:07:03 +03:00
}
return granted ;
2008-10-31 20:51:45 +03:00
}
2008-11-01 04:04:53 +03:00
2019-03-01 20:20:35 +03:00
if ( security_token_has_sid ( token , sd - > owner_sid ) ) {
/*
* Check for explicit owner rights : if there are none , we remove
* the default owner right SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL
* from remaining_access . Otherwise we just process the
* explicitly granted rights when processing the ACEs .
*/
am_owner = true ;
for ( i = 0 ; i < sd - > dacl - > num_aces ; i + + ) {
struct security_ace * ace = & sd - > dacl - > aces [ i ] ;
if ( ace - > flags & SEC_ACE_FLAG_INHERIT_ONLY ) {
continue ;
}
have_owner_rights_ace = dom_sid_equal (
& ace - > trustee , & global_sid_Owner_Rights ) ;
if ( have_owner_rights_ace ) {
break ;
}
}
}
if ( am_owner & & ! have_owner_rights_ace ) {
2021-10-22 21:33:03 +03:00
switch ( implicit_owner_rights ) {
case IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS :
granted | = SEC_STD_WRITE_DAC ;
FALL_THROUGH ;
case IMPLICIT_OWNER_READ_CONTROL_RIGHTS :
granted | = SEC_STD_READ_CONTROL ;
break ;
}
2019-03-01 20:20:35 +03:00
}
2008-10-31 20:51:45 +03:00
for ( i = 0 ; i < sd - > dacl - > num_aces ; i + + ) {
struct security_ace * ace = & sd - > dacl - > aces [ i ] ;
2019-03-01 20:20:35 +03:00
bool is_owner_rights_ace = false ;
2000-06-08 12:41:28 +04:00
2008-10-31 20:51:45 +03:00
if ( ace - > flags & SEC_ACE_FLAG_INHERIT_ONLY ) {
continue ;
}
2000-06-08 12:41:28 +04:00
2019-03-01 20:20:35 +03:00
if ( am_owner ) {
is_owner_rights_ace = dom_sid_equal (
& ace - > trustee , & global_sid_Owner_Rights ) ;
2019-02-27 20:07:03 +03:00
}
2019-03-01 20:20:35 +03:00
if ( ! is_owner_rights_ace & &
! security_token_has_sid ( token , & ace - > trustee ) )
{
2008-10-31 20:51:45 +03:00
continue ;
}
2001-09-26 04:05:03 +04:00
2008-10-31 20:51:45 +03:00
switch ( ace - > type ) {
case SEC_ACE_TYPE_ACCESS_ALLOWED :
granted | = ace - > access_mask ;
break ;
case SEC_ACE_TYPE_ACCESS_DENIED :
case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT :
2019-03-01 20:57:23 +03:00
denied | = ~ granted & ace - > access_mask ;
2008-10-31 20:51:45 +03:00
break ;
libcli/security: access_check with MAXIMUM_ALLOWED checks callbacks
To help clarify the logic, we make new functions that separate the
deny and allow cases, which helps keep track of what 'yes' and 'no'
mean and which incorporate the logic of token->evaluate_claims
handling, which determines when we want to run a conditional ACE, when
we want to ignore it, and when we want to take offence. In the case
when we decide to run it, we then need to decide whether to apply it
or ignore it based on the result. This last bit differs between allow
and deny aces, hence the two functions.
These functions will replace check_callback_ace_access() over the next
few commits.
In the case where token->evaluate_claims is
CLAIMS_EVALUATION_INVALID_STATE and the DACL contains a conditional
ACE, the maximum allowed is 0, as if it was a "deny everything" ACE.
This is an unexpected case. Most likely the evaluate_claims state
will be NEVER or ALWAYS. In the NEVER case the conditional ACE is
skipped, as would have happened in all cases before 4.20, while in the
ALWAYS case the conditional ACE is run and applied if successful.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2023-09-13 08:25:52 +03:00
case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK :
{
enum ace_callback_result allow =
check_callback_ace_allow ( ace , token , sd ) ;
if ( allow = = ACE_CALLBACK_INVALID ) {
return 0 ;
}
if ( allow = = ACE_CALLBACK_ALLOW ) {
granted | = ace - > access_mask ;
}
break ;
}
case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK :
{
enum ace_callback_result deny =
check_callback_ace_deny ( ace , token , sd ) ;
if ( deny = = ACE_CALLBACK_INVALID ) {
return 0 ;
}
if ( deny = = ACE_CALLBACK_DENY ) {
denied | = ~ granted & ace - > access_mask ;
}
break ;
}
2008-10-31 20:51:45 +03:00
default : /* Other ACE types not handled/supported */
break ;
}
}
2000-06-08 12:41:28 +04:00
2008-10-31 20:51:45 +03:00
return granted & ~ denied ;
}
2000-08-09 22:40:48 +04:00
2023-09-13 08:25:34 +03:00
2021-10-22 21:33:03 +03:00
static NTSTATUS se_access_check_implicit_owner ( const struct security_descriptor * sd ,
const struct security_token * token ,
uint32_t access_desired ,
uint32_t * access_granted ,
enum implicit_owner_rights implicit_owner_rights )
2008-10-31 20:51:45 +03:00
{
2010-09-18 04:54:37 +04:00
uint32_t i ;
2008-10-31 20:51:45 +03:00
uint32_t bits_remaining ;
2012-01-11 00:58:13 +04:00
uint32_t explicitly_denied_bits = 0 ;
2019-03-01 20:20:35 +03:00
bool am_owner = false ;
bool have_owner_rights_ace = false ;
2000-06-08 12:41:28 +04:00
2023-09-15 03:36:56 +03:00
switch ( token - > evaluate_claims ) {
case CLAIMS_EVALUATION_INVALID_STATE :
if ( token - > num_local_claims > 0 | |
token - > num_user_claims > 0 | |
token - > num_device_claims > 0 | |
token - > num_device_sids > 0 ) {
DBG_WARNING ( " Refusing to evaluate token with claims or device SIDs but also "
" with CLAIMS_EVALUATION_INVALID_STATE \n " ) ;
return NT_STATUS_INVALID_TOKEN ;
}
break ;
case CLAIMS_EVALUATION_ALWAYS :
case CLAIMS_EVALUATION_NEVER :
break ;
}
2008-10-31 20:51:45 +03:00
* access_granted = access_desired ;
bits_remaining = access_desired ;
2000-08-08 23:34:34 +04:00
2008-10-31 20:51:45 +03:00
/* handle the maximum allowed flag */
if ( access_desired & SEC_FLAG_MAXIMUM_ALLOWED ) {
2008-11-04 09:42:53 +03:00
uint32_t orig_access_desired = access_desired ;
2021-10-22 21:33:03 +03:00
access_desired | = access_check_max_allowed ( sd , token , implicit_owner_rights ) ;
2008-10-31 20:51:45 +03:00
access_desired & = ~ SEC_FLAG_MAXIMUM_ALLOWED ;
* access_granted = access_desired ;
2011-03-21 13:21:57 +03:00
bits_remaining = access_desired ;
2008-11-04 09:42:53 +03:00
DEBUG ( 10 , ( " se_access_check: MAX desired = 0x%x, granted = 0x%x, remaining = 0x%x \n " ,
orig_access_desired ,
* access_granted ,
bits_remaining ) ) ;
2000-06-08 12:41:28 +04:00
}
2011-03-21 13:21:57 +03:00
/* a NULL dacl allows access */
if ( ( sd - > type & SEC_DESC_DACL_PRESENT ) & & sd - > dacl = = NULL ) {
* access_granted = access_desired ;
return NT_STATUS_OK ;
}
2008-10-31 20:51:45 +03:00
if ( sd - > dacl = = NULL ) {
goto done ;
2000-08-02 06:11:55 +04:00
}
2000-07-06 10:57:22 +04:00
2019-03-01 20:20:35 +03:00
if ( security_token_has_sid ( token , sd - > owner_sid ) ) {
/*
* Check for explicit owner rights : if there are none , we remove
* the default owner right SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL
* from remaining_access . Otherwise we just process the
* explicitly granted rights when processing the ACEs .
*/
am_owner = true ;
for ( i = 0 ; i < sd - > dacl - > num_aces ; i + + ) {
struct security_ace * ace = & sd - > dacl - > aces [ i ] ;
if ( ace - > flags & SEC_ACE_FLAG_INHERIT_ONLY ) {
continue ;
}
have_owner_rights_ace = dom_sid_equal (
& ace - > trustee , & global_sid_Owner_Rights ) ;
if ( have_owner_rights_ace ) {
break ;
}
}
}
if ( am_owner & & ! have_owner_rights_ace ) {
2021-10-22 21:33:03 +03:00
switch ( implicit_owner_rights ) {
case IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS :
bits_remaining & = ~ SEC_STD_WRITE_DAC ;
FALL_THROUGH ;
case IMPLICIT_OWNER_READ_CONTROL_RIGHTS :
bits_remaining & = ~ SEC_STD_READ_CONTROL ;
break ;
}
2019-03-01 20:20:35 +03:00
}
2008-10-31 20:51:45 +03:00
/* check each ace in turn. */
for ( i = 0 ; bits_remaining & & i < sd - > dacl - > num_aces ; i + + ) {
struct security_ace * ace = & sd - > dacl - > aces [ i ] ;
2019-03-01 20:20:35 +03:00
bool is_owner_rights_ace = false ;
2008-10-31 20:51:45 +03:00
if ( ace - > flags & SEC_ACE_FLAG_INHERIT_ONLY ) {
continue ;
2000-08-04 23:56:58 +04:00
}
2000-07-06 10:57:22 +04:00
2019-03-01 20:20:35 +03:00
if ( am_owner ) {
is_owner_rights_ace = dom_sid_equal (
& ace - > trustee , & global_sid_Owner_Rights ) ;
2012-03-14 03:47:17 +04:00
}
2019-03-01 20:20:35 +03:00
if ( ! is_owner_rights_ace & &
! security_token_has_sid ( token , & ace - > trustee ) )
{
2008-10-31 20:51:45 +03:00
continue ;
}
2000-07-06 10:57:22 +04:00
2008-10-31 20:51:45 +03:00
switch ( ace - > type ) {
case SEC_ACE_TYPE_ACCESS_ALLOWED :
bits_remaining & = ~ ace - > access_mask ;
break ;
case SEC_ACE_TYPE_ACCESS_DENIED :
case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT :
2012-01-11 00:58:13 +04:00
explicitly_denied_bits | = ( bits_remaining & ace - > access_mask ) ;
2008-10-31 20:51:45 +03:00
break ;
2023-09-13 08:25:34 +03:00
case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK :
2023-09-15 03:36:56 +03:00
{
2023-09-20 08:35:18 +03:00
enum ace_callback_result allow =
check_callback_ace_allow ( ace , token , sd ) ;
if ( allow = = ACE_CALLBACK_INVALID ) {
2023-09-15 03:36:56 +03:00
return NT_STATUS_INVALID_ACE_CONDITION ;
}
2023-09-20 08:35:18 +03:00
if ( allow = = ACE_CALLBACK_ALLOW ) {
2023-09-13 08:25:34 +03:00
bits_remaining & = ~ ace - > access_mask ;
}
break ;
2023-09-15 03:36:56 +03:00
}
2023-09-13 08:25:34 +03:00
case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK :
2023-09-20 08:35:18 +03:00
case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT :
2023-09-15 03:36:56 +03:00
{
2023-09-20 08:35:18 +03:00
enum ace_callback_result deny =
check_callback_ace_deny ( ace , token , sd ) ;
if ( deny = = ACE_CALLBACK_INVALID ) {
2023-09-15 03:36:56 +03:00
return NT_STATUS_INVALID_ACE_CONDITION ;
2023-09-13 08:25:34 +03:00
}
2023-09-20 08:35:18 +03:00
if ( deny = = ACE_CALLBACK_DENY ) {
2023-09-13 08:25:34 +03:00
explicitly_denied_bits | = ( bits_remaining & ace - > access_mask ) ;
}
break ;
2023-09-15 03:36:56 +03:00
}
2023-09-13 08:25:34 +03:00
2008-10-31 20:51:45 +03:00
default : /* Other ACE types not handled/supported */
break ;
}
}
2013-02-23 20:41:27 +04:00
/* Explicitly denied bits always override */
bits_remaining | = explicitly_denied_bits ;
2012-03-10 02:54:38 +04:00
/*
* We check privileges here because they override even DENY entries .
*/
/* Does the user have the privilege to gain SEC_PRIV_SECURITY? */
if ( bits_remaining & SEC_FLAG_SYSTEM_SECURITY ) {
if ( security_token_has_privilege ( token , SEC_PRIV_SECURITY ) ) {
bits_remaining & = ~ SEC_FLAG_SYSTEM_SECURITY ;
} else {
return NT_STATUS_PRIVILEGE_NOT_HELD ;
}
}
if ( ( bits_remaining & SEC_STD_WRITE_OWNER ) & &
security_token_has_privilege ( token , SEC_PRIV_TAKE_OWNERSHIP ) ) {
bits_remaining & = ~ ( SEC_STD_WRITE_OWNER ) ;
}
2008-10-31 20:51:45 +03:00
done :
if ( bits_remaining ! = 0 ) {
2009-02-03 04:10:27 +03:00
* access_granted = bits_remaining ;
2008-10-31 20:51:45 +03:00
return NT_STATUS_ACCESS_DENIED ;
2000-06-08 12:41:28 +04:00
}
2001-01-19 19:56:58 +03:00
2008-10-31 20:51:45 +03:00
return NT_STATUS_OK ;
}
2010-09-18 06:55:31 +04:00
2021-10-22 21:33:03 +03:00
/*
The main entry point for access checking . If returning ACCESS_DENIED
this function returns the denied bits in the uint32_t pointed
to by the access_granted pointer .
*/
NTSTATUS se_access_check ( const struct security_descriptor * sd ,
const struct security_token * token ,
uint32_t access_desired ,
uint32_t * access_granted )
{
return se_access_check_implicit_owner ( sd ,
token ,
access_desired ,
access_granted ,
IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS ) ;
}
2012-08-28 02:41:18 +04:00
/*
The main entry point for access checking FOR THE FILE SERVER ONLY !
If returning ACCESS_DENIED this function returns the denied bits in
the uint32_t pointed to by the access_granted pointer .
*/
NTSTATUS se_file_access_check ( const struct security_descriptor * sd ,
const struct security_token * token ,
bool priv_open_requested ,
uint32_t access_desired ,
uint32_t * access_granted )
{
uint32_t bits_remaining ;
NTSTATUS status ;
if ( ! priv_open_requested ) {
/* Fall back to generic se_access_check(). */
2021-10-22 21:33:03 +03:00
return se_access_check_implicit_owner ( sd ,
token ,
access_desired ,
access_granted ,
IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS ) ;
2012-08-28 02:41:18 +04:00
}
/*
* We need to handle the maximum allowed flag
* outside of se_access_check ( ) , as we need to
* add in the access allowed by the privileges
* as well .
*/
if ( access_desired & SEC_FLAG_MAXIMUM_ALLOWED ) {
uint32_t orig_access_desired = access_desired ;
2021-10-22 21:33:03 +03:00
access_desired | = access_check_max_allowed ( sd , token , true ) ;
2012-08-28 02:41:18 +04:00
access_desired & = ~ SEC_FLAG_MAXIMUM_ALLOWED ;
if ( security_token_has_privilege ( token , SEC_PRIV_BACKUP ) ) {
access_desired | = SEC_RIGHTS_PRIV_BACKUP ;
}
if ( security_token_has_privilege ( token , SEC_PRIV_RESTORE ) ) {
access_desired | = SEC_RIGHTS_PRIV_RESTORE ;
}
DEBUG ( 10 , ( " se_file_access_check: MAX desired = 0x%x "
" mapped to 0x%x \n " ,
orig_access_desired ,
access_desired ) ) ;
}
2021-10-22 21:33:03 +03:00
status = se_access_check_implicit_owner ( sd ,
token ,
access_desired ,
access_granted ,
IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS ) ;
2012-08-28 02:41:18 +04:00
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) ) {
return status ;
}
bits_remaining = * access_granted ;
/* Check if we should override with privileges. */
if ( ( bits_remaining & SEC_RIGHTS_PRIV_BACKUP ) & &
security_token_has_privilege ( token , SEC_PRIV_BACKUP ) ) {
bits_remaining & = ~ ( SEC_RIGHTS_PRIV_BACKUP ) ;
}
if ( ( bits_remaining & SEC_RIGHTS_PRIV_RESTORE ) & &
security_token_has_privilege ( token , SEC_PRIV_RESTORE ) ) {
bits_remaining & = ~ ( SEC_RIGHTS_PRIV_RESTORE ) ;
}
if ( bits_remaining ! = 0 ) {
* access_granted = bits_remaining ;
return NT_STATUS_ACCESS_DENIED ;
}
return NT_STATUS_OK ;
}
2010-09-18 06:55:31 +04:00
2023-01-26 21:57:27 +03:00
static const struct GUID * get_ace_object_type ( const struct security_ace * ace )
2010-09-18 06:55:31 +04:00
{
2013-01-16 13:05:56 +04:00
if ( ace - > object . object . flags & SEC_ACE_OBJECT_TYPE_PRESENT ) {
return & ace - > object . object . type . type ;
}
2010-09-18 06:55:31 +04:00
2013-01-16 13:05:56 +04:00
return NULL ;
2010-09-18 06:55:31 +04:00
}
2018-07-19 07:03:36 +03:00
/**
* Evaluates access rights specified in a object - specific ACE for an AD object .
* This logic corresponds to MS - ADTS 5.1 .3 .3 .3 Checking Object - Specific Access .
* @ param [ in ] ace - the ACE being processed
* @ param [ in / out ] tree - remaining_access gets updated for the tree
* @ param [ out ] grant_access - set to true if the ACE grants sufficient access
* rights to the object / attribute
* @ returns NT_STATUS_OK , unless access was denied
*/
2023-01-26 21:57:27 +03:00
static NTSTATUS check_object_specific_access ( const struct security_ace * ace ,
2018-07-19 07:03:36 +03:00
struct object_tree * tree ,
bool * grant_access )
{
struct object_tree * node = NULL ;
const struct GUID * type = NULL ;
* grant_access = false ;
2018-07-20 04:13:50 +03:00
/* if no tree was supplied, we can't do object-specific access checks */
2018-07-19 07:03:36 +03:00
if ( ! tree ) {
return NT_STATUS_OK ;
}
2018-07-20 04:13:50 +03:00
/* Get the ObjectType GUID this ACE applies to */
type = get_ace_object_type ( ace ) ;
/*
* If the ACE doesn ' t have a type , then apply it to the whole tree , i . e .
* treat ' OA ' ACEs as ' A ' and ' OD ' as ' D '
*/
2018-07-19 07:03:36 +03:00
if ( ! type ) {
node = tree ;
} else {
2018-07-20 04:13:50 +03:00
/* skip it if the ACE's ObjectType GUID is not in the tree */
node = get_object_tree_by_GUID ( tree , type ) ;
if ( ! node ) {
2018-07-19 07:03:36 +03:00
return NT_STATUS_OK ;
}
}
2023-09-25 04:36:59 +03:00
if ( ace - > type = = SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT | |
ace - > type = = SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT ) {
2018-07-20 04:13:50 +03:00
/* apply the access rights to this node, and any children */
2018-07-19 07:03:36 +03:00
object_tree_modify_access ( node , ace - > access_mask ) ;
2018-07-20 04:13:50 +03:00
/*
* Currently all nodes in the tree request the same access mask ,
* so we can use any node to check if processing this ACE now
* means the requested access has been granted
*/
2018-07-19 07:03:36 +03:00
if ( node - > remaining_access = = 0 ) {
* grant_access = true ;
return NT_STATUS_OK ;
}
CVE-2018-10919 security: Fix checking of object-specific CONTROL_ACCESS rights
An 'Object Access Allowed' ACE that assigned 'Control Access' (CR)
rights to a specific attribute would not actually grant access.
What was happening was the remaining_access mask for the object_tree
nodes would be Read Property (RP) + Control Access (CR). The ACE mapped
to the schemaIDGUID for a given attribute, which would end up being a
child node in the tree. So the CR bit was cleared for a child node, but
not the rest of the tree. We would then check the user had the RP access
right, which it did. However, the RP right was cleared for another node
in the tree, which still had the CR bit set in its remaining_access
bitmap, so Samba would not grant access.
Generally, the remaining_access only ever has one bit set, which means
this isn't a problem normally. However, in the Control Access case there
are 2 separate bits being checked, i.e. RP + CR.
One option to fix this problem would be to clear the remaining_access
for the tree instead of just the node. However, the Windows spec is
actually pretty clear on this: if the ACE has a CR right present, then
you can stop any further access checks.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=13434
Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
2018-07-20 04:01:00 +03:00
/*
* As per 5.1 .3 .3 .4 Checking Control Access Right - Based Access ,
* if the CONTROL_ACCESS right is present , then we can grant
* access and stop any further access checks
*/
if ( ace - > access_mask & SEC_ADS_CONTROL_ACCESS ) {
* grant_access = true ;
return NT_STATUS_OK ;
}
2018-07-19 07:03:36 +03:00
} else {
2018-07-20 04:13:50 +03:00
/* this ACE denies access to the requested object/attribute */
2018-07-19 07:03:36 +03:00
if ( node - > remaining_access & ace - > access_mask ) {
return NT_STATUS_ACCESS_DENIED ;
}
}
return NT_STATUS_OK ;
}
2023-06-29 06:15:43 +03:00
2021-10-22 21:33:03 +03:00
NTSTATUS sec_access_check_ds_implicit_owner ( const struct security_descriptor * sd ,
const struct security_token * token ,
uint32_t access_desired ,
uint32_t * access_granted ,
struct object_tree * tree ,
2023-01-26 21:57:27 +03:00
const struct dom_sid * replace_sid ,
2021-10-22 21:33:03 +03:00
enum implicit_owner_rights implicit_owner_rights )
2010-09-18 06:55:31 +04:00
{
2013-01-16 12:43:44 +04:00
uint32_t i ;
uint32_t bits_remaining ;
* access_granted = access_desired ;
bits_remaining = access_desired ;
/* handle the maximum allowed flag */
if ( access_desired & SEC_FLAG_MAXIMUM_ALLOWED ) {
2021-10-22 21:33:03 +03:00
access_desired | = access_check_max_allowed ( sd , token , implicit_owner_rights ) ;
2013-01-16 12:43:44 +04:00
access_desired & = ~ SEC_FLAG_MAXIMUM_ALLOWED ;
* access_granted = access_desired ;
2011-03-21 13:21:57 +03:00
bits_remaining = access_desired ;
2013-01-16 12:43:44 +04:00
}
2010-09-18 06:55:31 +04:00
2013-01-16 12:43:44 +04:00
if ( access_desired & SEC_FLAG_SYSTEM_SECURITY ) {
if ( security_token_has_privilege ( token , SEC_PRIV_SECURITY ) ) {
bits_remaining & = ~ SEC_FLAG_SYSTEM_SECURITY ;
} else {
return NT_STATUS_PRIVILEGE_NOT_HELD ;
}
}
2010-09-18 06:55:31 +04:00
2011-03-21 13:21:57 +03:00
/* the owner always gets SEC_STD_WRITE_DAC and SEC_STD_READ_CONTROL */
if ( ( bits_remaining & ( SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL ) ) & &
security_token_has_sid ( token , sd - > owner_sid ) ) {
2021-10-22 21:33:03 +03:00
switch ( implicit_owner_rights ) {
case IMPLICIT_OWNER_READ_CONTROL_AND_WRITE_DAC_RIGHTS :
bits_remaining & = ~ SEC_STD_WRITE_DAC ;
FALL_THROUGH ;
case IMPLICIT_OWNER_READ_CONTROL_RIGHTS :
bits_remaining & = ~ SEC_STD_READ_CONTROL ;
break ;
}
2011-03-21 13:21:57 +03:00
}
2013-10-15 03:06:38 +04:00
/* SEC_PRIV_TAKE_OWNERSHIP grants SEC_STD_WRITE_OWNER */
if ( ( bits_remaining & ( SEC_STD_WRITE_OWNER ) ) & &
security_token_has_privilege ( token , SEC_PRIV_TAKE_OWNERSHIP ) ) {
bits_remaining & = ~ ( SEC_STD_WRITE_OWNER ) ;
2011-03-21 13:21:57 +03:00
}
2013-01-16 12:43:44 +04:00
/* a NULL dacl allows access */
if ( ( sd - > type & SEC_DESC_DACL_PRESENT ) & & sd - > dacl = = NULL ) {
* access_granted = access_desired ;
return NT_STATUS_OK ;
}
2010-09-18 06:55:31 +04:00
2013-01-16 12:43:44 +04:00
if ( sd - > dacl = = NULL ) {
goto done ;
}
2010-09-18 06:55:31 +04:00
2013-01-16 12:43:44 +04:00
/* check each ace in turn. */
for ( i = 0 ; bits_remaining & & i < sd - > dacl - > num_aces ; i + + ) {
2023-01-26 21:57:27 +03:00
const struct dom_sid * trustee ;
const struct security_ace * ace = & sd - > dacl - > aces [ i ] ;
2018-07-19 07:03:36 +03:00
NTSTATUS status ;
bool grant_access = false ;
2010-09-18 06:55:31 +04:00
2013-01-16 12:43:44 +04:00
if ( ace - > flags & SEC_ACE_FLAG_INHERIT_ONLY ) {
continue ;
}
2013-01-16 12:46:48 +04:00
2023-09-11 05:13:47 +03:00
if ( dom_sid_equal ( & ace - > trustee , & global_sid_Self ) & & replace_sid ) {
2010-09-18 06:55:31 +04:00
trustee = replace_sid ;
2013-01-16 12:46:48 +04:00
} else {
2010-09-18 06:55:31 +04:00
trustee = & ace - > trustee ;
}
2013-01-16 12:46:48 +04:00
2013-01-16 12:43:44 +04:00
if ( ! security_token_has_sid ( token , trustee ) ) {
continue ;
}
switch ( ace - > type ) {
case SEC_ACE_TYPE_ACCESS_ALLOWED :
2013-01-16 12:46:48 +04:00
if ( tree ) {
2013-01-16 12:43:44 +04:00
object_tree_modify_access ( tree , ace - > access_mask ) ;
2013-01-16 12:46:48 +04:00
}
2013-01-16 12:43:44 +04:00
bits_remaining & = ~ ace - > access_mask ;
break ;
case SEC_ACE_TYPE_ACCESS_DENIED :
if ( bits_remaining & ace - > access_mask ) {
return NT_STATUS_ACCESS_DENIED ;
}
break ;
2023-06-29 06:15:43 +03:00
case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK :
2023-09-13 08:24:57 +03:00
{
enum ace_callback_result allow =
check_callback_ace_allow ( ace , token , sd ) ;
if ( allow = = ACE_CALLBACK_INVALID ) {
return NT_STATUS_INVALID_ACE_CONDITION ;
}
if ( allow = = ACE_CALLBACK_ALLOW ) {
bits_remaining & = ~ ace - > access_mask ;
2023-06-29 06:15:43 +03:00
}
2023-09-13 08:24:57 +03:00
break ;
}
2023-06-29 06:15:43 +03:00
2023-09-13 08:24:57 +03:00
case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK :
{
enum ace_callback_result deny =
check_callback_ace_deny ( ace , token , sd ) ;
if ( deny = = ACE_CALLBACK_INVALID ) {
return NT_STATUS_INVALID_ACE_CONDITION ;
}
if ( deny = = ACE_CALLBACK_DENY ) {
if ( bits_remaining & ace - > access_mask ) {
return NT_STATUS_ACCESS_DENIED ;
}
2023-06-29 06:15:43 +03:00
}
break ;
2023-09-13 08:24:57 +03:00
}
2023-06-29 06:15:43 +03:00
2013-01-16 12:43:44 +04:00
case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT :
case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT :
2018-07-19 07:03:36 +03:00
status = check_object_specific_access ( ace , tree ,
& grant_access ) ;
2013-01-16 12:43:44 +04:00
2018-07-19 07:03:36 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2013-01-16 12:46:48 +04:00
}
2013-01-16 12:43:44 +04:00
2018-07-19 07:03:36 +03:00
if ( grant_access ) {
return NT_STATUS_OK ;
2013-01-16 12:43:44 +04:00
}
break ;
2023-09-25 04:36:59 +03:00
case SEC_ACE_TYPE_ACCESS_ALLOWED_CALLBACK_OBJECT :
{
/*
* if the callback says ALLOW , we treat this as a
* SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT .
*
* Otherwise we act as if this ACE does not exist .
*/
enum ace_callback_result allow =
check_callback_ace_allow ( ace , token , sd ) ;
if ( allow = = ACE_CALLBACK_INVALID ) {
return NT_STATUS_INVALID_ACE_CONDITION ;
}
if ( allow ! = ACE_CALLBACK_ALLOW ) {
break ;
}
status = check_object_specific_access ( ace , tree ,
& grant_access ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( grant_access ) {
return NT_STATUS_OK ;
}
break ;
}
case SEC_ACE_TYPE_ACCESS_DENIED_CALLBACK_OBJECT :
{
/*
* ACCESS_DENIED_OBJECT ACEs can ' t grant access - -
* they either don ' t match the object and slide
* harmlessly past or they return
* NT_STATUS_ACCESS_DENIED .
*
* ACCESS_DENIED_CALLBACK_OBJECT ACEs add another way
* of not applying , and another way of failing .
*/
enum ace_callback_result deny =
check_callback_ace_deny ( ace , token , sd ) ;
if ( deny = = ACE_CALLBACK_INVALID ) {
return NT_STATUS_INVALID_ACE_CONDITION ;
}
if ( deny ! = ACE_CALLBACK_DENY ) {
break ;
}
status = check_object_specific_access ( ace , tree ,
& grant_access ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
break ;
}
2013-01-16 12:43:44 +04:00
default : /* Other ACE types not handled/supported */
break ;
}
}
2010-09-18 06:55:31 +04:00
done :
2013-01-16 12:43:44 +04:00
if ( bits_remaining ! = 0 ) {
return NT_STATUS_ACCESS_DENIED ;
}
2010-09-18 06:55:31 +04:00
2013-01-16 12:43:44 +04:00
return NT_STATUS_OK ;
2010-09-18 06:55:31 +04:00
}
2021-10-22 21:33:03 +03:00
/**
* @ brief Perform directoryservice ( DS ) related access checks for a given user
*
* Perform DS access checks for the user represented by its security_token , on
* the provided security descriptor . If an tree associating GUID and access
* required is provided then object access ( OA ) are checked as well . *
2023-04-27 16:56:42 +03:00
* @ param [ in ] sd The security descriptor against which the required
2021-10-22 21:33:03 +03:00
* access are requested
*
* @ param [ in ] token The security_token associated with the user to
* test
*
* @ param [ in ] access_desired A bitfield of rights that must be granted for the
* given user in the specified SD .
*
* If one
* of the entry in the tree grants all the requested rights for the given GUID
* FIXME
* tree can be null if not null it ' s the
* Lots of code duplication , it will be united in just one
* function eventually */
NTSTATUS sec_access_check_ds ( const struct security_descriptor * sd ,
const struct security_token * token ,
uint32_t access_desired ,
uint32_t * access_granted ,
struct object_tree * tree ,
2024-01-09 05:33:38 +03:00
const struct dom_sid * replace_sid )
2021-10-22 21:33:03 +03:00
{
return sec_access_check_ds_implicit_owner ( sd ,
token ,
access_desired ,
access_granted ,
tree ,
replace_sid ,
IMPLICIT_OWNER_READ_CONTROL_RIGHTS ) ;
}