/* 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); }