From 48237e529b080f6919d37773ad76f19c12df53d4 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 14 Jan 2025 00:35:24 +0100 Subject: [PATCH] libcli/security: add claims_tf_rule_set_parse_blob() for MS-CTA rules It parses [MS-CTA] rules into structures. Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme --- libcli/security/claims_transformation.h | 138 ++++ libcli/security/claims_transformation.l | 462 +++++++++++ libcli/security/claims_transformation.y | 977 ++++++++++++++++++++++++ libcli/security/wscript_build | 5 +- 4 files changed, 1581 insertions(+), 1 deletion(-) create mode 100644 libcli/security/claims_transformation.h create mode 100644 libcli/security/claims_transformation.l create mode 100644 libcli/security/claims_transformation.y diff --git a/libcli/security/claims_transformation.h b/libcli/security/claims_transformation.h new file mode 100644 index 00000000000..8027d850ec0 --- /dev/null +++ b/libcli/security/claims_transformation.h @@ -0,0 +1,138 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 2025 + + 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 . +*/ + +#ifndef _LIBCLI_SECURITY_CLAIMS_TRANSFORMATION_H_ +#define _LIBCLI_SECURITY_CLAIMS_TRANSFORMATION_H_ + +#include "librpc/gen_ndr/claims.h" + +struct claims_tf_claim { + const char *type; + enum CLAIM_TYPE value_type; + union { + int64_t ival; + uint64_t uval; + const char *string; + bool bval; + } value; +}; + +bool claims_tf_rule_set_parse_blob(const DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + struct claims_tf_rule_set **__rule_set, + char **_error_string); + +#ifdef CLAIMS_TRANSFORMATION_INTERNALS + +struct claims_tf_parser_state { + struct claims_tf_rule_set *rule_set; + struct { + int first_line; + int first_column; + int last_line; + int last_column; + char *string; + } error; +}; + +struct claims_tf_condition_ctr { + struct claims_tf_condition_ctr *prev; + struct claims_tf_condition *c1; + struct claims_tf_condition *c2; +}; +struct claims_tf_condition_set_ctr { + struct claims_tf_condition_set_ctr *prev; + struct claims_tf_condition_set *set; +}; +struct claims_tf_rule_ctr { + struct claims_tf_rule *rule; + struct claims_tf_rule_ctr *next; +}; + +struct claim_copy { + const char *identifier; +}; + +struct Cond_oper { + /* + * CLAIMS_TF_YY_EQ => CLAIMS_TF_CONDITION_OPERATOR_EQ + * CLAIMS_TF_YY_NEQ => CLAIMS_TF_CONDITION_OPERATOR_NEQ + * CLAIMS_TF_YY_REGEXP_MATCH => CLAIMS_TF_CONDITION_OPERATOR_REGEXP_MATCH + * CLAIMS_TF_YY_REGEXP_NOT_MATCH => CLAIMS_TF_CONDITION_OPERATOR_REGEXP_NOT_MATCH + */ + enum claims_tf_condition_operator operator; +}; + +struct Literal { + const char *str; +}; + +struct claim_prop { + /* + * CLAIMS_TF_YY_{TYPE,VALUE,VALUE_TYPE} + * => + * CLAIMS_TF_PROPERTY_{TYPE,VALUE,VALUE_TYPE} + * + * Here we only have TYPE or VALUE + */ + enum claims_tf_property_enum property; +}; + +struct Expr { + bool has_literal; + struct Literal literal; + struct { + const char *identifier; + /* + * CLAIMS_TF_YY_{TYPE,VALUE,VALUE_TYPE} + * => + * CLAIMS_TF_PROPERTY_{TYPE,VALUE,VALUE_TYPE} + * + * Here we only have TYPE or VALUE + */ + enum claims_tf_property_enum property; + } claim; +}; + +struct claim_type_assign { + struct Expr expr; +}; + +struct claim_val_type_assign { + struct Expr expr; +}; + +struct claim_val_assign { + struct Expr expr; +}; + +struct claim_value_assign { + struct claim_val_type_assign vt; + struct claim_val_assign val; +}; + +struct claim_new { + struct claim_type_assign type; + struct claim_value_assign value; +}; + +_PRIVATE_ enum CLAIM_TYPE claims_tf_type_from_string(const char *str); + +#endif /* CLAIMS_TRANSFORMATION_INTERNALS */ +#endif /* _LIBCLI_SECURITY_CLAIMS_TRANSFORMATION_H_ */ diff --git a/libcli/security/claims_transformation.l b/libcli/security/claims_transformation.l new file mode 100644 index 00000000000..4594adbea7e --- /dev/null +++ b/libcli/security/claims_transformation.l @@ -0,0 +1,462 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 2025 + + 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 . +*/ + +%{ +#include "includes.h" +#define CLAIMS_TRANSFORMATION_INTERNALS 1 +#include "libcli/security/claims_transformation.h" +#include "libcli/security/claims_transformation.tab.h" + +#undef strcasecmp + +static char *strip_quote(const char *phrase); + +#define YYSTYPE __CLAIMS_TF_YY_STYPE +#define YYLTYPE __CLAIMS_TF_YY_LTYPE + +_PRIVATE_ int __claims_tf_yy_lex( + YYSTYPE * yylval_param, + YYLTYPE * yylloc_param, + struct claims_tf_parser_state *ctf_ps, + yyscan_t yyscanner); + +#define YY_DECL int __claims_tf_yy_lex \ + (YYSTYPE * yylval_param, \ + YYLTYPE * yylloc_param, \ + struct claims_tf_parser_state *ctf_ps, \ + yyscan_t yyscanner) + +#define YY_USER_ACTION do { \ + size_t __idx; \ + yylloc->first_line = yylloc->last_line; \ + yylloc->first_column = yylloc->last_column; \ + for (__idx = 0; yytext[__idx] != '\0'; __idx++) { \ + if (yytext[__idx] == '\n') { \ + yylloc->last_line++; \ + yylloc->last_column = 0; \ + } else { \ + yylloc->last_column++; \ + } \ + } \ + ctf_ps->error.first_line = yylloc->first_line; \ + ctf_ps->error.first_column = yylloc->first_column; \ + ctf_ps->error.last_line = yylloc->last_line; \ + ctf_ps->error.last_column = yylloc->last_column; \ +} while(0); + +%} + +%option prefix="__claims_tf_yy_" +%option case-insensitive +%option bison-bridge +%option bison-locations +%option reentrant + +%option noyywrap +%option nounput +%option noyyalloc +%option noyyrealloc +%option noyyfree + +%option noinput +%option nounput +%option noyylineno +%option noyy_push_state +%option noyy_pop_state +%option noyy_top_state +%option noyyget_leng +%option noyyget_text +%option noyyget_lineno +%option noyyset_lineno +%option noyyget_in +%option noyyset_in +%option noyyget_out +%option noyyset_out +%option noyyget_lval +%option noyyset_lval +%option noyyget_lloc +%option noyyset_lloc +%option noyyget_debug +%option noyyset_debug + +%% +\=\> return CLAIMS_TF_YY_IMPLY; +\; return CLAIMS_TF_YY_SEMICOLON; +\: return CLAIMS_TF_YY_COLON; +\, return CLAIMS_TF_YY_COMMA; +\. return CLAIMS_TF_YY_DOT; +\[ return CLAIMS_TF_YY_O_SQ_BRACKET; +\] return CLAIMS_TF_YY_C_SQ_BRACKET; +\( return CLAIMS_TF_YY_O_BRACKET; +\) return CLAIMS_TF_YY_C_BRACKET; +\=\= return CLAIMS_TF_YY_EQ; +\!\= return CLAIMS_TF_YY_NEQ; +\=\~ return CLAIMS_TF_YY_REGEXP_MATCH; +\!\~ return CLAIMS_TF_YY_REGEXP_NOT_MATCH; +\= return CLAIMS_TF_YY_ASSIGN; +\&\& return CLAIMS_TF_YY_AND; +issue return CLAIMS_TF_YY_ISSUE; +type return CLAIMS_TF_YY_TYPE; +value return CLAIMS_TF_YY_VALUE; +valuetype return CLAIMS_TF_YY_VALUE_TYPE; +claim return CLAIMS_TF_YY_CLAIM; +[_A-Za-z][_A-Za-z0-9]* {yylval->sval = talloc_strdup(talloc_tos(), yytext); return CLAIMS_TF_YY_IDENTIFIER;} +\"[^\"\n]*\" {yylval->sval = strip_quote(yytext); return CLAIMS_TF_YY_STRING;} +[ \t\n] /* ignore */ +%% + + + +static char *strip_quote(const char *phrase) +{ + size_t phrase_len = 0; + char *stripped_phrase = NULL; + + if (phrase == NULL) { + return NULL; + } + + phrase_len = strlen(phrase); + if (phrase_len < 2 || + phrase[0] != '\"' || + phrase[phrase_len - 1] != '\"') + { + return talloc_strdup(talloc_tos(), phrase); + } + + phrase++; + + stripped_phrase = talloc_strndup(talloc_tos(), phrase, phrase_len - 2); + if (stripped_phrase == NULL) { + return NULL; + } + return stripped_phrase; +} + +_PRIVATE_ void *yyalloc(yy_size_t bytes, yyscan_t yyscanner) +{ + return talloc_size(yyscanner, bytes); +} + +_PRIVATE_ void *yyrealloc(void *ptr, yy_size_t bytes, yyscan_t yyscanner) +{ + return talloc_realloc_size(yyscanner, ptr, bytes); +} + +_PRIVATE_ void yyfree(void *ptr, yyscan_t yyscanner) +{ + if (ptr == yyscanner) { + talloc_free(yyscanner); + } else { + talloc_unlink(yyscanner, ptr); + } +} + +_PRIVATE_ enum CLAIM_TYPE claims_tf_type_from_string(const char *str) +{ + int cmp; + + cmp = strcasecmp(str, "int64"); + if (cmp == 0) { + return CLAIM_TYPE_INT64; + } + + cmp = strcasecmp(str, "uint64"); + if (cmp == 0) { + return CLAIM_TYPE_UINT64; + } + + cmp = strcasecmp(str, "string"); + if (cmp == 0) { + return CLAIM_TYPE_STRING; + } + + cmp = strcasecmp(str, "boolean"); + if (cmp == 0) { + return CLAIM_TYPE_STRING; + } + + return 0; +} + +static bool claims_tf_rule_verify_conditions(const struct claims_tf_rule *rule) +{ + uint32_t csi; + + /* + * TODO: do we need to verify that all + * optional condition_set identifiers + * are unique? + * + * At least the powershell commands + * on Windows don't verify this. + */ + + for (csi = 0; csi < rule->num_condition_sets; csi++) { + const struct claims_tf_condition_set *cs = + &rule->condition_sets[csi]; + uint32_t ci; + + for (ci = 0; ci < cs->num_conditions; ci++) { + const struct claims_tf_condition *c = + &cs->conditions[ci]; + enum CLAIM_TYPE vt; + + if (c->string == NULL) { + return false; + } + + if (c->property != CLAIMS_TF_PROPERTY_VALUE_TYPE) { + continue; + } + + vt = claims_tf_type_from_string(c->string); + if (vt == 0) { + return false; + } + } + } + + return true; +} + +static bool claims_tf_rule_verify_vt_action(const struct claims_tf_rule *rule, + const struct claims_tf_property *property) +{ + if (property->ref.property == CLAIMS_TF_PROPERTY_INVALID) { + enum CLAIM_TYPE vt; + + if (property->string == NULL) { + return false; + } + + vt = claims_tf_type_from_string(property->string); + if (vt == 0) { + return false; + } + + return true; + } + + if (property->ref.property != CLAIMS_TF_PROPERTY_VALUE_TYPE) { + return false; + } + + return true; +} + +static bool claims_tf_rule_verify_action(const struct claims_tf_rule *rule, + const struct claims_tf_property *property) +{ + uint32_t csi; + + if (property->ref.property == CLAIMS_TF_PROPERTY_INVALID) { + if (property->string == NULL) { + return false; + } + return true; + } + + if (property->ref.identifier == NULL) { + return false; + } + + for (csi = 0; csi < rule->num_condition_sets; csi++) { + const struct claims_tf_condition_set *cs = + &rule->condition_sets[csi]; + bool ok; + + if (cs->opt_identifier == NULL) { + continue; + } + + ok = strequal(property->ref.identifier, + cs->opt_identifier); + if (ok) { + return true; + } + } + + return false; +} + +_PUBLIC_ bool claims_tf_rule_set_parse_blob(const DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + struct claims_tf_rule_set **_rule_set, + char **_error_string) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct claims_tf_parser_state *ctf_ps = NULL; + yyscan_t scanner = NULL; + YY_BUFFER_STATE buf = NULL; + uint32_t ri; + int rc; + +#if __CLAIMS_TF_YY_DEBUG != 0 + __claims_tf_yy_debug = 1; +#endif + + rc = yylex_init(&scanner); + if (rc != 0) { + if (_error_string != NULL) { + *_error_string = talloc_asprintf(mem_ctx, + "yylex_init failed rc=%d", + rc); + } + + TALLOC_FREE(frame); + return false; + } + + buf = yy_scan_bytes((const char *)blob->data, + blob->length, + scanner); + if (buf == NULL) { + if (_error_string != NULL) { + *_error_string = talloc_asprintf(mem_ctx, + "yy_scan_bytes(length=%zu) failed", + blob->length); + } + + yylex_destroy(scanner); + TALLOC_FREE(frame); + return false; + } + + ctf_ps = talloc_zero(frame, struct claims_tf_parser_state); + if (ctf_ps == NULL) { + if (_error_string != NULL) { + *_error_string = talloc_asprintf(mem_ctx, + "talloc_zero failed"); + } + + yy_delete_buffer(buf, scanner); + yylex_destroy(scanner); + TALLOC_FREE(frame); + return false; + } + + rc = __claims_tf_yy_parse(ctf_ps, scanner); + if (rc != 0) { + if (_error_string != NULL && ctf_ps->error.string != NULL) { + *_error_string = talloc_asprintf(mem_ctx, + "__claims_tf_yy_parse() failed rc=%d " + "fl=%d,fc=%d,ll=%d,lc=%d: %s", + rc, + ctf_ps->error.first_line, + ctf_ps->error.first_column, + ctf_ps->error.last_line, + ctf_ps->error.last_column, + ctf_ps->error.string); + } else if (_error_string != NULL) { + *_error_string = talloc_asprintf(mem_ctx, + "__claims_tf_yy_parse() failed rc=%d", + rc); + } + + yy_delete_buffer(buf, scanner); + yylex_destroy(scanner); + TALLOC_FREE(frame); + return false; + } + + yy_delete_buffer(buf, scanner); + yylex_destroy(scanner); + + for (ri = 0; ri < ctf_ps->rule_set->num_rules; ri++) { + const struct claims_tf_rule *r = + &ctf_ps->rule_set->rules[ri]; + bool ok; + + ok = claims_tf_rule_verify_conditions(r); + if (!ok) { + if (_error_string != NULL) { + *_error_string = talloc_asprintf(mem_ctx, + "rule[%"PRIu32"] " + "has invalid conditions", + ri); + } + TALLOC_FREE(frame); + return false; + } + + ok = claims_tf_rule_verify_vt_action(r, &r->action.value_type); + if (!ok) { + if (_error_string != NULL) { + *_error_string = talloc_asprintf(mem_ctx, + "rule[%"PRIu32"] " + "action.value_type invalid value type specifier", + ri); + } + TALLOC_FREE(frame); + return false; + } + + /* + * Now verify that identifiers used + * in rule actions are also used + * as condition_set identifier. + */ + + ok = claims_tf_rule_verify_action(r, &r->action.type); + if (!ok) { + if (_error_string != NULL) { + *_error_string = talloc_asprintf(mem_ctx, + "rule[%"PRIu32"] " + "action.type invalid tidentifier %s", + ri, r->action.type.ref.identifier); + } + TALLOC_FREE(frame); + return false; + } + + ok = claims_tf_rule_verify_action(r, &r->action.value); + if (!ok) { + if (_error_string != NULL) { + *_error_string = talloc_asprintf(mem_ctx, + "rule[%"PRIu32"] " + "action.value invalid tidentifier %s", + ri, r->action.type.ref.identifier); + } + TALLOC_FREE(frame); + return false; + } + + ok = claims_tf_rule_verify_action(r, &r->action.value_type); + if (!ok) { + if (_error_string != NULL) { + *_error_string = talloc_asprintf(mem_ctx, + "rule[%"PRIu32"] " + "action.value_type invalid tidentifier %s", + ri, r->action.type.ref.identifier); + } + TALLOC_FREE(frame); + return false; + } + } + + *_rule_set = talloc_move(mem_ctx, &ctf_ps->rule_set); + TALLOC_FREE(frame); + + if (_error_string != NULL) { + *_error_string = NULL; + } + + return true; +} diff --git a/libcli/security/claims_transformation.y b/libcli/security/claims_transformation.y new file mode 100644 index 00000000000..9f34a0fab04 --- /dev/null +++ b/libcli/security/claims_transformation.y @@ -0,0 +1,977 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 2025 + + 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 . +*/ + +%{ +#include "includes.h" +#define CLAIMS_TRANSFORMATION_INTERNALS 1 +#include "libcli/security/claims_transformation.h" +#include "libcli/security/claims_transformation.tab.h" +#pragma GCC diagnostic ignored "-Wstrict-overflow" + +/* forward declarations */ +struct claims_tf_parser_state; + +static struct claims_tf_rule_set * +claims_tf_rule_set_prepare(struct claims_tf_parser_state *ctf_ps, + struct claims_tf_rule_ctr *list); +static struct claims_tf_rule_ctr * +claims_tf_rule_ctr_prepare(struct claims_tf_parser_state *ctf_ps, + struct claims_tf_rule *rule, + struct claims_tf_rule_ctr *next); +static struct claims_tf_rule * +claims_tf_rule_prepare(struct claims_tf_parser_state *ctf_ps, + const struct claim_copy *c_copy, + const struct claim_new *c_new); +static struct claims_tf_rule * +claims_tf_rule_attach_conditions(struct claims_tf_rule *rule, + struct claims_tf_condition_set_ctr *list); +static struct claims_tf_condition_set_ctr * +claims_tf_condition_set_ctr_prepare(struct claims_tf_parser_state *ctf_ps, + struct claims_tf_condition_set *set, + struct claims_tf_condition_set_ctr *prev); +static struct claims_tf_condition_set * +claims_tf_condition_set_prepare(struct claims_tf_parser_state *ctf_ps, + const struct claims_tf_condition_ctr *list, + const char *identifier); +static struct claims_tf_condition_ctr * +claims_tf_condition_ctr_prepare(struct claims_tf_parser_state *ctf_ps, + struct claims_tf_condition *c1, + struct claims_tf_condition *c2); +static struct claims_tf_condition_ctr * +claims_tf_condition_ctr_attach_prev(struct claims_tf_condition_ctr *ctr, + struct claims_tf_condition_ctr *prev); +static struct claims_tf_condition * +claims_tf_condition_prepare(struct claims_tf_parser_state *ctf_ps, + enum claims_tf_property_enum property, + enum claims_tf_condition_operator operator, + const struct Literal *expr); + +static void __claims_tf_yy_error(__CLAIMS_TF_YY_LTYPE *llocp, + struct claims_tf_parser_state *ctf_ps, + void *yyscanner, + const char *str); + +_PRIVATE_ int __claims_tf_yy_lex(__CLAIMS_TF_YY_STYPE * yylval_param, + __CLAIMS_TF_YY_LTYPE * yylloc_param , + struct claims_tf_parser_state *ctf_ps, + void *yyscanner); +%} + +%code provides { +} + +%union { + int ival; + const char *sval; + struct claims_tf_rule_set *rule_set; + struct claims_tf_rule_ctr *rule_ctr; + struct claims_tf_rule *rule; + struct claims_tf_condition_set_ctr *condition_set_ctr; + struct claims_tf_condition_set *condition_set; + struct claims_tf_condition_ctr *condition_ctr; + struct claims_tf_condition *condition; + struct claim_prop prop; + struct Cond_oper oper; + struct Expr expr; + struct Literal literal; + struct claim_copy c_copy; + struct claim_new c_new; + struct claim_value_assign value_assign; + struct claim_val_assign val_assign; + struct claim_val_type_assign val_type_assign; + struct claim_type_assign type_assign; +} + +%define api.prefix {__claims_tf_yy_} +%define parse.error verbose + +/* %define parse.trace */ + +%define api.pure full +%locations +%lex-param {struct claims_tf_parser_state *ctf_ps} +%lex-param {void *yyscanner} +%parse-param {struct claims_tf_parser_state *ctf_ps} +%parse-param {void *yyscanner} + + +%type CLAIMS_TF_YY_TYPE CLAIMS_TF_YY_VALUE CLAIMS_TF_YY_EQ CLAIMS_TF_YY_NEQ CLAIMS_TF_YY_REGEXP_MATCH CLAIMS_TF_YY_REGEXP_NOT_MATCH +%type CLAIMS_TF_YY_IDENTIFIER CLAIMS_TF_YY_STRING +%type Rule_set +%type Rules +%type Rule Issue_params Rule_action Rule_body +%type Conditions Sel_condition_list +%type Sel_condition +%type Sel_condition_body Opt_cond_list Cond_list Cond Value_cond +%type Type_cond Val_cond Val_type_cond +%type claim_prop +%type Cond_oper +%type Expr +%type Literal +%type claim_copy +%type claim_new claim_prop_assign_list +%type claim_value_assign +%type claim_val_assign +%type claim_val_type_assign +%type claim_type_assign + +%token CLAIMS_TF_YY_IMPLY +%token CLAIMS_TF_YY_SEMICOLON +%token CLAIMS_TF_YY_COLON +%token CLAIMS_TF_YY_COMMA +%token CLAIMS_TF_YY_DOT +%token CLAIMS_TF_YY_O_SQ_BRACKET +%token CLAIMS_TF_YY_C_SQ_BRACKET +%token CLAIMS_TF_YY_O_BRACKET +%token CLAIMS_TF_YY_C_BRACKET +%token CLAIMS_TF_YY_EQ +%token CLAIMS_TF_YY_NEQ +%token CLAIMS_TF_YY_REGEXP_MATCH +%token CLAIMS_TF_YY_REGEXP_NOT_MATCH +%token CLAIMS_TF_YY_ASSIGN +%token CLAIMS_TF_YY_AND +%token CLAIMS_TF_YY_ISSUE +%token CLAIMS_TF_YY_TYPE +%token CLAIMS_TF_YY_VALUE +%token CLAIMS_TF_YY_VALUE_TYPE +%token CLAIMS_TF_YY_CLAIM +%token CLAIMS_TF_YY_IDENTIFIER +%token CLAIMS_TF_YY_STRING + +%% + +Rule_set: +%empty { + $$ = claims_tf_rule_set_prepare(ctf_ps, NULL); + if ($$ == NULL) { + YYERROR; + } +} +| Rules { + $$ = claims_tf_rule_set_prepare(ctf_ps, $1); + if ($$ == NULL) { + YYERROR; + } +} +; + +Rules: +Rule { + $$ = claims_tf_rule_ctr_prepare(ctf_ps, $1, NULL); + if ($$ == NULL) { + YYERROR; + } +} +| +Rule Rules { + $$ = claims_tf_rule_ctr_prepare(ctf_ps, $1, $2); + if ($$ == NULL) { + YYERROR; + } +} +; + +Rule: +Rule_body { + $$ = $1; +} +; + +Rule_body: +Conditions CLAIMS_TF_YY_IMPLY Rule_action CLAIMS_TF_YY_SEMICOLON { + $$ = claims_tf_rule_attach_conditions($3, $1); + if ($$ == NULL) { + YYERROR; + } +} +; + +Conditions: +%empty { + $$ = NULL; +} +| +Sel_condition_list { + $$ = $1; +} +; + +Sel_condition_list: +Sel_condition { + $$ = claims_tf_condition_set_ctr_prepare(ctf_ps, $1, NULL); + if ($$ == NULL) { + YYERROR; + } +} +| +Sel_condition_list CLAIMS_TF_YY_AND Sel_condition { + $$ = claims_tf_condition_set_ctr_prepare(ctf_ps, $3, $1); + if ($$ == NULL) { + YYERROR; + } +} +; + +Sel_condition: +Sel_condition_body { + $$ = claims_tf_condition_set_prepare(ctf_ps, $1, NULL); + if ($$ == NULL) { + YYERROR; + } +} +| +CLAIMS_TF_YY_IDENTIFIER CLAIMS_TF_YY_COLON Sel_condition_body { + if ($1 == NULL) { + YYERROR; + } + $$ = claims_tf_condition_set_prepare(ctf_ps, $3, $1); + if ($$ == NULL) { + YYERROR; + } +} +; + +Sel_condition_body: +CLAIMS_TF_YY_O_SQ_BRACKET Opt_cond_list CLAIMS_TF_YY_C_SQ_BRACKET { + $$ = $2; +} +; + +Opt_cond_list: +%empty { + $$ = NULL; +} +| +Cond_list { + $$ = $1; +} +; + +Cond_list: +Cond { + $$ = $1; +} +| +Cond_list CLAIMS_TF_YY_COMMA Cond { + $$ = claims_tf_condition_ctr_attach_prev($3, $1); + if ($$ == NULL) { + YYERROR; + } +} +; + +Cond: +Value_cond { + $$ = $1; +} +| +Type_cond { + $$ = claims_tf_condition_ctr_prepare(ctf_ps, $1, NULL); + if ($$ == NULL) { + YYERROR; + } +} +; + +Type_cond: +CLAIMS_TF_YY_TYPE Cond_oper Literal { + $$ = claims_tf_condition_prepare(ctf_ps, + CLAIMS_TF_PROPERTY_TYPE, + $2.operator, + &$3); + if ($$ == NULL) { + YYERROR; + } +} +; + +Value_cond: +Val_cond CLAIMS_TF_YY_COMMA Val_type_cond { + $$ = claims_tf_condition_ctr_prepare(ctf_ps, $1, $3); + if ($$ == NULL) { + YYERROR; + } +} +| +Val_type_cond CLAIMS_TF_YY_COMMA Val_cond { + $$ = claims_tf_condition_ctr_prepare(ctf_ps, $1, $3); + if ($$ == NULL) { + YYERROR; + } +} +; + +Val_cond: +CLAIMS_TF_YY_VALUE Cond_oper Literal { + $$ = claims_tf_condition_prepare(ctf_ps, + CLAIMS_TF_PROPERTY_VALUE, + $2.operator, + &$3); + if ($$ == NULL) { + YYERROR; + } +} +; + +Val_type_cond: +CLAIMS_TF_YY_VALUE_TYPE Cond_oper Literal { + $$ = claims_tf_condition_prepare(ctf_ps, + CLAIMS_TF_PROPERTY_VALUE_TYPE, + $2.operator, + &$3); + if ($$ == NULL) { + YYERROR; + } +} +; + +claim_prop: +CLAIMS_TF_YY_TYPE { + $$ = (struct claim_prop) { .property = CLAIMS_TF_PROPERTY_TYPE, }; +} +| +CLAIMS_TF_YY_VALUE { + $$ = (struct claim_prop) { .property = CLAIMS_TF_PROPERTY_VALUE, }; +} +| +CLAIMS_TF_YY_VALUE_TYPE { + $$ = (struct claim_prop) { .property = CLAIMS_TF_PROPERTY_VALUE_TYPE, }; +} +; + +Cond_oper: +CLAIMS_TF_YY_EQ { + $$ = (struct Cond_oper) { .operator = CLAIMS_TF_CONDITION_OPERATOR_EQ, }; +} +| +CLAIMS_TF_YY_NEQ { + $$ = (struct Cond_oper) { .operator = CLAIMS_TF_CONDITION_OPERATOR_NEQ, }; +} +| +CLAIMS_TF_YY_REGEXP_MATCH { + $$ = (struct Cond_oper) { .operator = CLAIMS_TF_CONDITION_OPERATOR_REGEXP_MATCH, }; +} +| +CLAIMS_TF_YY_REGEXP_NOT_MATCH { + $$ = (struct Cond_oper) { .operator = CLAIMS_TF_CONDITION_OPERATOR_REGEXP_NOT_MATCH, }; +} +; + +Expr: +Literal { + $$ = (struct Expr) { + .has_literal = true, + .literal = $1, + }; +} +| +CLAIMS_TF_YY_IDENTIFIER CLAIMS_TF_YY_DOT claim_prop { + if ($1 == NULL) { + YYERROR; + } + $$ = (struct Expr) { + .claim = { + .identifier = $1, + .property = $3.property, + }, + }; +} +; + +Literal: +CLAIMS_TF_YY_STRING { + $$ = (struct Literal) { .str = $1, }; +} +; + +Rule_action: +CLAIMS_TF_YY_ISSUE CLAIMS_TF_YY_O_BRACKET Issue_params CLAIMS_TF_YY_C_BRACKET { + $$ = $3; +} +; + +Issue_params: +claim_copy { + $$ = claims_tf_rule_prepare(ctf_ps, &$1, NULL); + if ($$ == NULL) { + YYERROR; + } +} +| +claim_new { + $$ = claims_tf_rule_prepare(ctf_ps, NULL, &$1); + if ($$ == NULL) { + YYERROR; + } +} +; + +claim_copy: +CLAIMS_TF_YY_CLAIM CLAIMS_TF_YY_ASSIGN CLAIMS_TF_YY_IDENTIFIER { + $$ = (struct claim_copy) { .identifier = $3, }; +} +; + +claim_new: +claim_prop_assign_list { + $$ = $1; +} +; + +claim_prop_assign_list: +claim_value_assign CLAIMS_TF_YY_COMMA claim_type_assign { + $$ = (struct claim_new) { + .type = $3, + .value = $1, + }; +} +| +claim_type_assign CLAIMS_TF_YY_COMMA claim_value_assign { + $$ = (struct claim_new) { + .type = $1, + .value = $3, + }; +} +; + +claim_value_assign: +claim_val_assign CLAIMS_TF_YY_COMMA claim_val_type_assign { + $$ = (struct claim_value_assign) { + .vt = $3, + .val = $1, + }; +} +| +claim_val_type_assign CLAIMS_TF_YY_COMMA claim_val_assign { + $$ = (struct claim_value_assign) { + .vt = $1, + .val = $3, + }; +} +; + +claim_val_assign: +CLAIMS_TF_YY_VALUE CLAIMS_TF_YY_ASSIGN Expr { + $$ = (struct claim_val_assign) { .expr = $3, }; +} +; + +claim_val_type_assign: +CLAIMS_TF_YY_VALUE_TYPE CLAIMS_TF_YY_ASSIGN Expr { + if ($3.has_literal) { + const char *lstr = $3.literal.str; + enum CLAIM_TYPE vt; + + if (lstr == NULL) { + YYERROR; + } + + vt = claims_tf_type_from_string(lstr); + if (vt == 0) { + ctf_ps->error.string = talloc_asprintf(ctf_ps, + "Invalid " + "ValueType " + "string[%s]", + lstr); + YYERROR; + } + } else { + if ($3.claim.property != CLAIMS_TF_PROPERTY_VALUE_TYPE) { + const char *istr = $3.claim.identifier; + + if (istr == NULL) { + istr = ""; + } + + ctf_ps->error.string = talloc_asprintf(ctf_ps, + "ValueType " + "requires " + "%s.ValueType", + istr); + YYERROR; + } + } + $$ = (struct claim_val_type_assign) { .expr = $3, }; +} +; + +claim_type_assign: +CLAIMS_TF_YY_TYPE CLAIMS_TF_YY_ASSIGN Expr { + $$ = (struct claim_type_assign) { .expr = $3, }; +} +; + + +%% + +static struct claims_tf_rule_set * +claims_tf_rule_set_prepare(struct claims_tf_parser_state *ctf_ps, + struct claims_tf_rule_ctr *list) +{ + struct claims_tf_rule_set *rule_set = NULL; + struct claims_tf_rule_ctr *cr = NULL; + size_t num_rules = 0; + struct claims_tf_rule *rules = NULL; + size_t i; + + if (ctf_ps == NULL) { + return NULL; + } + if (ctf_ps->rule_set != NULL) { + return NULL; + } + + rule_set = talloc_zero(ctf_ps, + struct claims_tf_rule_set); + if (rule_set == NULL) { + return NULL; + } + + for (cr = list; cr != NULL; cr = cr->next) { + if (cr->rule == NULL) { + TALLOC_FREE(rule_set); + return NULL; + } + + num_rules += 1; + } + + if (num_rules >= UINT32_MAX) { + TALLOC_FREE(rule_set); + return NULL; + } + if (num_rules > 0) { + rules = talloc_zero_array(rule_set, + struct claims_tf_rule, + num_rules); + if (rules == NULL) { + TALLOC_FREE(rule_set); + return NULL; + } + } + + i = 0; + for (cr = list; cr != NULL; cr = cr->next) { + SMB_ASSERT(i < num_rules); + talloc_steal(rule_set, cr->rule); + rules[i] = *cr->rule; + i += 1; + } + SMB_ASSERT(i == num_rules); + rule_set->num_rules = num_rules; + rule_set->rules = rules; + + ctf_ps->rule_set = rule_set; + return rule_set; +} + +static bool claims_tf_property_from_expr(TALLOC_CTX *mem_ctx, + const struct Expr *expr, + struct claims_tf_property *prop) +{ + if (expr->has_literal) { + if (expr->literal.str == NULL) { + return false; + } + + *prop = (struct claims_tf_property) { + .string = talloc_strdup(mem_ctx, expr->literal.str), + }; + if (prop->string == NULL) { + return false; + } + + return true; + } + + if (expr->claim.identifier == NULL) { + return false; + } + + *prop = (struct claims_tf_property) { + .ref = { + .identifier = talloc_strdup(mem_ctx, + expr->claim.identifier), + .property = expr->claim.property, + }, + }; + if (prop->ref.identifier == NULL) { + return false; + } + + return true; +} + +static struct claims_tf_rule_ctr * +claims_tf_rule_ctr_prepare(struct claims_tf_parser_state *ctf_ps, + struct claims_tf_rule *rule, + struct claims_tf_rule_ctr *next) +{ + struct claims_tf_rule_ctr *rule_ctr = NULL; + + if (ctf_ps == NULL) { + return NULL; + } + + rule_ctr = talloc_zero(ctf_ps, struct claims_tf_rule_ctr); + if (rule_ctr == NULL) { + return NULL; + } + + rule_ctr->rule = talloc_steal(rule_ctr, rule); + rule_ctr->next = talloc_steal(rule_ctr, next); + + return rule_ctr; +} + +static struct claims_tf_rule * +claims_tf_rule_prepare(struct claims_tf_parser_state *ctf_ps, + const struct claim_copy *c_copy, + const struct claim_new *c_new) +{ + struct claims_tf_rule *rule = NULL; + const struct Expr *type_expr = NULL; + const struct Expr *vt_expr = NULL; + const struct Expr *val_expr = NULL; + bool ok; + + if (ctf_ps == NULL) { + return NULL; + } + + rule = talloc_zero(ctf_ps, struct claims_tf_rule); + if (rule == NULL) { + return NULL; + } + + if (c_copy != NULL) { + const char *identifier = NULL; + + if (c_copy->identifier == NULL) { + TALLOC_FREE(rule); + return NULL; + } + + identifier = talloc_strdup(rule, c_copy->identifier); + if (identifier == NULL) { + TALLOC_FREE(rule); + return NULL; + } + + /* + * Copy all properties from the given claim identifier + */ + rule->action.type.ref = (struct claims_tf_property_ref) { + .identifier = identifier, + .property = CLAIMS_TF_PROPERTY_TYPE, + }; + rule->action.value_type.ref = (struct claims_tf_property_ref) { + .identifier = identifier, + .property = CLAIMS_TF_PROPERTY_VALUE_TYPE, + }; + rule->action.value.ref = (struct claims_tf_property_ref) { + .identifier = identifier, + .property = CLAIMS_TF_PROPERTY_VALUE, + }; + + return rule; + } + + if (c_new == NULL) { + TALLOC_FREE(rule); + return NULL; + } + + type_expr = &c_new->type.expr; + vt_expr = &c_new->value.vt.expr; + val_expr = &c_new->value.val.expr; + + ok = claims_tf_property_from_expr(rule, type_expr, &rule->action.type); + if (!ok) { + TALLOC_FREE(rule); + return NULL; + } + + ok = claims_tf_property_from_expr(rule, vt_expr, &rule->action.value_type); + if (!ok) { + TALLOC_FREE(rule); + return NULL; + } + + ok = claims_tf_property_from_expr(rule, val_expr, &rule->action.value); + if (!ok) { + TALLOC_FREE(rule); + return NULL; + } + + return rule; +} + +static struct claims_tf_rule * +claims_tf_rule_attach_conditions(struct claims_tf_rule *rule, + struct claims_tf_condition_set_ctr *list) +{ + struct claims_tf_condition_set_ctr *cl = NULL; + size_t num_condition_sets = 0; + struct claims_tf_condition_set *condition_sets = NULL; + size_t i; + + if (rule == NULL) { + return NULL; + } + + for (cl = list; cl != NULL; cl = cl->prev) { + if (cl->set == NULL) { + TALLOC_FREE(rule); + return NULL; + } + num_condition_sets += 1; + } + + if (num_condition_sets >= UINT32_MAX) { + TALLOC_FREE(rule); + return NULL; + } + + if (num_condition_sets != 0) { + condition_sets = talloc_zero_array(rule, + struct claims_tf_condition_set, + num_condition_sets); + if (condition_sets == NULL) { + TALLOC_FREE(rule); + return NULL; + } + } + + /* + * list is a list from tail to head. + * + * But we want rule->condition_sets to have + * head at index 0. + */ + i = num_condition_sets; + for (cl = list; cl != NULL; cl = cl->prev) { + SMB_ASSERT(i > 0); + i -= 1; + talloc_steal(rule, cl->set); + condition_sets[i] = *cl->set; + } + SMB_ASSERT(i == 0); + rule->num_condition_sets = num_condition_sets; + rule->condition_sets = condition_sets; + + return rule; +} + +static struct claims_tf_condition_set_ctr * +claims_tf_condition_set_ctr_prepare(struct claims_tf_parser_state *ctf_ps, + struct claims_tf_condition_set *set, + struct claims_tf_condition_set_ctr *prev) +{ + struct claims_tf_condition_set_ctr *condition_set_ctr = NULL; + + if (ctf_ps == NULL) { + return NULL; + } + + condition_set_ctr = talloc_zero(ctf_ps, struct claims_tf_condition_set_ctr); + if (condition_set_ctr == NULL) { + return NULL; + } + + condition_set_ctr->set = talloc_steal(condition_set_ctr, set); + condition_set_ctr->prev = talloc_steal(condition_set_ctr, prev); + + return condition_set_ctr; +} + +static struct claims_tf_condition_set * +claims_tf_condition_set_prepare(struct claims_tf_parser_state *ctf_ps, + const struct claims_tf_condition_ctr *list, + const char *identifier) +{ + struct claims_tf_condition_set *condition_set = NULL; + const struct claims_tf_condition_ctr *cl = NULL; + size_t num_conditions = 0; + struct claims_tf_condition *conditions = NULL; + size_t i; + + if (ctf_ps == NULL) { + return NULL; + } + + condition_set = talloc_zero(ctf_ps, struct claims_tf_condition_set); + if (condition_set == NULL) { + return NULL; + } + + if (identifier != NULL) { + condition_set->opt_identifier = talloc_strdup(condition_set, + identifier); + if (condition_set->opt_identifier == NULL) { + TALLOC_FREE(condition_set); + return NULL; + } + } + + for (cl = list; cl != NULL; cl = cl->prev) { + if (cl->c1 == NULL) { + TALLOC_FREE(condition_set); + return NULL; + } + + if (cl->c2 != NULL) { + num_conditions += 1; + } + num_conditions += 1; + } + + if (num_conditions >= UINT32_MAX) { + TALLOC_FREE(condition_set); + return NULL; + } + + if (num_conditions > 0) { + conditions = talloc_zero_array(condition_set, + struct claims_tf_condition, + num_conditions); + if (conditions == NULL) { + TALLOC_FREE(condition_set); + return NULL; + } + } + + /* + * list is a list from tail to head. + * + * But we want rule->condition_sets to have + * head at index 0. + */ + i = num_conditions; + for (cl = list; cl != NULL; cl = cl->prev) { + if (cl->c2 != NULL) { + SMB_ASSERT(i > 0); + i -= 1; + talloc_steal(condition_set, cl->c2); + conditions[i] = *cl->c2; + } + SMB_ASSERT(i > 0); + i -= 1; + talloc_steal(condition_set, cl->c1); + conditions[i] = *cl->c1; + } + SMB_ASSERT(i == 0); + condition_set->num_conditions = num_conditions; + condition_set->conditions = conditions; + + return condition_set; +} + +static struct claims_tf_condition_ctr * +claims_tf_condition_ctr_prepare(struct claims_tf_parser_state *ctf_ps, + struct claims_tf_condition *c1, + struct claims_tf_condition *c2) +{ + struct claims_tf_condition_ctr *condition_ctr = NULL; + + if (ctf_ps == NULL) { + return NULL; + } + + condition_ctr = talloc_zero(ctf_ps, struct claims_tf_condition_ctr); + if (condition_ctr == NULL) { + return NULL; + } + + condition_ctr->c1 = talloc_steal(condition_ctr, c1); + condition_ctr->c2 = talloc_steal(condition_ctr, c2); + + return condition_ctr; +} + +static struct claims_tf_condition_ctr * +claims_tf_condition_ctr_attach_prev(struct claims_tf_condition_ctr *ctr, + struct claims_tf_condition_ctr *prev) +{ + ctr->prev = talloc_steal(ctr, prev); + return ctr; +} + +static struct claims_tf_condition * +claims_tf_condition_prepare(struct claims_tf_parser_state *ctf_ps, + enum claims_tf_property_enum property, + enum claims_tf_condition_operator operator, + const struct Literal *expr) +{ + struct claims_tf_condition *condition = NULL; + const char *match = NULL; + + if (ctf_ps == NULL) { + return NULL; + } + + condition = talloc_zero(ctf_ps, struct claims_tf_condition); + if (condition == NULL) { + return NULL; + } + + match = expr->str; + + if (match == NULL) { + TALLOC_FREE(condition); + return NULL; + } + + if (property == CLAIMS_TF_PROPERTY_VALUE_TYPE) { + enum CLAIM_TYPE vt; + + vt = claims_tf_type_from_string(match); + if (vt == 0) { + ctf_ps->error.string = talloc_asprintf(ctf_ps, + "Invalid " + "ValueType " + "string[%s]", + match); + TALLOC_FREE(condition); + return NULL; + } + } + + condition->property = property; + condition->operator = operator; + condition->string = talloc_strdup(condition, match); + if (condition->string == NULL) { + TALLOC_FREE(condition); + return NULL; + } + + return condition; +} + +static void __claims_tf_yy_error(__CLAIMS_TF_YY_LTYPE *llocp, + struct claims_tf_parser_state *ctf_ps, + void *yyscanner, + const char *str) +{ + ctf_ps->error.first_line = llocp->first_line; + ctf_ps->error.first_column = llocp->first_column; + ctf_ps->error.last_line = llocp->last_line; + ctf_ps->error.last_column = llocp->last_column; + ctf_ps->error.string = talloc_strdup(ctf_ps, str); +} diff --git a/libcli/security/wscript_build b/libcli/security/wscript_build index db8a9b94c1e..d23d3a1268d 100644 --- a/libcli/security/wscript_build +++ b/libcli/security/wscript_build @@ -9,7 +9,10 @@ bld.SAMBA_LIBRARY('samba-security', 'object_tree.c', 'create_descriptor.c', 'util_sid.c', 'session.c', 'secdesc.c', 'conditional_ace.c', 'sddl_conditional_ace.c', - 'claims-conversions.c'], + 'claims-conversions.c', + 'claims_transformation.l', + 'claims_transformation.y', + ], private_library=True, deps='stable_sort talloc ndr NDR_SECURITY NDR_CONDITIONAL_ACE') -- 2.47.2