2023-07-12 17:21:06 +12:00
/*
* Unix SMB implementation .
* Functions for understanding conditional ACEs
*
* 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 "librpc/gen_ndr/ndr_security.h"
# include "librpc/gen_ndr/conditional_ace.h"
# include "libcli/security/security.h"
# include "libcli/security/conditional_ace.h"
2023-09-06 15:50:43 +12:00
# include "libcli/security/claims-conversions.h"
# include "lib/util/tsort.h"
# include "lib/util/bytearray.h"
2023-07-12 17:21:06 +12:00
2023-09-06 15:50:43 +12:00
/* We're only dealing with utf-8 here. Honestly. */
# undef strncasecmp
# define SDDL_FLAG_EXPECTING_UNARY_OP 1
# define SDDL_FLAG_EXPECTING_BINARY_OP 2
# define SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP 4
# define SDDL_FLAG_EXPECTING_LOCAL_ATTR 8
# define SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR 16
# define SDDL_FLAG_EXPECTING_LITERAL 32
# define SDDL_FLAG_EXPECTING_PAREN 64
2023-11-07 11:48:58 +13:00
# define SDDL_FLAG_EXPECTING_PAREN_LITERAL 128
# define SDDL_FLAG_NOT_EXPECTING_END_PAREN 256
2023-09-06 15:50:43 +12:00
2023-11-03 14:57:02 +13:00
# define SDDL_FLAG_DEVICE 512
2023-09-06 15:50:43 +12:00
# define SDDL_FLAG_IS_UNARY_OP (1 << 20)
# define SDDL_FLAG_IS_BINARY_OP (1 << 21)
# define SDDL_FLAGS_EXPR_START (SDDL_FLAG_EXPECTING_UNARY_OP | \
SDDL_FLAG_EXPECTING_LOCAL_ATTR | \
SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
SDDL_FLAG_EXPECTING_PAREN )
# define SDDL_FLAGS_MEMBER_OP (SDDL_FLAG_EXPECTING_LITERAL | \
SDDL_FLAG_EXPECTING_PAREN_LITERAL | \
SDDL_FLAG_IS_UNARY_OP )
# define SDDL_FLAGS_RELATIONAL_OP (SDDL_FLAG_EXPECTING_LITERAL | \
2023-09-22 12:02:56 +12:00
SDDL_FLAG_EXPECTING_PAREN_LITERAL | \
2023-09-06 15:50:43 +12:00
SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
SDDL_FLAG_IS_BINARY_OP )
# define SDDL_FLAGS_CONTAINS_OP (SDDL_FLAG_EXPECTING_LITERAL | \
SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
SDDL_FLAG_IS_BINARY_OP )
# define SDDL_FLAGS_EXISTS_OP (SDDL_FLAG_EXPECTING_LOCAL_ATTR | \
SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
SDDL_FLAG_IS_UNARY_OP )
# define SDDL_FLAGS_LOGIC_OP (SDDL_FLAG_EXPECTING_LOCAL_ATTR | \
SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
SDDL_FLAG_EXPECTING_PAREN | \
SDDL_FLAG_EXPECTING_UNARY_OP | \
SDDL_FLAG_IS_BINARY_OP )
# define SDDL_FLAGS_ATTRIBUTE (SDDL_FLAG_EXPECTING_BINARY_OP | \
2023-09-20 14:41:17 +12:00
SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP )
2023-09-06 15:50:43 +12:00
2023-09-20 14:41:46 +12:00
# define SDDL_FLAGS_LITERAL SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP
2023-09-06 15:50:43 +12:00
# define SDDL_FLAGS_PAREN_END (SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP | \
2023-09-20 14:42:26 +12:00
SDDL_FLAG_EXPECTING_BINARY_OP )
2023-09-06 15:50:43 +12:00
enum {
SDDL_NOT_AN_OP = 0 ,
SDDL_PRECEDENCE_EXISTS ,
SDDL_PRECEDENCE_COMMON ,
SDDL_PRECEDENCE_NOT ,
SDDL_PRECEDENCE_AND ,
SDDL_PRECEDENCE_OR ,
SDDL_PRECEDENCE_PAREN_END ,
SDDL_PRECEDENCE_PAREN_START ,
} ;
struct ace_condition_sddl_compiler_context {
TALLOC_CTX * mem_ctx ;
const uint8_t * sddl ;
uint32_t length ;
uint32_t offset ;
uint32_t stack_depth ;
uint32_t max_program_length ;
uint32_t approx_size ;
struct ace_condition_script * program ;
struct ace_condition_token * stack ;
struct ace_condition_token * target ;
uint32_t * target_len ;
const char * message ;
uint32_t message_offset ;
struct dom_sid * domain_sid ;
uint32_t state ;
uint8_t last_token_type ;
2023-11-03 14:57:02 +13:00
bool allow_device ;
2023-09-06 15:50:43 +12:00
} ;
struct sddl_data {
const char * name ;
uint32_t flags ;
uint8_t op_precedence ;
uint8_t nargs ;
} ;
2023-11-07 15:35:28 +13:00
static const struct sddl_data sddl_strings [ 256 ] = {
2023-09-06 15:50:43 +12:00
/* operators */
[ CONDITIONAL_ACE_TOKEN_MEMBER_OF ] = {
" Member_of " ,
SDDL_FLAGS_MEMBER_OP ,
SDDL_PRECEDENCE_COMMON ,
1
} ,
[ CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF ] = {
" Device_Member_of " ,
2023-11-03 14:57:02 +13:00
SDDL_FLAGS_MEMBER_OP | SDDL_FLAG_DEVICE ,
2023-09-06 15:50:43 +12:00
SDDL_PRECEDENCE_COMMON ,
1
} ,
[ CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY ] = {
/* [MS-DTYP] says "_Any", but windows prefers '_any' */
" Member_of_any " ,
SDDL_FLAGS_MEMBER_OP ,
SDDL_PRECEDENCE_COMMON ,
1
} ,
[ CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY ] = {
" Device_Member_of_Any " ,
2023-11-03 14:57:02 +13:00
SDDL_FLAGS_MEMBER_OP | SDDL_FLAG_DEVICE ,
2023-09-06 15:50:43 +12:00
SDDL_PRECEDENCE_COMMON ,
1
} ,
[ CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF ] = {
" Not_Member_of " ,
SDDL_FLAGS_MEMBER_OP ,
SDDL_PRECEDENCE_COMMON ,
1
} ,
[ CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF ] = {
" Not_Device_Member_of " ,
2023-11-03 14:57:02 +13:00
SDDL_FLAGS_MEMBER_OP | SDDL_FLAG_DEVICE ,
2023-09-06 15:50:43 +12:00
SDDL_PRECEDENCE_COMMON ,
1
} ,
[ CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY ] = {
" Not_Member_of_Any " ,
SDDL_FLAGS_MEMBER_OP ,
SDDL_PRECEDENCE_COMMON ,
1
} ,
[ CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY ] = {
" Not_Device_Member_of_Any " ,
2023-11-03 14:57:02 +13:00
SDDL_FLAGS_MEMBER_OP | SDDL_FLAG_DEVICE ,
2023-09-06 15:50:43 +12:00
SDDL_PRECEDENCE_COMMON ,
1
} ,
[ CONDITIONAL_ACE_TOKEN_EQUAL ] = {
" == " ,
SDDL_FLAGS_RELATIONAL_OP ,
SDDL_PRECEDENCE_COMMON ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_NOT_EQUAL ] = {
" != " ,
SDDL_FLAGS_RELATIONAL_OP ,
SDDL_PRECEDENCE_COMMON ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_LESS_THAN ] = {
" < " ,
SDDL_FLAGS_RELATIONAL_OP ,
SDDL_PRECEDENCE_COMMON ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL ] = {
" <= " ,
SDDL_FLAGS_RELATIONAL_OP ,
SDDL_PRECEDENCE_COMMON ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_GREATER_THAN ] = {
" > " ,
SDDL_FLAGS_RELATIONAL_OP ,
SDDL_PRECEDENCE_COMMON ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL ] = {
" >= " ,
SDDL_FLAGS_RELATIONAL_OP ,
SDDL_PRECEDENCE_COMMON ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_CONTAINS ] = {
" Contains " ,
SDDL_FLAGS_CONTAINS_OP ,
SDDL_PRECEDENCE_COMMON ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_ANY_OF ] = {
" Any_of " ,
SDDL_FLAGS_CONTAINS_OP ,
SDDL_PRECEDENCE_COMMON ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_NOT_CONTAINS ] = {
" Not_Contains " ,
SDDL_FLAGS_CONTAINS_OP ,
SDDL_PRECEDENCE_COMMON ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_NOT_ANY_OF ] = {
" Not_Any_of " ,
SDDL_FLAGS_CONTAINS_OP ,
SDDL_PRECEDENCE_COMMON ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_AND ] = {
" && " ,
SDDL_FLAGS_LOGIC_OP ,
SDDL_PRECEDENCE_AND ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_OR ] = {
" || " ,
SDDL_FLAGS_LOGIC_OP ,
SDDL_PRECEDENCE_OR ,
2
} ,
[ CONDITIONAL_ACE_TOKEN_NOT ] = {
" ! " ,
( SDDL_FLAG_EXPECTING_PAREN |
SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR |
SDDL_FLAG_IS_UNARY_OP ) ,
SDDL_PRECEDENCE_NOT ,
1
} ,
[ CONDITIONAL_ACE_TOKEN_EXISTS ] = {
" Exists " ,
SDDL_FLAGS_EXISTS_OP ,
SDDL_PRECEDENCE_EXISTS ,
1
} ,
[ CONDITIONAL_ACE_TOKEN_NOT_EXISTS ] = {
" Not_Exists " ,
SDDL_FLAGS_EXISTS_OP ,
SDDL_PRECEDENCE_EXISTS ,
1
} ,
/* pseudo-operator pseudo-tokens */
[ CONDITIONAL_ACE_SAMBA_SDDL_PAREN ] = {
" ( " ,
2023-09-20 14:42:26 +12:00
0 ,
2023-09-06 15:50:43 +12:00
SDDL_PRECEDENCE_PAREN_START ,
0
} ,
[ CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END ] = {
" ) " ,
SDDL_FLAGS_PAREN_END ,
SDDL_PRECEDENCE_PAREN_END ,
0
} ,
/*
* non - operators .
* The names here are only used for error messages .
*
* some of them will never actually be encountered ( e . g . 8 - bit
* integers ) .
*/
[ CONDITIONAL_ACE_TOKEN_INT8 ] = {
. name = " 8-bit integer " ,
. flags = SDDL_FLAGS_LITERAL ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_TOKEN_INT16 ] = {
" 16-bit integer " ,
SDDL_FLAGS_LITERAL ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_TOKEN_INT32 ] = {
" 32-bit integer " ,
SDDL_FLAGS_LITERAL ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_TOKEN_INT64 ] = {
" 64-bit integer " ,
SDDL_FLAGS_LITERAL ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_TOKEN_UNICODE ] = {
" unicode " ,
SDDL_FLAGS_LITERAL ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_TOKEN_OCTET_STRING ] = {
" byte string " ,
SDDL_FLAGS_LITERAL ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_TOKEN_COMPOSITE ] = {
" composite list " ,
SDDL_FLAGS_LITERAL ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_TOKEN_SID ] = {
" SID " ,
SDDL_FLAGS_LITERAL ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_LOCAL_ATTRIBUTE ] = {
" local attribute " ,
SDDL_FLAGS_ATTRIBUTE ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_USER_ATTRIBUTE ] = {
" user attribute " ,
SDDL_FLAGS_ATTRIBUTE ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_RESOURCE_ATTRIBUTE ] = {
" resource attribute " ,
SDDL_FLAGS_ATTRIBUTE ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_DEVICE_ATTRIBUTE ] = {
" device attribute " ,
2023-11-03 14:57:02 +13:00
SDDL_FLAGS_ATTRIBUTE | SDDL_FLAG_DEVICE ,
2023-09-06 15:50:43 +12:00
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_SAMBA_RESULT_BOOL ] = {
" boolean result " ,
0 ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_SAMBA_RESULT_NULL ] = {
" null result " ,
0 ,
SDDL_NOT_AN_OP ,
0
} ,
[ CONDITIONAL_ACE_SAMBA_RESULT_ERROR ] = {
" error result " ,
0 ,
SDDL_NOT_AN_OP ,
0
} ,
} ;
struct sddl_attr_type {
const char * name ;
uint8_t code ;
} ;
/*
* These are the prefixes for non - local attribute types . [ MS - DTYP ]
* styles them in title case ( " @User. " ) , but Windows itself seems to
* prefer all - caps , so that is how we render them .
*/
2023-11-07 15:35:28 +13:00
static const struct sddl_attr_type sddl_attr_types [ ] = {
2023-09-06 15:50:43 +12:00
{ " USER. " , CONDITIONAL_ACE_USER_ATTRIBUTE } ,
{ " RESOURCE. " , CONDITIONAL_ACE_RESOURCE_ATTRIBUTE } ,
{ " DEVICE. " , CONDITIONAL_ACE_DEVICE_ATTRIBUTE } ,
} ;
struct sddl_write_context {
TALLOC_CTX * mem_ctx ;
char * sddl ;
size_t len ;
size_t alloc_len ;
} ;
static bool sddl_write ( struct sddl_write_context * ctx ,
const char * s )
{
size_t len = strlen ( s ) ;
if ( ctx - > alloc_len - ctx - > len < = len | |
ctx - > sddl = = NULL ) {
size_t old = ctx - > alloc_len ;
ctx - > alloc_len = old + MAX ( old / 2 , len + 50 ) ;
if ( ctx - > alloc_len < = old | |
ctx - > alloc_len - ctx - > len < = len ) {
return false ;
}
ctx - > sddl = talloc_realloc ( ctx - > mem_ctx , ctx - > sddl ,
char , ctx - > alloc_len ) ;
if ( ctx - > sddl = = NULL ) {
return false ;
}
}
memcpy ( ctx - > sddl + ctx - > len , s , len ) ;
ctx - > len + = len ;
ctx - > sddl [ ctx - > len ] = 0 ;
return true ;
}
2023-07-12 17:21:06 +12:00
/*
* This is a helper function to create a representation of a
* conditional ACE . This is not SDDL , more like a disassembly ,
* but it uses some of the same tables .
*/
char * debug_conditional_ace ( TALLOC_CTX * mem_ctx ,
struct ace_condition_script * program )
{
2023-09-06 15:50:43 +12:00
size_t i ;
size_t depth = 0 ;
char stack [ ] = " " ;
char line [ 120 ] ;
struct sddl_write_context ctx = {
. mem_ctx = mem_ctx
} ;
for ( i = 0 ; i < program - > length ; i + + ) {
struct ace_condition_token * tok = & program - > tokens [ i ] ;
struct sddl_data s = sddl_strings [ tok - > type ] ;
char hex [ 21 ] ;
char * utf8 = NULL ;
int utf8_len ;
char type ;
char nom [ 40 ] ;
snprintf ( nom , sizeof ( nom ) , " \033 [1;33m%20s \033 [0m " , s . name ) ;
switch ( tok - > type ) {
case CONDITIONAL_ACE_TOKEN_INT8 :
case CONDITIONAL_ACE_TOKEN_INT16 :
case CONDITIONAL_ACE_TOKEN_INT32 :
case CONDITIONAL_ACE_TOKEN_INT64 :
if ( tok - > data . int64 . sign > 3 | |
tok - > data . int64 . base > 3 ) {
goto error ;
}
snprintf ( line , sizeof ( line ) ,
" %s % " PRIi64 " %c%c \n " ,
nom ,
tok - > data . int64 . value ,
" ?+-_ " [ tok - > data . int64 . sign ] ,
" ?odh " [ tok - > data . int64 . base ]
) ;
type = ' i ' ;
break ;
case CONDITIONAL_ACE_TOKEN_MEMBER_OF :
case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF :
case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY :
case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY :
case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF :
case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF :
case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY :
case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY :
snprintf ( line , sizeof ( line ) ,
" %s bool \n " ,
nom
) ;
type = ' b ' ;
break ;
case CONDITIONAL_ACE_TOKEN_EQUAL :
case CONDITIONAL_ACE_TOKEN_NOT_EQUAL :
case CONDITIONAL_ACE_TOKEN_LESS_THAN :
case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL :
case CONDITIONAL_ACE_TOKEN_GREATER_THAN :
case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL :
case CONDITIONAL_ACE_TOKEN_CONTAINS :
case CONDITIONAL_ACE_TOKEN_ANY_OF :
case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS :
case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF :
case CONDITIONAL_ACE_TOKEN_AND :
case CONDITIONAL_ACE_TOKEN_OR :
snprintf ( line , sizeof ( line ) ,
" %s bool \n " ,
nom
) ;
type = ' b ' ;
break ;
case CONDITIONAL_ACE_TOKEN_EXISTS :
case CONDITIONAL_ACE_TOKEN_NOT_EXISTS :
case CONDITIONAL_ACE_TOKEN_NOT :
snprintf ( line , sizeof ( line ) ,
" %s bool \n " ,
nom
) ;
type = ' b ' ;
break ;
case CONDITIONAL_ACE_LOCAL_ATTRIBUTE :
case CONDITIONAL_ACE_USER_ATTRIBUTE :
case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE :
case CONDITIONAL_ACE_DEVICE_ATTRIBUTE :
snprintf ( line , sizeof ( line ) ,
2023-09-21 12:26:15 +12:00
" %s.%s (any type) \n " ,
nom ,
2023-09-06 15:50:43 +12:00
tok - > data . unicode . value
) ;
type = ' ? ' ;
break ;
case CONDITIONAL_ACE_TOKEN_UNICODE :
snprintf ( line , sizeof ( line ) ,
2023-09-21 12:26:15 +12:00
" %s.%s (any type) \n " ,
nom ,
2023-09-06 15:50:43 +12:00
tok - > data . unicode . value
) ;
type = ' u ' ;
break ;
case CONDITIONAL_ACE_TOKEN_OCTET_STRING :
utf8_len = MIN ( tok - > data . bytes . length , 9 ) ;
2023-09-21 12:55:53 +12:00
hex_encode_buf ( hex , tok - > data . bytes . data , utf8_len ) ;
2023-09-06 15:50:43 +12:00
snprintf ( line , sizeof ( line ) ,
" %s %.*s (%d) \n " ,
nom , utf8_len * 2 , hex , utf8_len ) ;
type = ' o ' ;
break ;
case CONDITIONAL_ACE_TOKEN_SID :
utf8 = sddl_encode_sid ( mem_ctx ,
2023-09-20 09:31:31 +12:00
& tok - > data . sid . sid ,
2023-09-06 15:50:43 +12:00
NULL ) ;
snprintf ( line , sizeof ( line ) ,
" %s (%s) \n " ,
nom , utf8 ) ;
type = ' S ' ;
break ;
case CONDITIONAL_ACE_TOKEN_COMPOSITE :
snprintf ( line , sizeof ( line ) ,
" %s % " PRIu32 " direct members \n " ,
nom , tok - > data . composite . n_members ) ;
type = ' C ' ;
break ;
case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING :
snprintf ( line , sizeof ( line ) ,
" %s \n " , nom ) ;
type = ' 0 ' ;
break ;
default :
snprintf ( line , sizeof ( line ) ,
" unknown opcode %#02x \n " , tok - > type ) ;
type = ' ! ' ;
break ;
}
if ( s . nargs > depth ) {
snprintf ( nom , sizeof ( nom ) ,
" UNDER: -%zu " , s . nargs - depth ) ;
depth = 0 ;
sddl_write ( & ctx , nom ) ;
} else if ( depth > = strlen ( stack ) ) {
snprintf ( nom , sizeof ( nom ) ,
" depth %zu " , s . nargs - depth ) ;
depth - = ( s . nargs - 1 ) ;
sddl_write ( & ctx , nom ) ;
} else {
depth - = s . nargs ;
stack [ depth ] = type ;
depth + + ;
if ( depth < strlen ( stack ) ) {
stack [ depth ] = ' ' ;
}
sddl_write ( & ctx , stack ) ;
}
sddl_write ( & ctx , line ) ;
}
if ( depth = = 1 & & stack [ 0 ] = = ' b ' ) {
snprintf ( line , sizeof ( line ) ,
" \033 [1;32mGOOD: finishes on a single bool \033 [0m \n " ) ;
} else {
snprintf ( line , sizeof ( line ) ,
" \033 [1;31mBAD: should finish with a bool \033 [0m \n " ) ;
}
sddl_write ( & ctx , line ) ;
return ctx . sddl ;
error :
TALLOC_FREE ( ctx . sddl ) ;
2023-07-12 17:21:06 +12:00
return NULL ;
}
2023-09-06 15:50:43 +12:00
struct sddl_node {
struct ace_condition_token * tok ;
struct sddl_node * lhs ;
struct sddl_node * rhs ;
bool wants_parens ;
} ;
static bool sddl_write_int ( struct sddl_write_context * ctx ,
2023-11-10 16:18:32 +13:00
const struct ace_condition_token * tok )
2023-09-06 15:50:43 +12:00
{
int64_t v = tok - > data . int64 . value ;
uint8_t sign = tok - > data . int64 . sign ;
uint8_t base = tok - > data . int64 . base ;
char buf [ 26 ] ; /* oct(1<<63) + sign + \0 */
2023-12-20 13:38:53 +13:00
char sign_char ;
2023-09-06 15:50:43 +12:00
if ( sign > CONDITIONAL_ACE_INT_SIGN_NONE | |
base > CONDITIONAL_ACE_INT_BASE_16 ) {
return false ;
}
/*
* we have 9 combinations of base / sign ( + some invalid combinations of
* actual sign vs claimed sign ) .
*/
if ( sign = = CONDITIONAL_ACE_INT_SIGN_NONE ) {
/* octal and hex will end up unsigned! */
if ( base = = CONDITIONAL_ACE_INT_BASE_8 ) {
2023-12-13 17:20:38 +13:00
snprintf ( buf , sizeof ( buf ) , " 0% " PRIo64 , v ) ;
2023-09-06 15:50:43 +12:00
} else if ( base = = CONDITIONAL_ACE_INT_BASE_10 ) {
snprintf ( buf , sizeof ( buf ) , " % " PRId64 , v ) ;
} else {
2023-12-13 17:24:50 +13:00
snprintf ( buf , sizeof ( buf ) , " 0x% " PRIx64 , v ) ;
2023-09-06 15:50:43 +12:00
}
return sddl_write ( ctx , buf ) ;
}
if ( sign = = CONDITIONAL_ACE_INT_SIGN_POSITIVE & & v < 0 ) {
return false ;
}
if ( sign = = CONDITIONAL_ACE_INT_SIGN_NEGATIVE & & v > 0 ) {
/* note we allow "-0", because we will parse it. */
return false ;
}
2023-12-20 13:38:53 +13:00
sign_char = ( sign = = CONDITIONAL_ACE_INT_SIGN_NEGATIVE ) ? ' - ' : ' + ' ;
2023-09-06 15:50:43 +12:00
/*
2023-12-20 13:40:15 +13:00
* We can use " %+ld " for the decimal sign ( except - 0 ) , but
* " %+lx " and " %+lo " are invalid because % o and % x are
* unsigned .
2023-09-06 15:50:43 +12:00
*/
if ( base = = CONDITIONAL_ACE_INT_BASE_10 ) {
2023-12-20 13:40:15 +13:00
if ( v = = 0 ) {
snprintf ( buf , sizeof ( buf ) , " %c0 " , sign_char ) ;
} else {
snprintf ( buf , sizeof ( buf ) , " %+ " PRId64 , v ) ;
}
2023-09-06 15:50:43 +12:00
return sddl_write ( ctx , buf ) ;
}
if ( v = = INT64_MIN ) {
/*
* llabs ( INT64_MIN ) will be undefined .
* The lengths we must go to to round trip !
*/
if ( base = = CONDITIONAL_ACE_INT_BASE_8 ) {
return sddl_write ( ctx , " -01000000000000000000000 " ) ;
}
return sddl_write ( ctx , " -0x8000000000000000 " ) ;
}
if ( base = = CONDITIONAL_ACE_INT_BASE_8 ) {
2023-12-20 13:38:53 +13:00
snprintf ( buf , sizeof ( buf ) , " %c0%llo " , sign_char , llabs ( v ) ) ;
2023-09-06 15:50:43 +12:00
} else {
2023-12-20 13:38:53 +13:00
snprintf ( buf , sizeof ( buf ) , " %c0x%llx " , sign_char , llabs ( v ) ) ;
2023-09-06 15:50:43 +12:00
}
return sddl_write ( ctx , buf ) ;
}
static bool sddl_should_escape_utf16 ( uint16_t c )
{
if ( c < = ' ' | | c > 126 ) {
return true ;
}
switch ( c ) {
case ' ! ' :
case ' " ' :
case ' & ' :
case ' ( ' :
case ' ) ' :
case ' < ' :
case ' = ' :
case ' > ' :
case ' | ' :
case ' % ' :
return true ;
}
return false ;
}
static bool sddl_encode_attr_name ( TALLOC_CTX * mem_ctx ,
const char * src ,
char * * dest ,
size_t * dest_len )
{
size_t i , j ;
bool ok ;
uint16_t * utf16 = NULL ;
char * escaped = NULL ;
size_t utf16_byte_len ;
size_t utf16_len ;
2023-09-21 12:26:15 +12:00
size_t src_len = strlen ( src ) ;
2023-09-06 15:50:43 +12:00
size_t escapees ;
size_t required ;
* dest = NULL ;
/*
* Writing the string escapes can only really happen in
* utf - 16.
*/
ok = convert_string_talloc ( mem_ctx ,
CH_UTF8 , CH_UTF16LE ,
src , src_len ,
& utf16 , & utf16_byte_len ) ;
if ( ! ok ) {
return false ;
}
utf16_len = utf16_byte_len / 2 ;
escapees = 0 ;
for ( i = 0 ; i < utf16_len ; i + + ) {
uint16_t c = utf16 [ i ] ;
if ( sddl_should_escape_utf16 ( c ) ) {
escapees + + ;
}
if ( c = = 0 ) {
/* we can't have '\0' (or "%0000") in a name. */
TALLOC_FREE ( utf16 ) ;
return false ;
}
}
required = src_len + escapees * 5 ;
escaped = talloc_size ( mem_ctx , required + 1 ) ;
if ( escaped = = NULL ) {
TALLOC_FREE ( utf16 ) ;
return false ;
}
if ( escapees = = 0 ) {
/* there is nothing to escape: the original string is fine */
memcpy ( escaped , src , src_len ) ;
escaped [ src_len ] = ' \0 ' ;
* dest = escaped ;
* dest_len = src_len ;
TALLOC_FREE ( utf16 ) ;
return true ;
}
for ( i = 0 , j = 0 ; i < utf16_len & & j < required ; i + + ) {
uint16_t c = utf16 [ i ] ;
if ( sddl_should_escape_utf16 ( c ) ) {
if ( j + 5 > = required ) {
TALLOC_FREE ( escaped ) ;
TALLOC_FREE ( utf16 ) ;
return false ;
}
snprintf ( escaped + j , 6 , " %%%04x " , c ) ;
j + = 5 ;
} else {
escaped [ j ] = c ;
j + + ;
}
}
escaped [ j ] = ' \0 ' ;
* dest = escaped ;
* dest_len = j ;
TALLOC_FREE ( utf16 ) ;
return true ;
}
static bool sddl_write_attr ( struct sddl_write_context * ctx ,
struct ace_condition_token * tok )
{
char * name = NULL ;
size_t name_len ;
size_t i ;
bool ok = sddl_encode_attr_name ( ctx - > mem_ctx ,
tok - > data . local_attr . value ,
& name , & name_len ) ;
if ( ! ok ) {
return false ;
}
for ( i = 0 ; i < ARRAY_SIZE ( sddl_attr_types ) ; i + + ) {
struct sddl_attr_type x = sddl_attr_types [ i ] ;
if ( x . code = = tok - > type ) {
ok = sddl_write ( ctx , " @ " ) ;
if ( ! ok ) {
return false ;
}
ok = sddl_write ( ctx , x . name ) ;
if ( ! ok ) {
return false ;
}
break ;
}
}
ok = sddl_write ( ctx , name ) ;
talloc_free ( name ) ;
return ok ;
}
static bool sddl_write_unicode ( struct sddl_write_context * ctx ,
2023-11-10 16:18:32 +13:00
const struct ace_condition_token * tok )
2023-09-06 15:50:43 +12:00
{
char * quoted = NULL ;
bool ok ;
/*
2023-09-21 12:26:15 +12:00
* We rely on tok - > data . unicode . value being
* nul - terminated .
2023-09-06 15:50:43 +12:00
*/
2023-09-22 14:16:35 +12:00
if ( strchr ( tok - > data . unicode . value , ' " ' ) ! = NULL ) {
/*
* There is a double quote in this string , but SDDL
* has no mechanism for escaping these ( or anything
* else ) in unicode strings .
*
* The only thing to do is fail .
*
2023-11-30 08:18:49 +13:00
* This cannot happen with an ACE created from SDDL ,
2023-09-22 14:16:35 +12:00
* because the same no - escapes rule applies on the way
* in .
*/
return false ;
}
2023-09-21 12:26:15 +12:00
quoted = talloc_asprintf ( ctx - > mem_ctx , " \" %s \" " ,
tok - > data . unicode . value ) ;
2023-09-06 15:50:43 +12:00
if ( quoted = = NULL ) {
return false ;
}
ok = sddl_write ( ctx , quoted ) ;
TALLOC_FREE ( quoted ) ;
return ok ;
}
static bool sddl_write_octet_string ( struct sddl_write_context * ctx ,
2023-11-10 16:18:32 +13:00
const struct ace_condition_token * tok )
2023-09-06 15:50:43 +12:00
{
bool ok ;
char * hex = hex_encode_talloc ( ctx - > mem_ctx ,
2023-09-21 12:55:53 +12:00
tok - > data . bytes . data ,
2023-09-06 15:50:43 +12:00
tok - > data . bytes . length ) ;
ok = sddl_write ( ctx , " # " ) ;
if ( ! ok ) {
return false ;
}
ok = sddl_write ( ctx , hex ) ;
talloc_free ( hex ) ;
return ok ;
}
2023-11-24 16:24:00 +13:00
/*
* For octet strings , the Resource attribute ACE SDDL differs from conditional
* ACE SDDL , lacking the leading ' # ' .
*/
static bool sddl_write_ra_octet_string ( struct sddl_write_context * ctx ,
const struct ace_condition_token * tok )
{
bool ok ;
char * hex = hex_encode_talloc ( ctx - > mem_ctx ,
tok - > data . bytes . data ,
tok - > data . bytes . length ) ;
ok = sddl_write ( ctx , hex ) ;
talloc_free ( hex ) ;
return ok ;
}
2023-09-06 15:50:43 +12:00
static bool sddl_write_sid ( struct sddl_write_context * ctx ,
2023-11-10 16:18:32 +13:00
const struct ace_condition_token * tok )
2023-09-06 15:50:43 +12:00
{
bool ok ;
char * sddl = NULL ;
char * sid = sddl_encode_sid ( ctx - > mem_ctx ,
2023-09-20 09:31:31 +12:00
& tok - > data . sid . sid ,
2023-09-06 15:50:43 +12:00
NULL ) ;
if ( sid = = NULL ) {
return false ;
}
sddl = talloc_asprintf ( ctx - > mem_ctx , " SID(%s) " , sid ) ;
if ( sddl = = NULL ) {
talloc_free ( sid ) ;
return false ;
}
ok = sddl_write ( ctx , sddl ) ;
talloc_free ( sid ) ;
talloc_free ( sddl ) ;
return ok ;
}
static bool sddl_write_composite ( struct sddl_write_context * ctx ,
struct ace_condition_token * tok )
{
/*
* Looks like { 1 , 2 , 3 , " four " , { " woah, nesting " , { 6 } } , SID ( BA ) } .
*/
struct ace_condition_composite * c = & tok - > data . composite ;
uint32_t i ;
bool ok ;
ok = sddl_write ( ctx , " { " ) ;
if ( ! ok ) {
return false ;
}
for ( i = 0 ; i < c - > n_members ; i + + ) {
struct ace_condition_token * t = & c - > tokens [ i ] ;
if ( i > 0 ) {
ok = sddl_write ( ctx , " , " ) ;
if ( ! ok ) {
return false ;
}
}
switch ( t - > type ) {
case CONDITIONAL_ACE_TOKEN_INT8 :
case CONDITIONAL_ACE_TOKEN_INT16 :
case CONDITIONAL_ACE_TOKEN_INT32 :
case CONDITIONAL_ACE_TOKEN_INT64 :
ok = sddl_write_int ( ctx , t ) ;
break ;
case CONDITIONAL_ACE_TOKEN_UNICODE :
ok = sddl_write_unicode ( ctx , t ) ;
break ;
case CONDITIONAL_ACE_TOKEN_OCTET_STRING :
ok = sddl_write_octet_string ( ctx , t ) ;
break ;
case CONDITIONAL_ACE_TOKEN_SID :
ok = sddl_write_sid ( ctx , t ) ;
break ;
case CONDITIONAL_ACE_TOKEN_COMPOSITE :
2023-09-22 16:30:41 +12:00
return false ;
2023-09-06 15:50:43 +12:00
default :
return false ;
}
if ( ! ok ) {
return false ;
}
}
ok = sddl_write ( ctx , " } " ) ;
return ok ;
}
static bool sddl_write_node ( struct sddl_write_context * ctx ,
struct sddl_node * node )
{
struct ace_condition_token * tok = node - > tok ;
switch ( tok - > type ) {
case CONDITIONAL_ACE_TOKEN_INT8 :
case CONDITIONAL_ACE_TOKEN_INT16 :
case CONDITIONAL_ACE_TOKEN_INT32 :
case CONDITIONAL_ACE_TOKEN_INT64 :
return sddl_write_int ( ctx , tok ) ;
case CONDITIONAL_ACE_TOKEN_MEMBER_OF :
case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF :
case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY :
case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY :
case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF :
case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF :
case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY :
case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY :
case CONDITIONAL_ACE_TOKEN_EQUAL :
case CONDITIONAL_ACE_TOKEN_NOT_EQUAL :
case CONDITIONAL_ACE_TOKEN_LESS_THAN :
case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL :
case CONDITIONAL_ACE_TOKEN_GREATER_THAN :
case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL :
case CONDITIONAL_ACE_TOKEN_CONTAINS :
case CONDITIONAL_ACE_TOKEN_ANY_OF :
case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS :
case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF :
case CONDITIONAL_ACE_TOKEN_AND :
case CONDITIONAL_ACE_TOKEN_OR :
case CONDITIONAL_ACE_TOKEN_EXISTS :
case CONDITIONAL_ACE_TOKEN_NOT_EXISTS :
case CONDITIONAL_ACE_TOKEN_NOT :
return sddl_write ( ctx , sddl_strings [ tok - > type ] . name ) ;
case CONDITIONAL_ACE_LOCAL_ATTRIBUTE :
case CONDITIONAL_ACE_USER_ATTRIBUTE :
case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE :
case CONDITIONAL_ACE_DEVICE_ATTRIBUTE :
return sddl_write_attr ( ctx , tok ) ;
case CONDITIONAL_ACE_TOKEN_UNICODE :
return sddl_write_unicode ( ctx , tok ) ;
case CONDITIONAL_ACE_TOKEN_OCTET_STRING :
return sddl_write_octet_string ( ctx , tok ) ;
case CONDITIONAL_ACE_TOKEN_SID :
return sddl_write_sid ( ctx , tok ) ;
case CONDITIONAL_ACE_TOKEN_COMPOSITE :
return sddl_write_composite ( ctx , tok ) ;
case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING :
/*
* This is only expected at the very end , which we
* can ' t ( and don ' t need to ) check here , but we can at
* least ensure it ' s the end of a sub - expression .
*/
return ( node - > rhs = = NULL ) ;
default :
return false ;
}
/* not expecting to get here */
return false ;
}
static inline bool sddl_wants_outer_parens ( struct sddl_node * node )
{
/*
* Binary ops ( having a LHS ) are always parenthesised " (a == 2) "
*
* Member - of ops are too , for some reason .
*/
return ( node - > lhs ! = NULL | |
node - > tok - > type = = CONDITIONAL_ACE_TOKEN_MEMBER_OF | |
node - > tok - > type = = CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF | |
node - > tok - > type = = CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY | |
node - > tok - > type = = CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY | |
node - > tok - > type = = CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF | |
node - > tok - > type = = CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF | |
node - > tok - > type = = CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY | |
node - > tok - > type = = CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY ) ;
}
static inline bool sddl_wants_inner_parens ( struct sddl_node * node ,
struct sddl_node * child )
{
/*
* logical operators are serialised with parentheses around their
* arguments ( for NOT it is obligatory ) .
*/
if ( node - > tok - > type ! = CONDITIONAL_ACE_TOKEN_NOT & &
node - > tok - > type ! = CONDITIONAL_ACE_TOKEN_AND & &
node - > tok - > type ! = CONDITIONAL_ACE_TOKEN_OR ) {
return false ;
}
if ( sddl_wants_outer_parens ( child ) ) {
return false ;
}
return true ;
}
static void sddl_tree_resolve_parens ( struct sddl_node * node )
{
if ( sddl_wants_outer_parens ( node ) ) {
node - > wants_parens = true ;
}
if ( node - > lhs ! = NULL ) {
bool p = sddl_wants_inner_parens ( node , node - > lhs ) ;
node - > lhs - > wants_parens = p ;
sddl_tree_resolve_parens ( node - > lhs ) ;
}
if ( node - > rhs ! = NULL ) {
bool p = sddl_wants_inner_parens ( node , node - > rhs ) ;
node - > rhs - > wants_parens = p ;
sddl_tree_resolve_parens ( node - > rhs ) ;
}
}
static bool sddl_tree_to_sddl ( struct sddl_write_context * ctx ,
struct sddl_node * node )
{
bool ok ;
if ( node - > wants_parens ) {
ok = sddl_write ( ctx , " ( " ) ;
if ( ! ok ) {
return false ;
}
}
if ( node - > lhs ! = NULL ) {
ok = sddl_tree_to_sddl ( ctx , node - > lhs ) ;
if ( ! ok ) {
return false ;
}
ok = sddl_write ( ctx , " " ) ;
if ( ! ok ) {
return false ;
}
}
ok = sddl_write_node ( ctx , node ) ;
if ( ! ok ) {
return false ;
}
if ( node - > rhs ! = NULL ) {
/* NOT is a special case: "!(x)", not "! (x)" */
if ( node - > tok - > type ! = CONDITIONAL_ACE_TOKEN_NOT ) {
ok = sddl_write ( ctx , " " ) ;
if ( ! ok ) {
return false ;
}
}
ok = sddl_tree_to_sddl ( ctx , node - > rhs ) ;
if ( ! ok ) {
return false ;
}
}
if ( node - > wants_parens ) {
ok = sddl_write ( ctx , " ) " ) ;
if ( ! ok ) {
return false ;
}
}
return true ;
}
2023-07-12 17:21:06 +12:00
/*
* Convert conditional ACE conditions into SDDL conditions .
*
* @ param mem_ctx
* @ param program
* @ return a string or NULL on error .
*/
char * sddl_from_conditional_ace ( TALLOC_CTX * mem_ctx ,
struct ace_condition_script * program )
{
2023-09-06 15:50:43 +12:00
size_t i ;
char * sddl = NULL ;
struct sddl_node * nodes = NULL ;
struct sddl_node * * trees = NULL ;
size_t n_trees = 0 ;
struct ace_condition_token * tok = NULL ;
struct sddl_data s ;
bool ok ;
struct sddl_write_context ctx = {
. mem_ctx = mem_ctx
} ;
if ( program - > length = = 0 ) {
/*
* The empty program is a special case .
*/
return talloc_strdup ( mem_ctx , " () " ) ;
}
nodes = talloc_zero_array ( mem_ctx ,
struct sddl_node ,
program - > length ) ;
if ( nodes = = NULL ) {
talloc_free ( sddl ) ;
return NULL ;
}
trees = talloc_array ( mem_ctx ,
struct sddl_node * ,
program - > length ) ;
if ( trees = = NULL ) {
talloc_free ( sddl ) ;
talloc_free ( nodes ) ;
return NULL ;
}
/*
* This loop constructs a tree , which we then traverse to get the
* SDDL . Consider this transformation :
*
* { A , B , = = , C , D , = = , & & } = > " ((A == B) && (C == D)) "
*
* We keep an array of sub - trees , and add to it in sequence . When the
* thing we ' re adding takes arguments , we pop those off the tree list .
* So it would go through this sequence :
*
* len items
* 1 : A
* 2 : A , B
* 1 : = = ( A , B )
* 2 : = = ( A , B ) , C
* 3 : = = ( A , B ) , C , D
* 2 : = = ( A , B ) , = = ( C , D )
* 1 & & ( = = ( A , B ) , = = ( C , D ) )
*
* Without building a tree it would be difficult to know how many
* parentheses to put before A .
*
* ( A = = B = = C ) should become
* { A B = = C = = } which should be the same as
* ( ( A = = B ) = = C )
*/
for ( i = 0 ; i < program - > length ; i + + ) {
tok = & program - > tokens [ i ] ;
s = sddl_strings [ tok - > type ] ;
nodes [ i ] . tok = tok ;
if ( s . nargs > n_trees ) {
goto error ;
}
if ( s . nargs > = 1 ) {
/*
* Read this note if you ' re trying to follow
* [ MS - DTYP ] . MS - DTYP uses ' LHS ' to describe the
* operand of unary operators even though they are
* always displayed on the right of the operator . It
2023-07-31 10:14:48 +12:00
* makes everything much simpler to use rhs
2023-09-06 15:50:43 +12:00
* instead .
*/
n_trees - - ;
nodes [ i ] . rhs = trees [ n_trees ] ;
if ( s . nargs = = 2 ) {
n_trees - - ;
nodes [ i ] . lhs = trees [ n_trees ] ;
}
}
trees [ n_trees ] = & nodes [ i ] ;
n_trees + + ;
}
if ( n_trees ! = 1 ) {
goto error ;
}
/*
* First we walk the tree to work out where to put parentheses ( to
* match the canonical Windows representation ) .
*
* Doing it in the same traverse as the writing would be possible but
* trickier to get right .
*/
sddl_tree_resolve_parens ( trees [ 0 ] ) ;
trees [ 0 ] - > wants_parens = true ;
/*
* Clamber over the tree , writing the string .
*/
ok = sddl_tree_to_sddl ( & ctx , trees [ 0 ] ) ;
if ( ! ok ) {
goto error ;
}
talloc_free ( trees ) ;
talloc_free ( nodes ) ;
return ctx . sddl ;
error :
talloc_free ( sddl ) ;
talloc_free ( trees ) ;
talloc_free ( nodes ) ;
2023-07-12 17:21:06 +12:00
return NULL ;
}
2023-09-06 15:50:43 +12:00
static void comp_error ( struct ace_condition_sddl_compiler_context * comp ,
const char * fmt , . . . ) PRINTF_ATTRIBUTE ( 2 , 3 ) ;
static void comp_error ( struct ace_condition_sddl_compiler_context * comp ,
const char * fmt , . . . )
{
char * msg = NULL ;
va_list ap ;
va_start ( ap , fmt ) ;
msg = talloc_vasprintf ( comp - > mem_ctx , fmt , ap ) ;
va_end ( ap ) ;
if ( msg = = NULL ) {
goto fail ;
}
if ( comp - > message = = NULL ) {
/*
* Previously unset message ; prepend the position .
*
* This is the common case .
*/
2023-11-01 10:46:20 +13:00
comp - > message_offset = comp - > offset ;
comp - > message = msg ;
2023-09-06 15:50:43 +12:00
return ;
}
/*
* There ' s a message already so we ' ll try to append .
* This is unlikely to happen .
*/
comp - > message = talloc_asprintf ( comp - > mem_ctx ,
2023-11-01 10:46:20 +13:00
" %s AND THEN %s " ,
comp - > message ,
2023-09-06 15:50:43 +12:00
msg ) ;
TALLOC_FREE ( msg ) ;
if ( comp - > message = = NULL ) {
goto fail ;
}
2023-11-02 15:11:58 +13:00
DBG_NOTICE ( " %s \n " , comp - > message ) ;
2023-09-06 15:50:43 +12:00
return ;
fail :
2023-10-21 12:56:24 +13:00
comp - > message = talloc_strdup ( comp - > mem_ctx ,
" failed to set error message " ) ;
2023-11-02 15:11:58 +13:00
DBG_WARNING ( " %s \n " , comp - > message ) ;
2023-09-06 15:50:43 +12:00
}
/*
conditional - ace = " ( " conditional - ace - type " ; " [ ace - flag - string ] " ; " ace - rights
" ; " [ object - guid ] " ; " [ inherit - object - guid ] " ; " sid - string " ; " " ( " cond - expr
" ) " " ) "
wspace = 1 * ( % x09 - 0 D / % x20 )
literal - SID = " SID( " sid - string " ) "
term = [ wspace ] ( memberof - op / exists - op / rel - op / contains - op / anyof - op /
attr - name / rel - op2 ) [ wspace ]
cond - expr = term / term [ wspace ] ( " || " / " && " ) [ wspace ] cond - expr / ( [ " ! " ]
[ wspace ] " ( " cond - expr " ) " )
memberof - op = ( " Member_of " / " Not_Member_of " / " Member_of_Any " /
" Not_Member_of_Any " / " Device_Member_of " / " Device_Member_of_Any " /
" Not_Device_Member_of " / " Not_Device_Member_of_Any " ) wspace sid - array
exists - op = ( " Exists " / " Not_Exists " ) wspace attr - name
rel - op = attr - name [ wspace ] ( " < " / " <= " / " > " / " >= " ) [ wspace ] ( attr - name2 /
value ) ; only scalars
rel - op2 = attr - name [ wspace ] ( " == " / " != " ) [ wspace ] ( attr - name2 / value - array )
; scalar or list
contains - op = attr - name wspace ( " Contains " / " Not_Contains " ) wspace ( attr - name2
/ value - array )
anyof - op = attr - name wspace ( " Any_of " / " Not_Any_of " ) wspace ( attr - name2 /
value - array )
attr - name1 = attr - char1 * ( attr - char1 / " @ " )
attr - char1 = 1 * ( ALPHA / DIGIT / " : " / " . " / " / " / " _ " )
attr - name2 = ( " @user. " / " @device. " / " @resource. " ) 1 * attr - char2
; new prefixed name form
attr - char2 = attr - char1 / lit - char
attr - name = attr - name1 / attr - name2
*/
static inline bool is_wspace ( uint8_t c )
{
/* wspace := %x09-0D | %x20 */
return ( c = = ' ' | | c = = ' \x09 ' | | c = = ' \x0A ' | |
c = = ' \x0B ' | | c = = ' \x0C ' | | c = = ' \x0D ' ) ;
}
static inline bool is_attr_char1 ( uint8_t c )
{
/*
* attr - char1 = 1 * ( ALPHA / DIGIT / " : " / " . " / " / " / " _ " )
* ( ALPHA and DIGIT being ASCII only ) .
*
* These are used for local attributes , which we don ' t really
* expect to see in Samba AD .
*
* One example is " WIN://SYSAPPID " , which is used in conditional ACEs
* that seem to relate to software installers ; another is
* " APPID://PATH " , used by Windows Applocker .
*/
return ( ( ( c > = ' a ' ) & & ( c < = ' z ' ) ) | |
( ( c > = ' A ' ) & & ( c < = ' Z ' ) ) | |
( ( c > = ' 0 ' ) & & ( c < = ' 9 ' ) ) | |
c = = ' : ' | | c = = ' . ' | | c = = ' / ' | | c = = ' _ ' ) ;
}
static ssize_t read_attr2_string (
struct ace_condition_sddl_compiler_context * comp ,
struct ace_condition_unicode * dest )
{
/*
* our SDDL is utf - 8 , but we need to convert to utf - 16 and
* parse the escapes , then back to utf - 8 , because that ' s how
* the claims will appear .
*
* attr_char2 is used for attribute names that follow " @Class. "
* specifiers . They can consume 5 characters to specify a single code
* unit , using " %1234 " style escapes . Certain characters must be
* encoded this way , while others must be literal values . Because the
* % 1234 refers to a utf - 16 code unit , we really need to do the work
* in that codespace .
*/
bool ok ;
uint16_t * utf16 = NULL ;
size_t utf16_byte_len ;
size_t utf16_chars ;
size_t utf8_len ;
size_t src_len ;
ssize_t i , j ;
ssize_t max_len = comp - > length - comp - > offset ;
const uint8_t * src = comp - > sddl + comp - > offset ;
for ( i = 0 ; i < max_len ; i + + ) {
uint8_t c = src [ i ] ;
/*
2023-09-20 14:55:34 +12:00
* A double ‐ byte that must be escaped but isn ' t tells us that
2023-09-06 15:50:43 +12:00
* the attribute name has ended .
*
* The exception is ' % ' , which must also be escaped
* ( as " %0025 " ) , but is obviously still expected in
* the escaped string .
*/
if ( strchr ( " !&()><=| \" " , c ) ! = NULL | | is_wspace ( c ) ) {
break ;
}
}
if ( i = = max_len ) {
/* too long, because we need at least one ')' */
comp_error ( comp , " interminable attribute name " ) ;
return - 1 ;
}
if ( i = = 0 ) {
/* too short! like "User.>= 4" */
comp_error ( comp , " empty attribute name " ) ;
return - 1 ;
}
if ( unlikely ( i > CONDITIONAL_ACE_MAX_LENGTH ) ) {
/*
* This is imprecise ; the limit for the whole ACL is 64 k .
* However there could be many escapes in the SDDL name which
2023-09-20 14:55:34 +12:00
* would reduce down to single utf16 code units in the
2023-09-06 15:50:43 +12:00
* compiled string .
*/
comp_error ( comp , " attribute is way too long (%zu) " , i ) ;
return - 1 ;
}
src_len = i ;
ok = convert_string_talloc ( comp - > mem_ctx ,
CH_UTF8 , CH_UTF16LE ,
src , src_len ,
& utf16 , & utf16_byte_len ) ;
if ( ! ok ) {
comp_error ( comp , " could not convert to utf-16 " ) ;
return - 1 ;
}
/*
* utf16_byte_len is in bytes , we want to count uint16s .
*/
utf16_chars = utf16_byte_len / 2 ;
/* now the escapes. */
for ( i = 0 , j = 0 ;
j < utf16_chars & & i < utf16_chars ;
j + + ) {
uint16_t c = utf16 [ i ] ;
if ( c = = ' % ' ) {
uint16_t v = 0 ;
size_t end = i + 5 ;
/*
* we need to read 4 hex characters .
* hex_byte ( ) won ' t help because that is 8 - bit .
*/
if ( end > utf16_chars ) {
comp_error ( comp ,
" insufficient room for %% escape " ) ;
talloc_free ( utf16 ) ;
return - 1 ;
}
for ( i + + ; i < end ; i + + ) {
v < < = 4 ;
c = utf16 [ i ] ;
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
v + = c - ' 0 ' ;
} else if ( c > = ' A ' & & c < = ' F ' ) {
v + = c - ' A ' + 10 ;
} else if ( c > = ' a ' & & c < = ' f ' ) {
v + = c - ' a ' + 10 ;
} else {
comp_error ( comp , " invalid %% escape " ) ;
talloc_free ( utf16 ) ;
return - 1 ;
}
}
/*
* from MS - DTYP 2.5 .1 .1 Syntax ( text , not ABNF ) , some
* characters must be literals , not escaped .
*/
if ( ( v > = ' 0 ' & & v < = ' 9 ' ) | |
( v > = ' A ' & & v < = ' Z ' ) | |
( v > = ' a ' & & v < = ' z ' ) | |
( v < 127 & &
strchr ( " #$'*+-;?@[ \\ ]^_`{}~:/. " , v ) ! = NULL ) ) {
comp_error ( comp , " invalid %% escape: "
" '%%%04x' should be literal '%c' " ,
v , v ) ;
talloc_free ( utf16 ) ;
return - 1 ;
}
utf16 [ j ] = v ;
continue ;
}
/*
* Note the characters " !&()><=|% \" " must be escaped per
* [ MS - DTYP ] , but as we found the bounds of this string using
* those in utf - 8 at the top of this function , we are not
* going to find them in the utf - 16 now .
*
* Also , per [ MS - DTYP ] , un - escaped whitespace is allowed , but
* effectively disallowed by Samba .
*/
utf16 [ j ] = utf16 [ i ] ;
i + + ;
}
ok = convert_string_talloc ( comp - > mem_ctx ,
CH_UTF16LE , CH_UTF8 ,
utf16 , j * 2 ,
& dest - > value , & utf8_len ) ;
TALLOC_FREE ( utf16 ) ;
if ( ! ok ) {
comp_error ( comp , " could not convert to utf-16 " ) ;
return - 1 ;
}
/* returning bytes consumed, not necessarily the length of token */
return src_len ;
}
static bool eat_whitespace ( struct ace_condition_sddl_compiler_context * comp ,
bool trailing )
{
/*
* Advance the offset to the first non - whitespace character .
*
* If trailing is false , there has to be something before the end of
* the string .
*/
while ( comp - > offset < comp - > length ) {
if ( ! is_wspace ( comp - > sddl [ comp - > offset ] ) ) {
break ;
}
comp - > offset + + ;
}
if ( ( ! trailing ) & & comp - > offset = = comp - > length ) {
comp_error ( comp , " input ends unexpectedly " ) ;
return false ;
}
return true ;
}
static bool pop_sddl_token ( struct ace_condition_sddl_compiler_context * comp ,
struct ace_condition_token * token ) ;
static bool write_sddl_token ( struct ace_condition_sddl_compiler_context * comp ,
struct ace_condition_token token ) ;
static bool pop_write_sddl_token (
struct ace_condition_sddl_compiler_context * comp ) ;
static bool flush_stack_tokens ( struct ace_condition_sddl_compiler_context * comp ,
uint8_t type )
{
bool ok ;
uint8_t precedence = sddl_strings [ type ] . op_precedence ;
if ( precedence = = SDDL_PRECEDENCE_PAREN_START ) {
/* paren has a special role */
return true ;
}
/*
* Any operators on the top of the stack that have a " higher "
* precedence ( tighter binding ) to this one get popped off and written
* to the output . " higher " is in quotes because it means lower enum
* value .
*
* This works for binary operators , for example , with " (a == b == c) "
* ( which is equivalent to " ((a == b) == c) " via the left - to - right
* rule ) , we have :
* TOKEN dest PROGRAM STACK
* (
* a p
* = = s a
* b p a = =
* = = s a b = =
* flush stack
* s - > p a b = = = =
* c p a b = =
* ) a b = = c = =
* flush stack
* a b = = c = =
*
* but it is not right for unary operators , as in " (!(!(Exists
* a ) ) ) " . As it turns out though, >= works for the unary
* operators and syntactic rules we have .
*/
while ( comp - > stack_depth > 0 ) {
struct ace_condition_token * op =
& comp - > stack [ comp - > stack_depth - 1 ] ;
if ( sddl_strings [ op - > type ] . op_precedence > precedence ) {
break ;
}
if ( sddl_strings [ op - > type ] . op_precedence = = precedence & &
sddl_strings [ op - > type ] . flags & SDDL_FLAG_IS_UNARY_OP ) {
break ;
}
ok = pop_write_sddl_token ( comp ) ;
if ( ! ok ) {
comp_error ( comp ,
" could not flush '%s' to program " ,
sddl_strings [ op - > type ] . name ) ;
return false ;
}
}
return true ;
}
static bool push_sddl_token ( struct ace_condition_sddl_compiler_context * comp ,
struct ace_condition_token token )
{
if ( comp - > stack_depth > = CONDITIONAL_ACE_MAX_TOKENS - 1 ) {
comp_error ( comp , " excessive recursion " ) ;
return false ;
}
if ( sddl_strings [ token . type ] . op_precedence = = SDDL_NOT_AN_OP ) {
comp_error ( comp ,
" wrong kind of token for the SDDL stack: %s " ,
sddl_strings [ token . type ] . name ) ;
return false ;
}
/*
* Any operators on the top of the stack that have a " greater " or
* equal precedence to this one get popped off and written to the
* output .
*/
flush_stack_tokens ( comp , token . type ) ;
token . data . op . sddl_position = comp - > offset ;
comp - > stack [ comp - > stack_depth ] = token ;
comp - > stack_depth + + ;
if ( token . type ! = CONDITIONAL_ACE_SAMBA_SDDL_PAREN ) {
comp - > last_token_type = token . type ;
}
return true ;
}
static bool pop_sddl_token ( struct ace_condition_sddl_compiler_context * comp ,
struct ace_condition_token * token )
{
if ( comp - > stack_depth = = 0 ) {
comp_error ( comp , " misbalanced expression " ) ;
return false ;
}
comp - > stack_depth - - ;
* token = comp - > stack [ comp - > stack_depth ] ;
return true ;
}
static bool write_sddl_token ( struct ace_condition_sddl_compiler_context * comp ,
struct ace_condition_token token )
{
/*
* This is adding a token to the program . Normally it will be to the
* main program list , but if we are constructing a composite list , then
* will be redirected there ( via comp - > target ) .
*
* We also conservatively track the overall size , so we don ' t waste
* time compiling something that is way too big .
*/
DBG_INFO ( " writing % " PRIu32 " %x %s \n " ,
* comp - > target_len ,
token . type ,
sddl_strings [ token . type ] . name ) ;
comp - > approx_size + + ;
if ( comp - > approx_size > CONDITIONAL_ACE_MAX_TOKENS ) {
comp_error ( comp , " program is too long "
" (over %d tokens) " ,
CONDITIONAL_ACE_MAX_TOKENS ) ;
return false ;
}
if ( token . type ! = CONDITIONAL_ACE_SAMBA_SDDL_PAREN ) {
comp - > last_token_type = token . type ;
}
comp - > target [ * comp - > target_len ] = token ;
( * comp - > target_len ) + + ;
return true ;
}
static bool pop_write_sddl_token (
struct ace_condition_sddl_compiler_context * comp )
{
bool ok ;
struct ace_condition_token token = { } ;
ok = pop_sddl_token ( comp , & token ) ;
if ( ! ok ) {
comp_error ( comp , " could not pop from op stack " ) ;
return false ;
}
if ( comp - > target ! = comp - > program - > tokens ) {
comp_error ( comp , " compiler is seriously confused " ) ;
return false ;
}
ok = write_sddl_token ( comp , token ) ;
if ( ! ok ) {
comp_error ( comp ,
" could not write '%s' to program " ,
sddl_strings [ token . type ] . name ) ;
return false ;
}
DBG_INFO ( " written '%s' \n " , sddl_strings [ token . type ] . name ) ;
return true ;
}
static bool parse_expression ( struct ace_condition_sddl_compiler_context * comp ) ;
static bool parse_composite ( struct ace_condition_sddl_compiler_context * comp ) ;
static bool parse_oppy_op ( struct ace_condition_sddl_compiler_context * comp )
{
/*
* These ones look like operators and are operators .
*/
bool ok ;
struct ace_condition_token token = { } ;
uint8_t c , d ;
uint32_t flag = SDDL_FLAG_EXPECTING_BINARY_OP ;
if ( comp - > offset + 1 > = comp - > length ) {
comp_error ( comp , " syntax error " ) ;
return false ;
}
token . data . sddl_op . start = comp - > offset ;
/*
* These are all one or two characters long , and we always have room
* to peek ahead .
*/
c = comp - > sddl [ comp - > offset ] ;
d = comp - > sddl [ comp - > offset + 1 ] ;
if ( c = = ' ! ' ) {
if ( d = = ' = ' ) {
comp - > offset + + ;
token . type = CONDITIONAL_ACE_TOKEN_NOT_EQUAL ;
} else {
token . type = CONDITIONAL_ACE_TOKEN_NOT ;
flag = SDDL_FLAG_EXPECTING_UNARY_OP ;
}
} else if ( c = = ' = ' & & d = = ' = ' ) {
comp - > offset + + ;
token . type = CONDITIONAL_ACE_TOKEN_EQUAL ;
} else if ( c = = ' > ' ) {
if ( d = = ' = ' ) {
comp - > offset + + ;
token . type = CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL ;
} else {
token . type = CONDITIONAL_ACE_TOKEN_GREATER_THAN ;
}
} else if ( c = = ' < ' ) {
if ( d = = ' = ' ) {
comp - > offset + + ;
token . type = CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL ;
} else {
token . type = CONDITIONAL_ACE_TOKEN_LESS_THAN ;
}
} else if ( c = = ' & ' & & d = = ' & ' ) {
comp - > offset + + ;
token . type = CONDITIONAL_ACE_TOKEN_AND ;
flag = SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP ;
} else if ( c = = ' | ' & & d = = ' | ' ) {
comp - > offset + + ;
token . type = CONDITIONAL_ACE_TOKEN_OR ;
flag = SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP ;
} else {
comp_error ( comp , " unknown operator " ) ;
return false ;
}
if ( ( comp - > state & flag ) = = 0 ) {
comp_error ( comp , " unexpected operator " ) ;
return false ;
}
comp - > offset + + ;
ok = push_sddl_token ( comp , token ) ;
if ( ! ok ) {
return false ;
}
ok = eat_whitespace ( comp , true ) ;
return ok ;
}
static bool parse_unicode ( struct ace_condition_sddl_compiler_context * comp )
{
/*
* This looks like " hello " ( including the double quotes ) .
*
* Fortunately ( for now ) , there is no mechanism for escaping
* double quotes in conditional ace strings , so we can simply
* look for the second quote without worrying about things
* like « \ \ \ " ».
*/
struct ace_condition_token token = { } ;
char * s = NULL ;
const uint8_t * src = NULL ;
char * utf16 = NULL ;
size_t len , max_len ;
bool ok ;
if ( comp - > sddl [ comp - > offset ] ! = ' " ' ) {
comp_error ( comp , " was expecting ' \" ' for Unicode string " ) ;
return false ;
}
comp - > offset + + ;
src = comp - > sddl + comp - > offset ;
max_len = comp - > length - comp - > offset ;
/* strnchr */
for ( len = 0 ; len < max_len ; len + + ) {
if ( src [ len ] = = ' " ' ) {
break ;
}
}
if ( len = = max_len ) {
comp_error ( comp , " unterminated unicode string " ) ;
return false ;
}
/*
* Look , this is wasteful , but it probably doesn ' t matter . We want to
* check that the string we ' re putting into the descriptor is valid ,
* or we ' ll see errors down the track .
*/
ok = convert_string_talloc ( comp - > mem_ctx ,
CH_UTF8 , CH_UTF16LE ,
src , len ,
& utf16 , NULL ) ;
if ( ! ok ) {
comp_error ( comp , " not valid unicode " ) ;
return false ;
}
TALLOC_FREE ( utf16 ) ;
s = talloc_array_size ( comp - > mem_ctx , 1 , len + 1 ) ;
if ( s = = NULL ) {
comp_error ( comp , " allocation error " ) ;
return false ;
}
memcpy ( s , src , len ) ;
s [ len ] = 0 ;
comp - > offset + = len + 1 ; /* +1 for the final quote */
token . type = CONDITIONAL_ACE_TOKEN_UNICODE ;
token . data . unicode . value = s ;
return write_sddl_token ( comp , token ) ;
}
static bool parse_octet_string ( struct ace_condition_sddl_compiler_context * comp )
{
/*
* This looks like ' # hhhh . . . ' , where each ' hh ' is hex for a byte , with
* the weird and annoying complication that ' # ' can be used to mean
* ' 0 ' .
*/
struct ace_condition_token token = { } ;
size_t length , i ;
if ( comp - > sddl [ comp - > offset ] ! = ' # ' ) {
comp_error ( comp , " was expecting '#' for octet string " ) ;
return false ;
}
comp - > offset + + ;
length = strspn ( ( const char * ) ( comp - > sddl + comp - > offset ) ,
" #0123456789abcdefABCDEF " ) ;
if ( length & 1 ) {
comp_error ( comp , " octet string has odd number of hex digits " ) ;
return false ;
}
length / = 2 ;
2023-09-21 12:55:53 +12:00
token . data . bytes = data_blob_talloc_zero ( comp - > mem_ctx , length ) ;
2023-09-06 15:50:43 +12:00
token . type = CONDITIONAL_ACE_TOKEN_OCTET_STRING ;
for ( i = 0 ; i < length ; i + + ) {
/*
* Why not just strhex_to_str ( ) ?
*
* Because we need to treat ' # ' as ' 0 ' in octet string values ,
* so all of the following are the same
* ( equaling { 0x10 , 0x20 , 0x30 , 0x0 } ) .
*
* # 10203000
* # 10203 # # #
* # 1 # 2 # 3 # # #
* # 10203 # 00
*/
bool ok ;
char pair [ 2 ] ;
size_t j = comp - > offset + i * 2 ;
pair [ 0 ] = ( comp - > sddl [ j ] = = ' # ' ) ? ' 0 ' : comp - > sddl [ j ] ;
pair [ 1 ] = ( comp - > sddl [ j + 1 ] = = ' # ' ) ? ' 0 ' : comp - > sddl [ j + 1 ] ;
2023-09-21 12:55:53 +12:00
ok = hex_byte ( pair , & token . data . bytes . data [ i ] ) ;
2023-09-06 15:50:43 +12:00
if ( ! ok ) {
2023-09-21 12:55:53 +12:00
talloc_free ( token . data . bytes . data ) ;
2023-09-06 15:50:43 +12:00
comp_error ( comp , " inexplicable error in octet string " ) ;
return false ;
}
}
comp - > offset + = length * 2 ;
return write_sddl_token ( comp , token ) ;
}
2023-11-02 15:28:15 +13:00
static bool parse_ra_octet_string ( struct ace_condition_sddl_compiler_context * comp )
{
/*
* Resource attribute octet strings resemble conditional ace octet
* strings , but have some important differences :
*
* 1. The ' # ' at the start is optional , and if present is
* counted as a zero .
*
* 2. An odd number of characters is implicitly left - padded with a zero .
*
* That is , " abc " means " 0abc " , " #12 " means " 0012 " , " f## "
* means " 0f00 " , and " ## " means 00.
*/
struct ace_condition_token token = { } ;
size_t string_length , bytes_length , i , j ;
bool ok ;
char pair [ 2 ] ;
string_length = strspn ( ( const char * ) ( comp - > sddl + comp - > offset ) ,
" #0123456789abcdefABCDEF " ) ;
bytes_length = ( string_length + 1 ) / 2 ;
if ( bytes_length = = 0 ) {
comp_error ( comp , " zero length octet bytes " ) ;
return false ;
}
token . data . bytes = data_blob_talloc_zero ( comp - > mem_ctx , bytes_length ) ;
if ( token . data . bytes . data = = NULL ) {
return false ;
}
token . type = CONDITIONAL_ACE_TOKEN_OCTET_STRING ;
j = comp - > offset ;
i = 0 ;
if ( string_length & 1 ) {
/*
* An odd number of characters means the first
* character gains an implicit 0 for the high nybble .
*/
pair [ 0 ] = 0 ;
pair [ 1 ] = ( comp - > sddl [ 0 ] = = ' # ' ) ? ' 0 ' : comp - > sddl [ 0 ] ;
ok = hex_byte ( pair , & token . data . bytes . data [ i ] ) ;
if ( ! ok ) {
goto fail ;
}
j + + ;
i + + ;
}
for ( ; i < bytes_length ; i + + ) {
/*
* Why not just strhex_to_str ( ) ?
*
* Because we need to treat ' # ' as ' 0 ' in octet string values .
*/
if ( comp - > length - j < 2 ) {
goto fail ;
}
pair [ 0 ] = ( comp - > sddl [ j ] = = ' # ' ) ? ' 0 ' : comp - > sddl [ j ] ;
pair [ 1 ] = ( comp - > sddl [ j + 1 ] = = ' # ' ) ? ' 0 ' : comp - > sddl [ j + 1 ] ;
ok = hex_byte ( pair , & token . data . bytes . data [ i ] ) ;
if ( ! ok ) {
goto fail ;
}
j + = 2 ;
}
comp - > offset = j ;
return write_sddl_token ( comp , token ) ;
fail :
comp_error ( comp , " inexplicable error in octet string " ) ;
talloc_free ( token . data . bytes . data ) ;
return false ;
}
2023-09-06 15:50:43 +12:00
static bool parse_sid ( struct ace_condition_sddl_compiler_context * comp )
{
struct dom_sid * sid = NULL ;
const uint8_t * sidstr = NULL ;
struct ace_condition_token token = { } ;
size_t end ;
2023-11-02 15:41:33 +13:00
if ( comp - > length - comp - > offset < 7 ) {
/* minimum: "SID(AA)" */
comp_error ( comp , " no room for a complete SID " ) ;
2023-09-06 15:50:43 +12:00
return false ;
}
2023-11-02 15:41:33 +13:00
/* conditional ACE SID string */
if ( comp - > sddl [ comp - > offset ] ! = ' S ' | |
comp - > sddl [ comp - > offset + 1 ] ! = ' I ' | |
comp - > sddl [ comp - > offset + 2 ] ! = ' D ' | |
comp - > sddl [ comp - > offset + 3 ] ! = ' ( ' ) {
comp_error ( comp , " malformed SID() constructor " ) ;
return false ;
2023-09-06 15:50:43 +12:00
} else {
2023-11-02 15:41:33 +13:00
comp - > offset + = 4 ;
2023-09-06 15:50:43 +12:00
}
sidstr = comp - > sddl + comp - > offset ;
sid = sddl_decode_sid ( comp - > mem_ctx ,
( const char * * ) & sidstr ,
comp - > domain_sid ) ;
if ( sid = = NULL ) {
comp_error ( comp , " could not parse SID " ) ;
return false ;
}
end = sidstr - comp - > sddl ;
if ( end > = comp - > length | | end < comp - > offset ) {
comp_error ( comp , " apparent overflow in SID parsing " ) ;
return false ;
}
comp - > offset = end ;
2023-11-02 15:41:33 +13:00
/*
* offset is now at the end of the SID , but we need to account
* for the ' ) ' .
*/
if ( comp - > sddl [ comp - > offset ] ! = ' ) ' ) {
comp_error ( comp , " expected ')' to follow SID " ) ;
return false ;
2023-09-06 15:50:43 +12:00
}
2023-11-02 15:41:33 +13:00
comp - > offset + + ;
2023-09-06 15:50:43 +12:00
token . type = CONDITIONAL_ACE_TOKEN_SID ;
2023-09-20 09:31:31 +12:00
token . data . sid . sid = * sid ;
2023-09-06 15:50:43 +12:00
return write_sddl_token ( comp , token ) ;
}
2023-11-02 15:37:33 +13:00
static bool parse_ra_sid ( struct ace_condition_sddl_compiler_context * comp )
{
struct dom_sid * sid = NULL ;
const uint8_t * sidstr = NULL ;
struct ace_condition_token token = { } ;
size_t end ;
if ( ( comp - > state & SDDL_FLAG_EXPECTING_LITERAL ) = = 0 ) {
comp_error ( comp , " did not expect a SID here " ) ;
return false ;
}
/*
* Here we are parsing a resource attribute ACE which doesn ' t
* have the SID ( ) wrapper around the SID string ( unlike a
* conditional ACE ) .
*
* The resource ACE doesn ' t need this because there is no
* ambiguity with local attribute names , besides which the
* type has already been specified earlier in the ACE .
*/
if ( comp - > length - comp - > offset < 2 ) {
comp_error ( comp , " no room for a complete SID " ) ;
return false ;
}
sidstr = comp - > sddl + comp - > offset ;
sid = sddl_decode_sid ( comp - > mem_ctx ,
( const char * * ) & sidstr ,
comp - > domain_sid ) ;
if ( sid = = NULL ) {
comp_error ( comp , " could not parse SID " ) ;
return false ;
}
end = sidstr - comp - > sddl ;
if ( end > = comp - > length | | end < comp - > offset ) {
comp_error ( comp , " apparent overflow in SID parsing " ) ;
return false ;
}
comp - > offset = end ;
token . type = CONDITIONAL_ACE_TOKEN_SID ;
token . data . sid . sid = * sid ;
return write_sddl_token ( comp , token ) ;
}
2023-09-06 15:50:43 +12:00
static bool parse_int ( struct ace_condition_sddl_compiler_context * comp )
{
/*
* This one is relatively simple . strtoll ( ) does the work .
*/
long long v ;
struct ace_condition_token token = { } ;
const char * start = ( const char * ) comp - > sddl + comp - > offset ;
char * end = NULL ;
const char * first_digit = start ;
size_t len ;
errno = 0 ;
v = strtoll ( start , & end , 0 ) ;
if ( errno ! = 0 ) {
comp_error ( comp , " bad integer: %s " , strerror ( errno ) ) ;
return false ;
}
len = end - start ;
if ( len = = 0 ) {
comp_error ( comp , " unexpected non-integer " ) ;
return false ;
}
if ( comp - > offset + len > comp - > length ) {
comp_error ( comp , " impossible integer length: %zu! " , len ) ;
return false ;
}
comp - > offset + = len ;
/*
* Record the base and sign , which are used for recreating the SDDL .
*
* ' Sign ' indicates whether there is a ' + ' or ' - ' sign . Base indicates
* whether the number was in hex , octal , or decimal . These make no
* difference to the evaluation of the ACE , just the display .
*
* This would not work reliably if eat_whitespace ( ) is not called
* before parse_int ( ) , but a ) we know it is , and b ) we don ' t * really *
* care if we lose these display hints .
*/
if ( * start = = ' - ' ) {
token . data . int64 . sign = CONDITIONAL_ACE_INT_SIGN_NEGATIVE ;
first_digit + + ;
} else if ( * start = = ' + ' ) {
token . data . int64 . sign = CONDITIONAL_ACE_INT_SIGN_POSITIVE ;
first_digit + + ;
} else {
token . data . int64 . sign = CONDITIONAL_ACE_INT_SIGN_NONE ;
}
if ( * first_digit = = ' 0 ' & & ( end - first_digit ) > 1 ) {
if ( ( end - first_digit > 2 ) & &
( first_digit [ 1 ] = = ' x ' | |
first_digit [ 1 ] = = ' X ' ) ) {
token . data . int64 . base = CONDITIONAL_ACE_INT_BASE_16 ;
} else {
token . data . int64 . base = CONDITIONAL_ACE_INT_BASE_8 ;
}
} else {
token . data . int64 . base = CONDITIONAL_ACE_INT_BASE_10 ;
}
token . data . int64 . value = v ;
token . type = CONDITIONAL_ACE_TOKEN_INT64 ;
return write_sddl_token ( comp , token ) ;
}
2023-11-22 13:23:26 +13:00
static bool parse_uint ( struct ace_condition_sddl_compiler_context * comp )
{
struct ace_condition_token * tok = NULL ;
bool ok = parse_int ( comp ) ;
if ( ok = = false ) {
return false ;
}
/*
* check that the token ' s value is positive .
*/
if ( comp - > target_len = = 0 ) {
return false ;
}
tok = & comp - > target [ * comp - > target_len - 1 ] ;
if ( tok - > type ! = CONDITIONAL_ACE_TOKEN_INT64 ) {
return false ;
}
if ( tok - > data . int64 . value < 0 ) {
comp_error ( comp , " invalid resource ACE value for unsigned TU claim " ) ;
return false ;
}
return true ;
}
2023-11-22 13:24:21 +13:00
static bool parse_bool ( struct ace_condition_sddl_compiler_context * comp )
{
struct ace_condition_token * tok = NULL ;
bool ok = parse_int ( comp ) ;
if ( ok = = false | | comp - > target_len = = 0 ) {
return false ;
}
/*
* check that the token is 0 or 1.
*/
tok = & comp - > target [ * comp - > target_len - 1 ] ;
if ( tok - > type ! = CONDITIONAL_ACE_TOKEN_INT64 ) {
return false ;
}
if ( tok - > data . int64 . value ! = 0 & & tok - > data . int64 . value ! = 1 ) {
comp_error ( comp , " invalid resource ACE Boolean value " ) ;
return false ;
}
return true ;
}
2023-09-06 15:50:43 +12:00
static bool could_be_an_int ( struct ace_condition_sddl_compiler_context * comp )
{
const char * start = ( const char * ) ( comp - > sddl + comp - > offset ) ;
char * end = NULL ;
if ( ( comp - > state & SDDL_FLAG_EXPECTING_LITERAL ) = = 0 ) {
return false ;
}
errno = 0 ;
/*
* See , we don ' t care about the strtoll return value , only
* whether it succeeds or not and what it finds at the end . If
* it succeeds , parse_int ( ) will do it again for the value .
*
* Note that an out of range int will raise ERANGE ( probably
* 34 ) , so it will be read as a local attribute .
*/
strtoll ( start , & end , 0 ) ;
if ( errno ! = 0 | |
end = = start | |
end > = ( const char * ) comp - > sddl + comp - > length ) {
return false ;
}
/*
* We know * some * characters form an int , but if we run right
* into other attr1 characters ( basically , letters ) , we won ' t
* count it as an int .
*
* For example , the " 17 " in " 17p " is not an int . The " 17 " in
* " 17|| " is .
*/
if ( is_attr_char1 ( * end ) ) {
return false ;
}
return true ;
}
static bool parse_word ( struct ace_condition_sddl_compiler_context * comp )
{
/*
* Sometimes a bare word must be a local attribute , while in other
* cases it could also be a member - of or exists operator . Sometimes it
* could actually be a SID , which we discover when we ' ve read as far
* as " SID( " . Sometimes it might be a literal integer ( attribute
* names can also consist entirely of digits ) .
*
* When it is an operator name , we have the complication that a match
* does not necessarily end the token . Consider " Member_of_Any " which
* contains the operator " Member_of " . According to [ MS - DTYP ] , a space
* is not necessary between the operator and the next token , but it
* does seem to be required for Windows 2022.
*
* Also , " Member_of " et . al . * could * be valid local attributes , which
* would make " (Member_of == 123) " a valid expression that we will
* fail to parse . This is not much of an issue for Samba AD where
* local attributes are not used .
*
* Operators are matched case - insensitively .
*
* There ' s another kind of attribute that starts with a ' @ ' , which we
* deal with in parse_attr2 ( ) . Those ones have full unicode glory ;
* these ones are ASCII only .
*/
size_t i , j , k ;
bool ok ;
uint8_t candidates [ 8 ] ;
size_t n_candidates = 0 ;
struct ace_condition_token token = { } ;
bool expecting_unary = comp - > state & SDDL_FLAG_EXPECTING_UNARY_OP ;
bool expecting_binary = comp - > state & SDDL_FLAG_EXPECTING_BINARY_OP ;
bool expecting_attr = comp - > state & SDDL_FLAG_EXPECTING_LOCAL_ATTR ;
bool expecting_literal = comp - > state & SDDL_FLAG_EXPECTING_LITERAL ;
const uint8_t * start = comp - > sddl + comp - > offset ;
uint8_t c = start [ 0 ] ;
char * s = NULL ;
if ( ! is_attr_char1 ( * start ) ) {
/* we shouldn't get here, because we peeked first */
return false ;
}
/*
* We ' ll look for a SID first , because it simplifies the rest .
*/
if ( expecting_literal & &
comp - > offset + 4 < comp - > length & &
start [ 0 ] = = ' S ' & &
start [ 1 ] = = ' I ' & &
start [ 2 ] = = ' D ' & &
start [ 3 ] = = ' ( ' ) {
/* actually, we are parsing a SID. */
return parse_sid ( comp ) ;
}
if ( expecting_binary | | expecting_unary ) {
/*
* Collect up the operators that can possibly be used
* here , including only those that start with the
* current letter and have the right arity / syntax .
*
* We don ' t expect more than 5 ( for ' N ' , beginning the
* " Not_... " unary ops ) , and we ' ll winnow them down as
* we progress through the word .
*/
int uc = toupper ( c ) ;
for ( i = 0 ; i < 256 ; i + + ) {
2023-11-07 15:35:28 +13:00
const struct sddl_data * d = & sddl_strings [ i ] ;
2023-09-06 15:50:43 +12:00
if ( sddl_strings [ i ] . op_precedence ! = SDDL_NOT_AN_OP & &
uc = = toupper ( ( unsigned char ) d - > name [ 0 ] ) ) {
if ( d - > flags & SDDL_FLAG_IS_UNARY_OP ) {
if ( ! expecting_unary ) {
continue ;
}
} else if ( ! expecting_binary ) {
continue ;
}
candidates [ n_candidates ] = i ;
n_candidates + + ;
if ( n_candidates = = ARRAY_SIZE ( candidates ) ) {
/* impossible, really. */
return false ;
}
}
}
} else if ( could_be_an_int ( comp ) ) {
/*
* if looks like an integer , and we expect an integer , it is
* an integer . If we don ' t expect an integer , it is a local
* attribute with a STUPID NAME . Or an error .
*/
return parse_int ( comp ) ;
} else if ( ! expecting_attr ) {
comp_error ( comp , " did not expect this word here " ) ;
return false ;
}
i = 1 ;
while ( comp - > offset + i < comp - > length ) {
c = start [ i ] ;
if ( ! is_attr_char1 ( c ) ) {
break ;
}
if ( n_candidates ! = 0 ) {
/*
* Filter out candidate operators that no longer
* match .
*/
int uc = toupper ( c ) ;
k = 0 ;
for ( j = 0 ; j < n_candidates ; j + + ) {
size_t o = candidates [ j ] ;
uint8_t c2 = sddl_strings [ o ] . name [ i ] ;
if ( uc = = toupper ( c2 ) ) {
candidates [ k ] = candidates [ j ] ;
k + + ;
}
}
n_candidates = k ;
}
i + + ;
}
/*
* We have finished and there is a complete word . If it could be an
* operator we ' ll assume it is one .
*
* A complication is we could have matched more than one operator , for
* example " Member_of " and " Member_of_Any " , so we have to look through
* the list of candidates for the one that ends .
*/
if ( n_candidates ! = 0 ) {
for ( j = 0 ; j < n_candidates ; j + + ) {
size_t o = candidates [ j ] ;
if ( sddl_strings [ o ] . name [ i ] = = ' \0 ' ) {
/* it is this one */
2023-11-03 14:57:02 +13:00
if ( ! comp - > allow_device & &
( sddl_strings [ o ] . flags & SDDL_FLAG_DEVICE ) )
{
comp_error (
comp ,
" a device‐ relative expression "
" will never evaluate to true "
" in this context (did you "
" intend a user‐ relative "
" expression?) " ) ;
return false ;
}
2023-09-06 15:50:43 +12:00
token . type = o ;
token . data . sddl_op . start = comp - > offset ;
comp - > offset + = i ;
ok = push_sddl_token ( comp , token ) ;
return ok ;
}
}
}
/*
* if looks like an integer , and we expect an integer , it is
* an integer . If we don ' t expect an integer , it is a local
* attribute with a STUPID NAME .
*/
if ( could_be_an_int ( comp ) ) {
return parse_int ( comp ) ;
}
if ( ! expecting_attr ) {
comp_error ( comp , " word makes no sense here " ) ;
return false ;
}
/* it's definitely an attribute name */
token . type = CONDITIONAL_ACE_LOCAL_ATTRIBUTE ;
if ( comp - > offset + i > = comp - > length ) {
comp_error ( comp , " missing trailing ')'? " ) ;
return false ;
}
s = talloc_memdup ( comp - > mem_ctx , start , i + 1 ) ;
if ( s = = NULL ) {
comp_error ( comp , " allocation error " ) ;
return false ;
}
s [ i ] = 0 ;
token . data . local_attr . value = s ;
comp - > offset + = i ;
return write_sddl_token ( comp , token ) ;
}
static bool parse_attr2 ( struct ace_condition_sddl_compiler_context * comp )
{
/*
* Attributes in the form @ class . attr
*
* class can be " User " , " Device " , or " Resource " , case insensitive .
*/
size_t i ;
bool ok ;
size_t len ;
struct ace_condition_token token = { } ;
if ( ( comp - > state & SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR ) = = 0 ) {
comp_error ( comp , " did not expect @attr here " ) ;
return false ;
}
if ( comp - > sddl [ comp - > offset ] ! = ' @ ' ) {
comp_error ( comp , " Expected '@' " ) ;
return false ;
}
comp - > offset + + ;
for ( i = 0 ; i < ARRAY_SIZE ( sddl_attr_types ) ; i + + ) {
int ret ;
size_t attr_len = strlen ( sddl_attr_types [ i ] . name ) ;
if ( attr_len > = comp - > length - comp - > offset ) {
continue ;
}
ret = strncasecmp ( sddl_attr_types [ i ] . name ,
( const char * ) ( comp - > sddl + comp - > offset ) ,
attr_len ) ;
if ( ret = = 0 ) {
2023-11-03 14:57:02 +13:00
const uint8_t code = sddl_attr_types [ i ] . code ;
if ( ! comp - > allow_device & &
( sddl_strings [ code ] . flags & SDDL_FLAG_DEVICE ) )
{
comp_error ( comp ,
" a device attribute is not "
" applicable in this context (did "
" you intend a user attribute?) " ) ;
return false ;
}
token . type = code ;
2023-09-06 15:50:43 +12:00
comp - > offset + = attr_len ;
break ;
}
}
if ( i = = ARRAY_SIZE ( sddl_attr_types ) ) {
comp_error ( comp , " unknown attribute class " ) ;
return false ;
}
/*
* Now we are past the class and the ' . ' , and into the
* attribute name . The attribute name can be almost
* anything , but some characters need to be escaped .
*/
len = read_attr2_string ( comp , & token . data . unicode ) ;
if ( len = = - 1 ) {
/* read_attr2_string has set a message */
return false ;
}
ok = write_sddl_token ( comp , token ) ;
if ( ! ok ) {
return false ;
}
comp - > offset + = len ;
ok = eat_whitespace ( comp , false ) ;
return ok ;
}
2023-09-22 16:31:36 +12:00
static bool parse_literal ( struct ace_condition_sddl_compiler_context * comp ,
bool in_composite )
2023-09-06 15:50:43 +12:00
{
uint8_t c = comp - > sddl [ comp - > offset ] ;
if ( ! ( comp - > state & SDDL_FLAG_EXPECTING_LITERAL ) ) {
comp_error ( comp , " did not expect to be parsing a literal now " ) ;
return false ;
}
switch ( c ) {
case ' # ' :
return parse_octet_string ( comp ) ;
case ' " ' :
return parse_unicode ( comp ) ;
case ' S ' :
return parse_sid ( comp ) ;
case ' { ' :
2023-09-22 16:31:36 +12:00
if ( in_composite ) {
/* nested composites are not supported */
return false ;
} else {
return parse_composite ( comp ) ;
}
2023-09-06 15:50:43 +12:00
default :
if ( strchr ( " 1234567890-+ " , c ) ! = NULL ) {
return parse_int ( comp ) ;
}
}
if ( c > 31 & & c < 127 ) {
comp_error ( comp ,
" unexpected byte 0x%02x '%c' parsing literal " , c , c ) ;
} else {
comp_error ( comp , " unexpected byte 0x%02x parsing literal " , c ) ;
}
return false ;
}
static bool parse_composite ( struct ace_condition_sddl_compiler_context * comp )
{
/*
* This jumps into a different parser , expecting a comma separated
* list of literal values , which might include nested literal
* composites .
*
* To handle the nesting , we redirect the pointers that determine
* where write_sddl_token ( ) writes .
*/
bool ok ;
2023-08-01 12:00:33 +12:00
bool first = true ;
2023-09-06 15:50:43 +12:00
struct ace_condition_token token = {
. type = CONDITIONAL_ACE_TOKEN_COMPOSITE
} ;
uint32_t start = comp - > offset ;
size_t alloc_size ;
struct ace_condition_token * old_target = comp - > target ;
uint32_t * old_target_len = comp - > target_len ;
if ( comp - > sddl [ start ] ! = ' { ' ) {
comp_error ( comp , " expected '{' for composite list " ) ;
return false ;
}
if ( ! ( comp - > state & SDDL_FLAG_EXPECTING_LITERAL ) ) {
comp_error ( comp , " did not expect '{' for composite list " ) ;
return false ;
}
comp - > offset + + ; /* past '{' */
/*
* the worst case is one token for every two bytes : { 1 , 1 , 1 } , and we
* allocate for that ( counting commas and finding ' } ' gets hard because
* string literals ) .
*/
alloc_size = MIN ( ( comp - > length - start ) / 2 + 1 ,
CONDITIONAL_ACE_MAX_LENGTH ) ;
token . data . composite . tokens = talloc_array (
comp - > mem_ctx ,
struct ace_condition_token ,
alloc_size ) ;
if ( token . data . composite . tokens = = NULL ) {
comp_error ( comp , " allocation failure " ) ;
return false ;
}
comp - > target = token . data . composite . tokens ;
comp - > target_len = & token . data . composite . n_members ;
/*
* in this loop we are looking for :
*
* a ) possible whitespace .
2023-08-01 12:00:33 +12:00
* b ) a comma ( or terminating ' } ' )
2023-09-06 15:50:43 +12:00
* c ) more possible whitespace
2023-08-01 12:00:33 +12:00
* d ) a literal
2023-09-06 15:50:43 +12:00
*
* Failures use a goto to reset comp - > target , just in case we ever try
* continuing after error .
*/
while ( comp - > offset < comp - > length ) {
uint8_t c ;
ok = eat_whitespace ( comp , false ) ;
if ( ! ok ) {
goto fail ;
}
2023-08-01 12:00:33 +12:00
c = comp - > sddl [ comp - > offset ] ;
if ( c = = ' } ' ) {
comp - > offset + + ;
break ;
}
if ( ! first ) {
if ( c ! = ' , ' ) {
comp_error ( comp ,
" malformed composite (expected comma) " ) ;
goto fail ;
}
comp - > offset + + ;
ok = eat_whitespace ( comp , false ) ;
if ( ! ok ) {
goto fail ;
}
}
first = false ;
2023-09-06 15:50:43 +12:00
if ( * comp - > target_len > = alloc_size ) {
comp_error ( comp ,
" Too many tokens in composite "
" (>= % " PRIu32 " tokens) " ,
* comp - > target_len ) ;
goto fail ;
}
2023-09-22 16:31:36 +12:00
ok = parse_literal ( comp , true ) ;
2023-09-06 15:50:43 +12:00
if ( ! ok ) {
goto fail ;
}
}
comp - > target = old_target ;
comp - > target_len = old_target_len ;
write_sddl_token ( comp , token ) ;
return true ;
fail :
talloc_free ( token . data . composite . tokens ) ;
comp - > target = old_target ;
comp - > target_len = old_target_len ;
return false ;
}
static bool parse_paren_literal ( struct ace_condition_sddl_compiler_context * comp )
{
bool ok ;
if ( comp - > sddl [ comp - > offset ] ! = ' ( ' ) {
comp_error ( comp , " expected '(' " ) ;
return false ;
}
comp - > offset + + ;
2023-09-22 16:31:36 +12:00
ok = parse_literal ( comp , false ) ;
2023-09-06 15:50:43 +12:00
if ( ! ok ) {
return false ;
}
if ( comp - > sddl [ comp - > offset ] ! = ' ) ' ) {
comp_error ( comp , " expected ')' " ) ;
return false ;
}
comp - > offset + + ;
return true ;
}
static bool parse_expression ( struct ace_condition_sddl_compiler_context * comp )
{
/*
* This expects a parenthesised expression .
*/
bool ok ;
struct ace_condition_token token = { } ;
uint32_t start = comp - > offset ;
if ( comp - > state & SDDL_FLAG_EXPECTING_PAREN_LITERAL ) {
/*
2023-09-22 12:02:56 +12:00
* Syntactically we allow parentheses to wrap a
* literal value after a Member_of or > = op , but we
* want to remember that it just wants a single
* literal , not a general expression .
2023-09-06 15:50:43 +12:00
*/
return parse_paren_literal ( comp ) ;
}
if ( comp - > sddl [ start ] ! = ' ( ' ) {
comp_error ( comp , " expected '(' " ) ;
return false ;
}
if ( ! ( comp - > state & SDDL_FLAG_EXPECTING_PAREN ) ) {
comp_error ( comp , " did not expect '(' " ) ;
return false ;
}
token . type = CONDITIONAL_ACE_SAMBA_SDDL_PAREN ;
token . data . sddl_op . start = start ;
ok = push_sddl_token ( comp , token ) ;
if ( ! ok ) {
return false ;
}
comp - > offset + + ; /* over the '(' */
comp - > state = SDDL_FLAGS_EXPR_START ;
DBG_INFO ( " %3 " PRIu32 " : ( \n " , comp - > offset ) ;
2023-09-22 12:53:42 +12:00
comp - > state | = SDDL_FLAG_NOT_EXPECTING_END_PAREN ;
2023-09-06 15:50:43 +12:00
while ( comp - > offset < comp - > length ) {
uint8_t c ;
ok = eat_whitespace ( comp , false ) ;
if ( ! ok ) {
return false ;
}
c = comp - > sddl [ comp - > offset ] ;
if ( c = = ' ( ' ) {
ok = parse_expression ( comp ) ;
} else if ( c = = ' ) ' ) {
if ( comp - > state & ( SDDL_FLAG_IS_BINARY_OP |
SDDL_FLAG_IS_UNARY_OP ) ) {
/*
* You can ' t have " (a ==) " or " (!) "
*/
comp_error ( comp ,
" operator lacks right hand argument " ) ;
return false ;
}
2023-09-22 12:53:42 +12:00
if ( comp - > state & SDDL_FLAG_NOT_EXPECTING_END_PAREN ) {
/*
* You can ' t have " ( ) "
*/
comp_error ( comp , " empty expression " ) ;
return false ;
}
2023-09-06 15:50:43 +12:00
break ;
} else if ( c = = ' @ ' ) {
ok = parse_attr2 ( comp ) ;
} else if ( strchr ( " !<>=&| " , c ) ) {
ok = parse_oppy_op ( comp ) ;
} else if ( is_attr_char1 ( c ) ) {
ok = parse_word ( comp ) ;
} else if ( comp - > state & SDDL_FLAG_EXPECTING_LITERAL ) {
2023-09-22 16:31:36 +12:00
ok = parse_literal ( comp , false ) ;
2023-09-06 15:50:43 +12:00
} else {
if ( c > 31 & & c < 127 ) {
comp_error ( comp ,
" unexpected byte 0x%02x '%c' " , c , c ) ;
} else {
comp_error ( comp , " unexpected byte 0x%02x " , c ) ;
}
ok = false ;
}
if ( ! ok ) {
return false ;
}
/*
* what did we just find ? Set what we expect accordingly .
*/
comp - > state = sddl_strings [ comp - > last_token_type ] . flags ;
DBG_INFO ( " %3 " PRIu32 " : %s \n " ,
comp - > offset ,
sddl_strings [ comp - > last_token_type ] . name ) ;
}
ok = eat_whitespace ( comp , false ) ;
if ( ! ok ) {
return false ;
}
if ( comp - > sddl [ comp - > offset ] ! = ' ) ' ) {
comp_error ( comp , " expected ')' to match '(' at % " PRIu32 , start ) ;
return false ;
}
/*
* we won ' t comp - > offset + + until after these other error checks , so
* that their messages have consistent locations .
*/
ok = flush_stack_tokens ( comp , CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END ) ;
if ( ! ok ) {
return false ;
}
if ( comp - > stack_depth = = 0 ) {
comp_error ( comp , " mysterious nesting error between % "
PRIu32 " and here " ,
start ) ;
return false ;
}
token = comp - > stack [ comp - > stack_depth - 1 ] ;
if ( token . type ! = CONDITIONAL_ACE_SAMBA_SDDL_PAREN ) {
comp_error ( comp , " nesting error between % " PRIu32 " and here " ,
start ) ;
return false ;
}
if ( token . data . sddl_op . start ! = start ) {
comp_error ( comp , " ')' should match '(' at % " PRIu32
" , not % " PRIu32 ,
token . data . sddl_op . start , start ) ;
return false ;
}
comp - > stack_depth - - ;
DBG_INFO ( " %3 " PRIu32 " : ) \n " , comp - > offset ) ;
comp - > offset + + ; /* for the ')' */
comp - > last_token_type = CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END ;
comp - > state = sddl_strings [ comp - > last_token_type ] . flags ;
ok = eat_whitespace ( comp , true ) ;
return ok ;
}
static bool init_compiler_context (
TALLOC_CTX * mem_ctx ,
struct ace_condition_sddl_compiler_context * comp ,
2023-11-03 14:57:02 +13:00
const enum ace_condition_flags ace_condition_flags ,
2023-09-06 15:50:43 +12:00
const char * sddl ,
size_t max_length ,
size_t max_stack )
{
struct ace_condition_script * program = NULL ;
comp - > sddl = ( const uint8_t * ) sddl ;
comp - > mem_ctx = mem_ctx ;
program = talloc_zero ( mem_ctx , struct ace_condition_script ) ;
if ( program = = NULL ) {
return false ;
}
/*
* For the moment , we allocate for the worst case up front .
*/
program - > tokens = talloc_array ( program ,
struct ace_condition_token ,
max_length ) ;
if ( program - > tokens = = NULL ) {
TALLOC_FREE ( program ) ;
return false ;
}
program - > stack = talloc_array ( program ,
struct ace_condition_token ,
max_stack + 1 ) ;
if ( program - > stack = = NULL ) {
TALLOC_FREE ( program ) ;
return false ;
}
comp - > program = program ;
/* we can borrow the program stack for the operator stack */
comp - > stack = program - > stack ;
comp - > target = program - > tokens ;
comp - > target_len = & program - > length ;
comp - > length = strlen ( sddl ) ;
comp - > state = SDDL_FLAG_EXPECTING_PAREN ;
2023-11-03 14:57:02 +13:00
comp - > allow_device = ace_condition_flags & ACE_CONDITION_FLAG_ALLOW_DEVICE ;
2023-09-06 15:50:43 +12:00
return true ;
}
2023-07-12 17:21:06 +12:00
/*
* Compile SDDL conditional ACE conditions .
*
* @ param mem_ctx
* @ param sddl - the string to be parsed
2023-11-03 14:57:02 +13:00
* @ param ace_condition_flags - flags controlling compiler behaviour
2023-07-12 17:21:06 +12:00
* @ param message - on error , a pointer to a compiler message
* @ param message_offset - where the error occurred
* @ param consumed_length - how much of the SDDL was used
* @ return a struct ace_condition_script ( or NULL ) .
*/
struct ace_condition_script * ace_conditions_compile_sddl (
TALLOC_CTX * mem_ctx ,
2023-11-03 14:57:02 +13:00
const enum ace_condition_flags ace_condition_flags ,
2023-07-12 17:21:06 +12:00
const char * sddl ,
const char * * message ,
size_t * message_offset ,
size_t * consumed_length )
{
2023-09-06 15:50:43 +12:00
bool ok ;
struct ace_condition_sddl_compiler_context comp = { } ;
2023-10-21 12:56:24 +13:00
* message = NULL ;
2023-09-06 15:50:43 +12:00
* message_offset = 0 ;
ok = init_compiler_context ( mem_ctx ,
& comp ,
2023-11-03 14:57:02 +13:00
ace_condition_flags ,
2023-09-06 15:50:43 +12:00
sddl ,
CONDITIONAL_ACE_MAX_LENGTH ,
CONDITIONAL_ACE_MAX_TOKENS ) ;
if ( ! ok ) {
return NULL ;
}
ok = parse_expression ( & comp ) ;
if ( ! ok ) {
goto error ;
}
if ( comp . stack_depth ! = 0 ) {
comp_error ( & comp , " incomplete expression " ) ;
goto error ;
}
if ( consumed_length ! = NULL ) {
* consumed_length = comp . offset ;
}
* message = comp . message ;
* message_offset = comp . message_offset ;
return comp . program ;
error :
* message = comp . message ;
* message_offset = comp . message_offset ;
TALLOC_FREE ( comp . program ) ;
2023-07-12 17:21:06 +12:00
return NULL ;
}
2023-09-06 15:51:23 +12:00
static bool parse_resource_attr_list (
struct ace_condition_sddl_compiler_context * comp ,
char attr_type_char )
{
/*
* This is a bit like parse_composite ( ) above , but with the following
* differences :
*
* - it doesn ' t want ' { . . . } ' around the list .
* - if there is just one value , it is not a composite
* - all the values must be the expected type .
* - there is no nesting .
* - SIDs are not written with SID ( . . . ) around them .
*/
bool ok ;
2023-08-01 12:00:33 +12:00
bool first = true ;
2023-09-06 15:51:23 +12:00
struct ace_condition_token composite = {
. type = CONDITIONAL_ACE_TOKEN_COMPOSITE
} ;
uint32_t start = comp - > offset ;
size_t alloc_size ;
struct ace_condition_token * old_target = comp - > target ;
uint32_t * old_target_len = comp - > target_len ;
2023-11-02 15:48:20 +13:00
comp - > state = SDDL_FLAG_EXPECTING_LITERAL ;
2023-09-06 15:51:23 +12:00
/*
* the worst case is one token for every two bytes : { 1 , 1 , 1 } , and we
* allocate for that ( counting commas and finding ' } ' gets hard because
* string literals ) .
*/
alloc_size = MIN ( ( comp - > length - start ) / 2 + 1 ,
CONDITIONAL_ACE_MAX_LENGTH ) ;
composite . data . composite . tokens = talloc_array (
comp - > mem_ctx ,
struct ace_condition_token ,
alloc_size ) ;
if ( composite . data . composite . tokens = = NULL ) {
comp_error ( comp , " allocation failure " ) ;
return false ;
}
comp - > target = composite . data . composite . tokens ;
comp - > target_len = & composite . data . composite . n_members ;
/*
* in this loop we are looking for :
*
* a ) possible whitespace .
2023-08-01 12:00:33 +12:00
* b ) a comma ( or terminating ' ) ' )
2023-09-06 15:51:23 +12:00
* c ) more possible whitespace
2023-08-01 12:00:33 +12:00
* d ) a literal , of the right type ( checked after )
2023-09-06 15:51:23 +12:00
*
* Failures use a goto to reset comp - > target , just in case we ever try
* continuing after error .
*/
while ( comp - > offset < comp - > length ) {
uint8_t c ;
ok = eat_whitespace ( comp , false ) ;
if ( ! ok ) {
goto fail ;
}
2023-08-01 12:00:33 +12:00
c = comp - > sddl [ comp - > offset ] ;
if ( c = = ' ) ' ) {
break ;
}
if ( ! first ) {
if ( c ! = ' , ' ) {
comp_error ( comp ,
2023-11-02 15:42:11 +13:00
" malformed resource attribute ACE "
" (expected comma) " ) ;
2023-08-01 12:00:33 +12:00
goto fail ;
}
comp - > offset + + ;
ok = eat_whitespace ( comp , false ) ;
if ( ! ok ) {
goto fail ;
}
}
first = false ;
2023-09-06 15:51:23 +12:00
if ( * comp - > target_len > = alloc_size ) {
comp_error ( comp ,
2023-11-02 15:42:11 +13:00
" Too many tokens in resource attribute ACE "
2023-09-06 15:51:23 +12:00
" (>= % " PRIu32 " tokens) " ,
* comp - > target_len ) ;
goto fail ;
}
2023-11-02 15:25:06 +13:00
switch ( attr_type_char ) {
case ' X ' :
2023-11-02 15:28:15 +13:00
ok = parse_ra_octet_string ( comp ) ;
2023-11-02 15:25:06 +13:00
break ;
case ' S ' :
ok = parse_unicode ( comp ) ;
break ;
case ' U ' :
2023-11-22 13:23:26 +13:00
ok = parse_uint ( comp ) ;
break ;
2023-11-02 15:25:06 +13:00
case ' B ' :
2023-11-22 13:24:21 +13:00
ok = parse_bool ( comp ) ;
break ;
2023-11-02 15:25:06 +13:00
case ' I ' :
ok = parse_int ( comp ) ;
break ;
case ' D ' :
2023-11-02 15:37:33 +13:00
ok = parse_ra_sid ( comp ) ;
2023-11-02 15:25:06 +13:00
break ;
default :
/* it's a mystery we got this far */
comp_error ( comp ,
" unknown attribute type T%c " ,
attr_type_char ) ;
goto fail ;
}
2023-09-06 15:51:23 +12:00
if ( ! ok ) {
goto fail ;
}
if ( * comp - > target_len = = 0 ) {
goto fail ;
}
}
comp - > target = old_target ;
comp - > target_len = old_target_len ;
/*
* If we only ended up collecting one token into the composite , we
* write that instead .
*/
if ( composite . data . composite . n_members = = 1 ) {
ok = write_sddl_token ( comp , composite . data . composite . tokens [ 0 ] ) ;
talloc_free ( composite . data . composite . tokens ) ;
} else {
ok = write_sddl_token ( comp , composite ) ;
}
if ( ! ok ) {
goto fail ;
}
return true ;
fail :
comp - > target = old_target ;
comp - > target_len = old_target_len ;
TALLOC_FREE ( composite . data . composite . tokens ) ;
return false ;
}
struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 * sddl_decode_resource_attr (
TALLOC_CTX * mem_ctx ,
const char * str ,
size_t * length )
{
/*
* Resource attribute ACEs define claims in object SACLs . They look like
*
* " (RA; «flags» ;;;;WD;( «attribute-data» )) "
*
* attribute - data = DQUOTE 1 * attr - char2 DQUOTE " , " \
* ( TI - attr / TU - attr / TS - attr / TD - attr / TX - attr / TB - attr )
* TI - attr = " TI " " , " attr - flags * ( " , " int - 64 )
* TU - attr = " TU " " , " attr - flags * ( " , " uint - 64 )
* TS - attr = " TS " " , " attr - flags * ( " , " char - string )
* TD - attr = " TD " " , " attr - flags * ( " , " sid - string )
* TX - attr = " TX " " , " attr - flags * ( " , " octet - string )
* TB - attr = " TB " " , " attr - flags * ( " , " ( " 0 " / " 1 " ) )
*
2023-11-02 15:28:15 +13:00
* and the data types are * mostly * parsed in the SDDL way ,
* though there are significant differences for octet - strings .
*
2023-09-06 15:51:23 +12:00
* At this point we only have the " («attribute-data») " .
*
* What we do is set up a conditional ACE compiler to be expecting a
* literal , and ask it to parse the strings between the commas . It ' s a
* hack .
*/
bool ok ;
struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 * claim = NULL ;
struct ace_condition_sddl_compiler_context comp = { } ;
char attr_type ;
struct ace_condition_token * tok ;
uint32_t flags ;
size_t len ;
struct ace_condition_unicode attr_name = { } ;
2023-11-03 14:57:02 +13:00
ok = init_compiler_context ( mem_ctx ,
& comp ,
ACE_CONDITION_FLAG_ALLOW_DEVICE ,
str ,
3 ,
3 ) ;
2023-09-06 15:51:23 +12:00
if ( ! ok ) {
return NULL ;
}
if ( comp . length < 6 | | comp . length > CONDITIONAL_ACE_MAX_LENGTH ) {
DBG_WARNING ( " invalid resource attribute: '%s' \n " , str ) ;
goto error ;
}
/*
* Resource attribute ACEs list SIDs in a bare form " S-1-2-3 " , while
* conditional ACEs use a wrapper syntax " SID(S-1-2-3) " . As almost
* everything is the same , we are reusing the conditional ACE parser ,
* with a flag set to tell the SID parser which form to expect .
*/
/* Most examples on the web have leading whitespace */
ok = eat_whitespace ( & comp , false ) ;
if ( ! ok ) {
return NULL ;
}
if ( comp . sddl [ comp . offset ] ! = ' ( ' | |
comp . sddl [ comp . offset + 1 ] ! = ' " ' ) {
DBG_WARNING ( " invalid resource attribute -- expected '( \" ' \n " ) ;
goto error ;
}
comp . offset + = 2 ;
/*
* Read the name . Here we are not reading a token into comp - > program ,
* just into a unicode blob .
*/
len = read_attr2_string ( & comp , & attr_name ) ;
if ( len = = - 1 ) {
DBG_WARNING ( " invalid resource attr name: %s \n " , str ) ;
goto error ;
}
comp . offset + = len ;
ok = eat_whitespace ( & comp , false ) ;
if ( comp . offset + 6 > comp . length ) {
DBG_WARNING ( " invalid resource attribute (too short): '%s' \n " ,
str ) ;
goto error ;
}
/*
* now we have the name . Next comes ' " ,«T[IUSDXB]»,' followed
* by the flags , which are a 32 bit number .
*/
if ( comp . sddl [ comp . offset ] ! = ' " ' | |
comp . sddl [ comp . offset + 1 ] ! = ' , ' | |
comp . sddl [ comp . offset + 2 ] ! = ' T ' ) {
DBG_WARNING ( " expected ' \" ,T[IUSDXB]' after attr name \n " ) ;
goto error ;
}
attr_type = comp . sddl [ comp . offset + 3 ] ;
if ( comp . sddl [ comp . offset + 4 ] ! = ' , ' ) {
DBG_WARNING ( " expected ',' after attr type \n " ) ;
goto error ;
}
comp . offset + = 5 ;
comp . state = SDDL_FLAG_EXPECTING_LITERAL ;
2023-09-22 16:31:36 +12:00
ok = parse_literal ( & comp , false ) ;
2023-09-06 15:51:23 +12:00
if ( ! ok | |
comp . program - > length ! = 1 ) {
DBG_WARNING ( " invalid attr flags: %s \n " , str ) ;
goto error ;
}
tok = & comp . program - > tokens [ 0 ] ;
if ( tok - > type ! = CONDITIONAL_ACE_TOKEN_INT64 | |
tok - > data . int64 . value < 0 | |
tok - > data . int64 . value > UINT32_MAX ) {
DBG_WARNING ( " invalid attr flags (want 32 bit int): %s \n " , str ) ;
goto error ;
}
flags = tok - > data . int64 . value ;
if ( flags & 0xff00 ) {
DBG_WARNING ( " invalid attr flags, "
" stepping on reserved 0xff00 range: %s \n " ,
str ) ;
goto error ;
}
if ( comp . offset + 3 > comp . length ) {
DBG_WARNING ( " invalid resource attribute (too short): '%s' \n " ,
str ) ;
goto error ;
}
if ( comp . sddl [ comp . offset ] ! = ' , ' ) {
DBG_WARNING ( " invalid resource attribute ace \n " ) ;
goto error ;
}
comp . offset + + ;
ok = parse_resource_attr_list ( & comp , attr_type ) ;
if ( ! ok | | comp . program - > length ! = 2 ) {
DBG_WARNING ( " invalid attribute type or value: T%c, %s \n " ,
attr_type , str ) ;
goto error ;
}
if ( comp . sddl [ comp . offset ] ! = ' ) ' ) {
DBG_WARNING ( " expected trailing ')' \n " ) ;
goto error ;
}
comp . offset + + ;
* length = comp . offset ;
ok = ace_token_to_claim_v1 ( mem_ctx ,
attr_name . value ,
& comp . program - > tokens [ 1 ] ,
& claim ,
flags ) ;
if ( ! ok ) {
goto error ;
}
TALLOC_FREE ( comp . program ) ;
return claim ;
error :
TALLOC_FREE ( comp . program ) ;
return NULL ;
}
static bool write_resource_attr_from_token ( struct sddl_write_context * ctx ,
2023-11-10 16:18:32 +13:00
const struct ace_condition_token * tok )
2023-09-06 15:51:23 +12:00
{
/*
* this is a helper for sddl_resource_attr_from_claim ( ) ,
* recursing into composites if necessary .
*/
bool ok ;
char * sid = NULL ;
size_t i ;
2023-11-10 16:18:32 +13:00
const struct ace_condition_composite * c = NULL ;
2023-09-06 15:51:23 +12:00
switch ( tok - > type ) {
case CONDITIONAL_ACE_TOKEN_INT64 :
/*
* Note that this includes uint and bool claim types ,
* but we don ' t check the validity of the ranges ( 0 | 1
* and > = 0 , respectively ) , rather we trust the claim
* to be self - consistent in this regard . Going the
* other way , string - to - claim , we do check .
*/
return sddl_write_int ( ctx , tok ) ;
case CONDITIONAL_ACE_TOKEN_UNICODE :
return sddl_write_unicode ( ctx , tok ) ;
case CONDITIONAL_ACE_TOKEN_SID :
2023-07-31 10:14:48 +12:00
/* unlike conditional ACE, SID does not have a "SID()" wrapper. */
2023-09-20 09:31:31 +12:00
sid = sddl_encode_sid ( ctx - > mem_ctx , & tok - > data . sid . sid , NULL ) ;
2023-09-06 15:51:23 +12:00
if ( sid = = NULL ) {
return false ;
}
return sddl_write ( ctx , sid ) ;
case CONDITIONAL_ACE_TOKEN_OCTET_STRING :
2023-11-24 16:24:00 +13:00
return sddl_write_ra_octet_string ( ctx , tok ) ;
2023-09-06 15:51:23 +12:00
case CONDITIONAL_ACE_TOKEN_COMPOSITE :
/*
* write each token , separated by commas . If there
* were nested composites , this would flatten them ,
* but that isn ' t really possible because the token we
* are dealing with came from a claim , which has no
* facility for nesting .
*/
c = & tok - > data . composite ;
for ( i = 0 ; i < c - > n_members ; i + + ) {
ok = write_resource_attr_from_token ( ctx , & c - > tokens [ i ] ) ;
if ( ! ok ) {
return false ;
}
if ( i ! = c - > n_members - 1 ) {
ok = sddl_write ( ctx , " , " ) ;
if ( ! ok ) {
return false ;
}
}
}
return true ;
default :
/* We really really don't expect to get here */
return false ;
}
}
char * sddl_resource_attr_from_claim (
TALLOC_CTX * mem_ctx ,
const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 * claim )
{
char * s = NULL ;
char attr_type ;
bool ok ;
struct ace_condition_token tok = { } ;
struct sddl_write_context ctx = { } ;
TALLOC_CTX * tmp_ctx = NULL ;
char * name = NULL ;
size_t name_len ;
switch ( claim - > value_type ) {
case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64 :
attr_type = ' I ' ;
break ;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64 :
attr_type = ' U ' ;
break ;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING :
attr_type = ' S ' ;
break ;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID :
attr_type = ' D ' ;
break ;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN :
attr_type = ' B ' ;
break ;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING :
attr_type = ' X ' ;
break ;
default :
return NULL ;
}
tmp_ctx = talloc_new ( mem_ctx ) ;
2023-11-10 16:19:40 +13:00
if ( tmp_ctx = = NULL ) {
return NULL ;
}
2023-09-06 15:51:23 +12:00
ctx . mem_ctx = tmp_ctx ;
libcli/security: separate out claim_v1_to_ace_composite_unchecked()
For SDDL Resource ACE conversions we don't want to check too much
claim validity so that a semi-invalid ACE can round-trip through
deserialisation and serialisation. This is because Windows allows it,
but also because if the check puts the values in a sorted order that
makes the round-trip less round (that is, the return string is
semantically the same but possibly different in byte order).
The validity we're talking about is mostly uniqueness. For example
`S:(RA;;;;;WD;("foo",TU,0,7,5,7))` has two 7s, and that would be
invalid as a claim, but this is not checked while in ACE form.
On the other hand `S:(RA;;;;;WD;("foo",TU,0,3,2))` is valid, but the
return string will have 3 and 2 reversed when the check is made. We
prefer the ACE to stay the same while it is just being an ACE.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2023-11-17 13:58:12 +13:00
ok = claim_v1_to_ace_composite_unchecked ( tmp_ctx , claim , & tok ) ;
2023-09-06 15:51:23 +12:00
if ( ! ok ) {
TALLOC_FREE ( tmp_ctx ) ;
return NULL ;
}
/* this will construct the proper string in ctx.sddl */
ok = write_resource_attr_from_token ( & ctx , & tok ) ;
if ( ! ok ) {
TALLOC_FREE ( tmp_ctx ) ;
return NULL ;
}
/* escape the claim name */
ok = sddl_encode_attr_name ( tmp_ctx ,
claim - > name ,
& name , & name_len ) ;
if ( ! ok ) {
TALLOC_FREE ( tmp_ctx ) ;
return NULL ;
}
s = talloc_asprintf ( mem_ctx ,
" ( \" %s \" ,T%c,0x%x,%s) " ,
name ,
attr_type ,
claim - > flags ,
ctx . sddl ) ;
TALLOC_FREE ( tmp_ctx ) ;
return s ;
}
struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 * parse_sddl_literal_as_claim (
TALLOC_CTX * mem_ctx ,
const char * name ,
const char * str )
{
/*
* For testing purposes ( and possibly for client tools ) , we
* want to be able to create claim literals , and we might as
* well use the SDDL syntax . So we pretend to be parsing SDDL
* for one literal .
*/
bool ok ;
struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 * claim = NULL ;
struct ace_condition_sddl_compiler_context comp = { } ;
2023-11-03 14:57:02 +13:00
ok = init_compiler_context ( mem_ctx ,
& comp ,
ACE_CONDITION_FLAG_ALLOW_DEVICE ,
str ,
2 ,
2 ) ;
2023-09-06 15:51:23 +12:00
if ( ! ok ) {
return NULL ;
}
comp . state = SDDL_FLAG_EXPECTING_LITERAL ;
2023-09-22 16:31:36 +12:00
ok = parse_literal ( & comp , false ) ;
2023-09-06 15:51:23 +12:00
if ( ! ok ) {
goto error ;
}
if ( comp . program - > length ! = 1 ) {
goto error ;
}
ok = ace_token_to_claim_v1 ( mem_ctx ,
name ,
& comp . program - > tokens [ 0 ] ,
& claim ,
0 ) ;
if ( ! ok ) {
goto error ;
}
TALLOC_FREE ( comp . program ) ;
return claim ;
error :
TALLOC_FREE ( comp . program ) ;
return NULL ;
}