2015-04-24 18:37:13 +03:00
/*
Unix SMB / CIFS implementation .
Security Descriptor ( SD ) helper functions
Copyright ( C ) Andrew Tridgell 2000
Copyright ( C ) Tim Potter 2000
Copyright ( C ) Jeremy Allison 2000
Copyright ( C ) Jelmer Vernooij 2003
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
the Free Software Foundation ; either version 3 of the License , or
( 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
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "libsmb/libsmb.h"
# include "util_sd.h"
# include "librpc/gen_ndr/ndr_lsa.h"
# include "../libcli/security/security.h"
# include "rpc_client/cli_pipe.h"
# include "rpc_client/cli_lsarpc.h"
2020-08-07 21:17:34 +03:00
# include "lib/util/string_wrappers.h"
2015-04-24 18:37:13 +03:00
2015-04-24 19:22:14 +03:00
/* These values discovered by inspection */
struct perm_value {
const char * perm ;
2015-05-10 02:33:10 +03:00
uint32_t mask ;
2015-04-24 19:22:14 +03:00
} ;
static const struct perm_value special_values [ ] = {
{ " R " , SEC_RIGHTS_FILE_READ } ,
{ " W " , SEC_RIGHTS_FILE_WRITE } ,
{ " X " , SEC_RIGHTS_FILE_EXECUTE } ,
{ " D " , SEC_STD_DELETE } ,
{ " P " , SEC_STD_WRITE_DAC } ,
{ " O " , SEC_STD_WRITE_OWNER } ,
{ NULL , 0 } ,
} ;
static const struct perm_value standard_values [ ] = {
{ " READ " , SEC_RIGHTS_DIR_READ | SEC_DIR_TRAVERSE } ,
{ " CHANGE " , SEC_RIGHTS_DIR_READ | SEC_STD_DELETE | \
2022-02-02 12:02:37 +03:00
SEC_DIR_DELETE_CHILD | \
2015-04-24 19:22:14 +03:00
SEC_RIGHTS_DIR_WRITE | SEC_DIR_TRAVERSE } ,
{ " FULL " , SEC_RIGHTS_DIR_ALL } ,
{ NULL , 0 } ,
} ;
2015-04-24 19:28:02 +03:00
static const struct {
uint16_t mask ;
const char * str ;
const char * desc ;
} sec_desc_ctrl_bits [ ] = {
{ SEC_DESC_OWNER_DEFAULTED , " OD " , " Owner Defaulted " } ,
{ SEC_DESC_GROUP_DEFAULTED , " GD " , " Group Defaulted " } ,
{ SEC_DESC_DACL_PRESENT , " DP " , " DACL Present " } ,
{ SEC_DESC_DACL_DEFAULTED , " DD " , " DACL Defaulted " } ,
{ SEC_DESC_SACL_PRESENT , " SP " , " SACL Present " } ,
{ SEC_DESC_SACL_DEFAULTED , " SD " , " SACL Defaulted " } ,
{ SEC_DESC_DACL_TRUSTED , " DT " , " DACL Trusted " } ,
{ SEC_DESC_SERVER_SECURITY , " SS " , " Server Security " } ,
{ SEC_DESC_DACL_AUTO_INHERIT_REQ , " DR " , " DACL Inheritance Required " } ,
{ SEC_DESC_SACL_AUTO_INHERIT_REQ , " SR " , " SACL Inheritance Required " } ,
{ SEC_DESC_DACL_AUTO_INHERITED , " DI " , " DACL Auto Inherited " } ,
{ SEC_DESC_SACL_AUTO_INHERITED , " SI " , " SACL Auto Inherited " } ,
{ SEC_DESC_DACL_PROTECTED , " PD " , " DACL Protected " } ,
{ SEC_DESC_SACL_PROTECTED , " PS " , " SACL Protected " } ,
{ SEC_DESC_RM_CONTROL_VALID , " RM " , " RM Control Valid " } ,
{ SEC_DESC_SELF_RELATIVE , " SR " , " Self Relative " } ,
} ;
2015-04-24 18:37:13 +03:00
/* Open cli connection and policy handle */
static NTSTATUS cli_lsa_lookup_sid ( struct cli_state * cli ,
const struct dom_sid * sid ,
TALLOC_CTX * mem_ctx ,
enum lsa_SidType * type ,
char * * domain , char * * name )
{
2017-06-14 02:56:48 +03:00
struct smbXcli_tcon * orig_tcon = NULL ;
2022-08-24 03:18:16 +03:00
char * orig_share = NULL ;
2015-04-24 18:37:13 +03:00
struct rpc_pipe_client * p = NULL ;
struct policy_handle handle ;
NTSTATUS status ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
enum lsa_SidType * types ;
char * * domains ;
char * * names ;
2017-06-14 02:56:48 +03:00
if ( cli_state_has_tcon ( cli ) ) {
2022-08-24 03:18:16 +03:00
cli_state_save_tcon_share ( cli , & orig_tcon , & orig_share ) ;
2017-06-14 02:56:48 +03:00
}
2016-12-08 09:13:57 +03:00
status = cli_tree_connect ( cli , " IPC$ " , " ????? " , NULL ) ;
2015-04-24 18:37:13 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto tcon_fail ;
}
status = cli_rpc_pipe_open_noauth ( cli , & ndr_table_lsarpc ,
& p ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
status = rpccli_lsa_open_policy ( p , talloc_tos ( ) , True ,
GENERIC_EXECUTE_ACCESS , & handle ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
status = rpccli_lsa_lookup_sids ( p , talloc_tos ( ) , & handle , 1 , sid ,
& domains , & names , & types ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
* type = types [ 0 ] ;
* domain = talloc_move ( mem_ctx , & domains [ 0 ] ) ;
* name = talloc_move ( mem_ctx , & names [ 0 ] ) ;
status = NT_STATUS_OK ;
fail :
TALLOC_FREE ( p ) ;
cli_tdis ( cli ) ;
tcon_fail :
2022-08-24 03:18:16 +03:00
cli_state_restore_tcon_share ( cli , orig_tcon , orig_share ) ;
2015-04-24 18:37:13 +03:00
TALLOC_FREE ( frame ) ;
return status ;
}
/* convert a SID to a string, either numeric or username/group */
void SidToString ( struct cli_state * cli , fstring str , const struct dom_sid * sid ,
bool numeric )
{
char * domain = NULL ;
char * name = NULL ;
enum lsa_SidType type ;
NTSTATUS status ;
sid_to_fstring ( str , sid ) ;
2015-04-24 19:49:23 +03:00
if ( numeric | | cli = = NULL ) {
2015-04-24 18:37:13 +03:00
return ;
}
status = cli_lsa_lookup_sid ( cli , sid , talloc_tos ( ) , & type ,
& domain , & name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return ;
}
if ( * domain ) {
slprintf ( str , sizeof ( fstring ) - 1 , " %s%s%s " ,
domain , lp_winbind_separator ( ) , name ) ;
} else {
fstrcpy ( str , name ) ;
}
}
2015-04-24 19:15:13 +03:00
static NTSTATUS cli_lsa_lookup_name ( struct cli_state * cli ,
const char * name ,
enum lsa_SidType * type ,
struct dom_sid * sid )
{
2017-06-14 02:56:48 +03:00
struct smbXcli_tcon * orig_tcon = NULL ;
2022-08-24 03:25:40 +03:00
char * orig_share = NULL ;
2021-02-11 10:03:38 +03:00
struct rpc_pipe_client * p = NULL ;
2015-04-24 19:15:13 +03:00
struct policy_handle handle ;
NTSTATUS status ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct dom_sid * sids ;
enum lsa_SidType * types ;
2017-06-14 02:56:48 +03:00
if ( cli_state_has_tcon ( cli ) ) {
2022-08-24 03:25:40 +03:00
cli_state_save_tcon_share ( cli , & orig_tcon , & orig_share ) ;
2017-06-14 02:56:48 +03:00
}
2016-12-08 09:13:57 +03:00
status = cli_tree_connect ( cli , " IPC$ " , " ????? " , NULL ) ;
2015-04-24 19:15:13 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto tcon_fail ;
}
status = cli_rpc_pipe_open_noauth ( cli , & ndr_table_lsarpc ,
& p ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
status = rpccli_lsa_open_policy ( p , talloc_tos ( ) , True ,
GENERIC_EXECUTE_ACCESS , & handle ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
status = rpccli_lsa_lookup_names ( p , talloc_tos ( ) , & handle , 1 , & name ,
NULL , 1 , & sids , & types ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
* type = types [ 0 ] ;
* sid = sids [ 0 ] ;
status = NT_STATUS_OK ;
fail :
TALLOC_FREE ( p ) ;
cli_tdis ( cli ) ;
tcon_fail :
2022-08-24 03:25:40 +03:00
cli_state_restore_tcon_share ( cli , orig_tcon , orig_share ) ;
2015-04-24 19:15:13 +03:00
TALLOC_FREE ( frame ) ;
return status ;
}
/* convert a string to a SID, either numeric or username/group */
bool StringToSid ( struct cli_state * cli , struct dom_sid * sid , const char * str )
{
enum lsa_SidType type ;
if ( string_to_sid ( sid , str ) ) {
return true ;
}
2015-04-24 19:49:23 +03:00
if ( cli = = NULL ) {
return false ;
}
2015-04-24 19:15:13 +03:00
return NT_STATUS_IS_OK ( cli_lsa_lookup_name ( cli , str , & type , sid ) ) ;
}
2015-04-24 19:22:14 +03:00
static void print_ace_flags ( FILE * f , uint8_t flags )
{
char * str = talloc_strdup ( NULL , " " ) ;
2023-01-12 13:55:04 +03:00
size_t len ;
2015-04-24 19:22:14 +03:00
if ( flags & SEC_ACE_FLAG_OBJECT_INHERIT ) {
2023-01-12 13:51:50 +03:00
talloc_asprintf_addbuf ( & str , " OI| " ) ;
2015-04-24 19:22:14 +03:00
}
if ( flags & SEC_ACE_FLAG_CONTAINER_INHERIT ) {
2023-01-12 13:51:50 +03:00
talloc_asprintf_addbuf ( & str , " CI| " ) ;
2015-04-24 19:22:14 +03:00
}
if ( flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT ) {
2023-01-12 13:51:50 +03:00
talloc_asprintf_addbuf ( & str , " NP| " ) ;
2015-04-24 19:22:14 +03:00
}
if ( flags & SEC_ACE_FLAG_INHERIT_ONLY ) {
2023-01-12 13:51:50 +03:00
talloc_asprintf_addbuf ( & str , " IO| " ) ;
2015-04-24 19:22:14 +03:00
}
if ( flags & SEC_ACE_FLAG_INHERITED_ACE ) {
2023-01-12 13:51:50 +03:00
talloc_asprintf_addbuf ( & str , " I| " ) ;
2015-04-24 19:22:14 +03:00
}
2023-01-12 13:51:50 +03:00
if ( str = = NULL ) {
goto out ;
}
2015-04-24 19:22:14 +03:00
/* Ignore define SEC_ACE_FLAG_SUCCESSFUL_ACCESS ( 0x40 )
and SEC_ACE_FLAG_FAILED_ACCESS ( 0x80 ) as they ' re
audit ace flags . */
2023-01-12 13:55:04 +03:00
len = strlen ( str ) ;
if ( len > 0 ) {
fprintf ( f , " /%.*s/ " , ( int ) len - 1 , str ) ;
2015-04-24 19:22:14 +03:00
} else {
fprintf ( f , " /0x%x/ " , flags ) ;
}
TALLOC_FREE ( str ) ;
return ;
out :
fprintf ( f , " /0x%x/ " , flags ) ;
}
/* print an ACE on a FILE, using either numeric or ascii representation */
void print_ace ( struct cli_state * cli , FILE * f , struct security_ace * ace ,
bool numeric )
{
const struct perm_value * v ;
fstring sidstr ;
int do_print = 0 ;
2015-05-10 02:33:10 +03:00
uint32_t got_mask ;
2015-04-24 19:22:14 +03:00
SidToString ( cli , sidstr , & ace - > trustee , numeric ) ;
fprintf ( f , " %s: " , sidstr ) ;
if ( numeric ) {
fprintf ( f , " %d/0x%x/0x%08x " ,
ace - > type , ace - > flags , ace - > access_mask ) ;
return ;
}
/* Ace type */
if ( ace - > type = = SEC_ACE_TYPE_ACCESS_ALLOWED ) {
fprintf ( f , " ALLOWED " ) ;
} else if ( ace - > type = = SEC_ACE_TYPE_ACCESS_DENIED ) {
fprintf ( f , " DENIED " ) ;
} else {
fprintf ( f , " %d " , ace - > type ) ;
}
print_ace_flags ( f , ace - > flags ) ;
/* Standard permissions */
for ( v = standard_values ; v - > perm ; v + + ) {
if ( ace - > access_mask = = v - > mask ) {
fprintf ( f , " %s " , v - > perm ) ;
return ;
}
}
/* Special permissions. Print out a hex value if we have
leftover bits in the mask . */
got_mask = ace - > access_mask ;
again :
for ( v = special_values ; v - > perm ; v + + ) {
if ( ( ace - > access_mask & v - > mask ) = = v - > mask ) {
if ( do_print ) {
fprintf ( f , " %s " , v - > perm ) ;
}
got_mask & = ~ v - > mask ;
}
}
if ( ! do_print ) {
if ( got_mask ! = 0 ) {
fprintf ( f , " 0x%08x " , ace - > access_mask ) ;
} else {
do_print = 1 ;
goto again ;
}
}
}
static bool parse_ace_flags ( const char * str , unsigned int * pflags )
{
const char * p = str ;
* pflags = 0 ;
while ( * p ) {
if ( strnequal ( p , " OI " , 2 ) ) {
* pflags | = SEC_ACE_FLAG_OBJECT_INHERIT ;
p + = 2 ;
} else if ( strnequal ( p , " CI " , 2 ) ) {
* pflags | = SEC_ACE_FLAG_CONTAINER_INHERIT ;
p + = 2 ;
} else if ( strnequal ( p , " NP " , 2 ) ) {
* pflags | = SEC_ACE_FLAG_NO_PROPAGATE_INHERIT ;
p + = 2 ;
} else if ( strnequal ( p , " IO " , 2 ) ) {
* pflags | = SEC_ACE_FLAG_INHERIT_ONLY ;
p + = 2 ;
} else if ( * p = = ' I ' ) {
* pflags | = SEC_ACE_FLAG_INHERITED_ACE ;
p + = 1 ;
} else if ( * p ) {
return false ;
}
switch ( * p ) {
case ' | ' :
p + + ;
2017-07-26 18:54:48 +03:00
FALL_THROUGH ;
2015-04-24 19:22:14 +03:00
case ' \0 ' :
continue ;
default :
return false ;
}
}
return true ;
}
/* parse an ACE in the same format as print_ace() */
bool parse_ace ( struct cli_state * cli , struct security_ace * ace ,
const char * orig_str )
{
char * p ;
const char * cp ;
char * tok ;
unsigned int atype = 0 ;
unsigned int aflags = 0 ;
unsigned int amask = 0 ;
struct dom_sid sid ;
uint32_t mask ;
const struct perm_value * v ;
char * str = SMB_STRDUP ( orig_str ) ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
if ( ! str ) {
TALLOC_FREE ( frame ) ;
return False ;
}
ZERO_STRUCTP ( ace ) ;
p = strchr_m ( str , ' : ' ) ;
if ( ! p ) {
printf ( " ACE '%s': missing ':'. \n " , orig_str ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
* p = ' \0 ' ;
p + + ;
if ( ! StringToSid ( cli , & sid , str ) ) {
printf ( " ACE '%s': failed to convert '%s' to SID \n " ,
orig_str , str ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
cp = p ;
if ( ! next_token_talloc ( frame , & cp , & tok , " / " ) ) {
printf ( " ACE '%s': failed to find '/' character. \n " ,
orig_str ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
if ( strncmp ( tok , " ALLOWED " , strlen ( " ALLOWED " ) ) = = 0 ) {
atype = SEC_ACE_TYPE_ACCESS_ALLOWED ;
} else if ( strncmp ( tok , " DENIED " , strlen ( " DENIED " ) ) = = 0 ) {
atype = SEC_ACE_TYPE_ACCESS_DENIED ;
2016-02-08 23:56:23 +03:00
} else if ( strnequal ( tok , " 0x " , 2 ) ) {
int result ;
result = sscanf ( tok , " %x " , & atype ) ;
if ( result = = 0 | |
( atype ! = SEC_ACE_TYPE_ACCESS_ALLOWED & &
atype ! = SEC_ACE_TYPE_ACCESS_DENIED ) ) {
printf ( " ACE '%s': bad hex value for type at '%s' \n " ,
orig_str , tok ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return false ;
}
} else if ( tok [ 0 ] > = ' 0 ' & & tok [ 0 ] < = ' 9 ' ) {
int result ;
result = sscanf ( tok , " %u " , & atype ) ;
if ( result = = 0 | |
( atype ! = SEC_ACE_TYPE_ACCESS_ALLOWED & &
atype ! = SEC_ACE_TYPE_ACCESS_DENIED ) ) {
printf ( " ACE '%s': bad integer value for type at '%s' \n " ,
orig_str , tok ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return false ;
}
2015-04-24 19:22:14 +03:00
} else {
printf ( " ACE '%s': missing 'ALLOWED' or 'DENIED' entry at '%s' \n " ,
orig_str , tok ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
if ( ! next_token_talloc ( frame , & cp , & tok , " / " ) ) {
printf ( " ACE '%s': bad flags entry at '%s' \n " ,
orig_str , tok ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
if ( tok [ 0 ] < ' 0 ' | | tok [ 0 ] > ' 9 ' ) {
if ( ! parse_ace_flags ( tok , & aflags ) ) {
printf ( " ACE '%s': bad named flags entry at '%s' \n " ,
orig_str , tok ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
} else if ( strnequal ( tok , " 0x " , 2 ) ) {
if ( ! sscanf ( tok , " %x " , & aflags ) ) {
printf ( " ACE '%s': bad hex flags entry at '%s' \n " ,
orig_str , tok ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
} else {
if ( ! sscanf ( tok , " %u " , & aflags ) ) {
printf ( " ACE '%s': bad integer flags entry at '%s' \n " ,
orig_str , tok ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
}
if ( ! next_token_talloc ( frame , & cp , & tok , " / " ) ) {
printf ( " ACE '%s': missing / at '%s' \n " ,
orig_str , tok ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
if ( strncmp ( tok , " 0x " , 2 ) = = 0 ) {
2015-05-21 13:35:43 +03:00
if ( sscanf ( tok , " %x " , & amask ) ! = 1 ) {
2015-04-24 19:22:14 +03:00
printf ( " ACE '%s': bad hex number at '%s' \n " ,
orig_str , tok ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
goto done ;
}
for ( v = standard_values ; v - > perm ; v + + ) {
if ( strcmp ( tok , v - > perm ) = = 0 ) {
amask = v - > mask ;
goto done ;
}
}
p = tok ;
while ( * p ) {
bool found = False ;
for ( v = special_values ; v - > perm ; v + + ) {
if ( v - > perm [ 0 ] = = * p ) {
amask | = v - > mask ;
found = True ;
}
}
if ( ! found ) {
printf ( " ACE '%s': bad permission value at '%s' \n " ,
orig_str , p ) ;
SAFE_FREE ( str ) ;
TALLOC_FREE ( frame ) ;
return False ;
}
p + + ;
}
if ( * p ) {
TALLOC_FREE ( frame ) ;
SAFE_FREE ( str ) ;
return False ;
}
done :
mask = amask ;
init_sec_ace ( ace , & sid , atype , mask , aflags ) ;
TALLOC_FREE ( frame ) ;
SAFE_FREE ( str ) ;
return True ;
}
2015-04-24 19:28:02 +03:00
static void print_acl_ctrl ( FILE * file , uint16_t ctrl , bool numeric )
{
int i ;
const char * separator = " " ;
fprintf ( file , " CONTROL: " ) ;
if ( numeric ) {
fprintf ( file , " 0x%x \n " , ctrl ) ;
return ;
}
for ( i = ARRAY_SIZE ( sec_desc_ctrl_bits ) - 1 ; i > = 0 ; i - - ) {
if ( ctrl & sec_desc_ctrl_bits [ i ] . mask ) {
fprintf ( file , " %s%s " ,
separator , sec_desc_ctrl_bits [ i ] . str ) ;
separator = " | " ;
}
}
fputc ( ' \n ' , file ) ;
}
/* print a ascii version of a security descriptor on a FILE handle */
void sec_desc_print ( struct cli_state * cli , FILE * f ,
struct security_descriptor * sd , bool numeric )
{
fstring sidstr ;
2015-05-10 02:33:10 +03:00
uint32_t i ;
2015-04-24 19:28:02 +03:00
fprintf ( f , " REVISION:%d \n " , sd - > revision ) ;
print_acl_ctrl ( f , sd - > type , numeric ) ;
/* Print owner and group sid */
if ( sd - > owner_sid ) {
SidToString ( cli , sidstr , sd - > owner_sid , numeric ) ;
} else {
fstrcpy ( sidstr , " " ) ;
}
fprintf ( f , " OWNER:%s \n " , sidstr ) ;
if ( sd - > group_sid ) {
SidToString ( cli , sidstr , sd - > group_sid , numeric ) ;
} else {
fstrcpy ( sidstr , " " ) ;
}
fprintf ( f , " GROUP:%s \n " , sidstr ) ;
/* Print aces */
for ( i = 0 ; sd - > dacl & & i < sd - > dacl - > num_aces ; i + + ) {
struct security_ace * ace = & sd - > dacl - > aces [ i ] ;
fprintf ( f , " ACL: " ) ;
print_ace ( cli , f , ace , numeric ) ;
fprintf ( f , " \n " ) ;
}
}