2023-07-12 04:03:53 +03:00
/*
2023-07-20 12:31:43 +03:00
Fuzz access check using SDDL strings and a known token
2023-07-12 04:03:53 +03:00
Copyright ( C ) Catalyst IT 2023
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 "replace.h"
# include "libcli/security/security.h"
2023-07-12 04:03:53 +03:00
# include "libcli/security/conditional_ace.h"
# include "libcli/security/claims-conversions.h"
2023-07-12 04:03:53 +03:00
# include "lib/util/attr.h"
# include "librpc/gen_ndr/ndr_security.h"
2023-07-12 04:03:53 +03:00
# include "librpc/gen_ndr/ndr_conditional_ace.h"
2023-07-12 04:03:53 +03:00
# include "lib/util/bytearray.h"
# include "fuzzing/fuzzing.h"
static struct security_token token = { 0 } ;
static struct dom_sid dom_sid = { 0 } ;
/*
2023-07-12 04:03:53 +03:00
* For this one we initialise a security token to have a few claims
* and SIDs . The fuzz strings contain SDDL that will be tested against
* this token in se_access_check ( ) or sec_access_check_ds ( ) - -
* supposing they compile .
2023-07-12 04:03:53 +03:00
*/
int LLVMFuzzerInitialize ( int * argc , char * * * argv )
{
size_t i ;
TALLOC_CTX * mem_ctx = talloc_new ( NULL ) ;
struct dom_sid * sid = NULL ;
2023-07-12 04:03:53 +03:00
struct claim_def {
const char * type ;
const char * name ;
const char * claim_sddl ;
} claims [ ] = {
{
" user " ,
" shoe size " ,
" 44 "
} ,
{
" user " ,
" © " ,
" { \" unknown \" , \" \" , \" ←ā \" } "
} ,
{
" device " ,
" © " ,
" { \" unknown \" , \" \" , \" ←ā \" } "
} ,
{
" device " ,
" least favourite groups " ,
" {SID(S-1-1-0),SID(S-1-5-3),SID(S-1-57777-333-33-33-2)} "
} ,
{
" local " ,
" birds " ,
" { \" tern \" } "
} ,
} ;
const char * device_sids [ ] = {
" S-1-1-0 " ,
" S-1-333-66 " ,
" S-1-2-3-4-5-6-7-8-9 " ,
} ;
2023-07-12 04:03:53 +03:00
const char * user_sids [ ] = {
" S-1-333-66 " ,
" S-1-16-8448 " ,
" S-1-9-8-7 " ,
} ;
for ( i = 0 ; i < ARRAY_SIZE ( user_sids ) ; i + + ) {
2023-07-12 04:03:53 +03:00
sid = sddl_decode_sid ( mem_ctx , & user_sids [ i ] , NULL ) ;
2023-07-12 04:03:53 +03:00
if ( sid = = NULL ) {
abort ( ) ;
}
add_sid_to_array ( mem_ctx , sid ,
& token . sids ,
& token . num_sids ) ;
}
2023-07-12 04:03:53 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( device_sids ) ; i + + ) {
sid = sddl_decode_sid ( mem_ctx , & device_sids [ i ] , NULL ) ;
if ( sid = = NULL ) {
abort ( ) ;
}
add_sid_to_array ( mem_ctx , sid ,
& token . device_sids ,
& token . num_device_sids ) ;
}
for ( i = 0 ; i < ARRAY_SIZE ( claims ) ; i + + ) {
struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 * claim = NULL ;
struct claim_def c = claims [ i ] ;
claim = parse_sddl_literal_as_claim ( mem_ctx ,
c . name ,
c . claim_sddl ) ;
if ( claim = = NULL ) {
abort ( ) ;
}
add_claim_to_token ( mem_ctx , & token , claim , c . type ) ;
}
/* we also need a global domain SID */
string_to_sid ( & dom_sid , device_sids [ 2 ] ) ;
2023-07-12 04:03:53 +03:00
return 0 ;
}
2023-08-05 05:30:24 +03:00
int LLVMFuzzerTestOneInput ( const uint8_t * input , size_t len )
2023-07-12 04:03:53 +03:00
{
TALLOC_CTX * mem_ctx = NULL ;
struct security_descriptor * sd = NULL ;
uint32_t access_desired ;
uint32_t access_granted ;
const char * sddl ;
ssize_t i ;
if ( len < 5 ) {
return 0 ;
}
access_desired = PULL_LE_U32 ( input + len - 4 , 0 ) ;
/*
* check there is a ' \0 ' .
*
* Note this allows double - dealing for the last 4 bytes : they are used
* as the access_desired mask ( see just above ) but also * could * be
* part of the sddl string . But this doesn ' t matter , for three
* reasons :
*
* 1. the desired access mask doesn ' t usually matter much .
*
* 2. the final ' \0 ' is rarely the operative one . Usually the
* effective string ends a long time before the end of the input , and
* the tail is just junk that comes along for the ride .
*
* 3. Even if there is a case where the end of the SDDL is part of the
2023-07-20 12:31:43 +03:00
* mask , the evolution strategy is very likely to try a different mask ,
2023-07-12 04:03:53 +03:00
* because it likes to add junk on the end .
*
* But still , you ask , WHY ? So that the seeds from here can be shared
* back and forth with the fuzz_sddl_parse seeds , which have the same
* form of a null - terminated - string - with - trailing - junk . If we started
* the loop at ` len - 5 ` instead of ` len - 1 ` , there might be
* interesting seeds that are valid there that would fail here . That ' s
* all .
*/
for ( i = len - 1 ; i > = 0 ; i - - ) {
2023-08-05 05:18:46 +03:00
if ( input [ i ] = = 0 ) {
2023-07-12 04:03:53 +03:00
break ;
}
}
if ( i < 0 ) {
return 0 ;
}
sddl = ( const char * ) input ;
mem_ctx = talloc_new ( NULL ) ;
sd = sddl_decode ( mem_ctx , sddl , & dom_sid ) ;
if ( sd = = NULL ) {
goto end ;
}
2023-07-17 23:56:40 +03:00
# ifdef FUZZ_SEC_ACCESS_CHECK_DS
/*
* The sec_access_check_ds ( ) function has two arguments not found in
* se_access_check , and also not found in our fuzzing examples .
*
* One is a struct object_tree , which is used for object ACE types .
* The other is a SID , which is used as a default if an ACE lacks a
* SID .
*/
sec_access_check_ds ( sd ,
& token ,
access_desired ,
& access_granted ,
NULL ,
NULL ) ;
# else
2023-07-12 04:03:53 +03:00
se_access_check ( sd , & token , access_desired , & access_granted ) ;
2023-07-17 23:56:40 +03:00
# endif
2023-07-12 04:03:53 +03:00
end :
talloc_free ( mem_ctx ) ;
return 0 ;
}