The branch, master has been updated via 436d888684f ndr_claims: only use compression if it actually reduces the size via 84b7de6deb4 python:tests/krb5: only expect compressed claims if the compression reduces the size via 23aa5e897c7 python:tests: add ClaimsTransformationTests to security.py via f8f92e71697 libcli/security: add py_claims_tf_policy_{parse_rules,wrap_xml}() via c33a441c8e5 libcli/security: add claims_tf_policy_[un]wrap_xml() for msDS-TransformationRules via 48237e529b0 libcli/security: add claims_tf_rule_set_parse_blob() for MS-CTA rules via a6474a96ade claims.idl: add some helper structs for claims transformation [MS-CTA] via 0ba1a8d7769 mdssvc: support a few more attributes from 6430e0a9fb7 vfs_ceph_new:minor logging improvement
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 436d888684fb718c4bb5ae9b33460aef3918c2af Author: Stefan Metzmacher <me...@samba.org> Date: Wed Jan 15 10:30:53 2025 +0100 ndr_claims: only use compression if it actually reduces the size I have captures showing that claims compression depends on the payload itself and how well it compresses, instead of the pure length of the payload. E.g. a single string claim with a value of 68 'a' characters has an unpressed size of 336 and compressed size is 335. While a single string with random string s1 has an unpressed size of 504 and it's still uncompressed on the wire. A different random string s2 also has an unpressed size of 504, but it is compressed into a size of 502. So it really depends if the compression makes it actually smaller than the uncompressed version. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> Autobuild-User(master): Ralph Böhme <s...@samba.org> Autobuild-Date(master): Fri Feb 14 11:56:49 UTC 2025 on atb-devel-224 commit 84b7de6deb453439b42948809f83dc634ee4a8ef Author: Stefan Metzmacher <me...@samba.org> Date: Wed Jan 15 12:24:04 2025 +0100 python:tests/krb5: only expect compressed claims if the compression reduces the size I have captures showing that claims compression depends on the payload itself and how well it compresses, instead of the pure length of the payload. E.g. a single string claim with a value of 68 'a' characters has an unpressed size of 336 and compressed size is 335. While a single string with random string s1 has an unpressed size of 504 and it's still uncompressed on the wire. A different random string s2 also has an unpressed size of 504, but it is compressed into a size of 502. So it really depends if the compression makes it actually smaller than the uncompressed version. This makes the tests more reliable against Windows DCs with existing claims defined. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> commit 23aa5e897c7214c14624386dfc87d3e370253b1e Author: Stefan Metzmacher <me...@samba.org> Date: Mon Feb 3 17:15:28 2025 +0100 python:tests: add ClaimsTransformationTests to security.py Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> commit f8f92e716979843dfc05ffc912c649028ffef9eb Author: Stefan Metzmacher <me...@samba.org> Date: Fri Jan 17 13:24:28 2025 +0100 libcli/security: add py_claims_tf_policy_{parse_rules,wrap_xml}() Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> commit c33a441c8e5c532d571cc661593ef0b3a6edcb1b Author: Stefan Metzmacher <me...@samba.org> Date: Mon Feb 3 14:31:23 2025 +0100 libcli/security: add claims_tf_policy_[un]wrap_xml() for msDS-TransformationRules Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> commit 48237e529b080f6919d37773ad76f19c12df53d4 Author: Stefan Metzmacher <me...@samba.org> Date: Tue Jan 14 00:35:24 2025 +0100 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 <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> commit a6474a96ade036e081fa0e976448756b66f848c5 Author: Stefan Metzmacher <me...@samba.org> Date: Mon Feb 3 13:56:54 2025 +0100 claims.idl: add some helper structs for claims transformation [MS-CTA] Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Ralph Boehme <s...@samba.org> commit 0ba1a8d77694182058d1c01b54a8759bdf0e28a6 Author: Ralph Boehme <s...@samba.org> Date: Wed Jan 29 15:11:16 2025 +0100 mdssvc: support a few more attributes This adds support for the following Spotlight Metadata Attributes: _kMDItemFileName (another alias for kMDItemFSName and kMDItemDisplayName) kMDItemLastUsedDate kMDItemContentCreationDate kMDItemLogicalSize (another alias for kMDItemFSSize) BUG: https://bugzilla.samba.org/show_bug.cgi?id=15796 Signed-off-by: Ralph Boehme <s...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> ----------------------------------------------------------------------- Summary of changes: libcli/security/claims_transformation.h | 144 +++++ libcli/security/claims_transformation.l | 534 +++++++++++++++++ libcli/security/claims_transformation.y | 977 ++++++++++++++++++++++++++++++++ libcli/security/pysecurity.c | 124 ++++ libcli/security/wscript_build | 8 +- librpc/idl/claims.idl | 58 +- librpc/ndr/ndr_claims.c | 125 +++- librpc/ndr/ndr_claims.h | 3 +- python/samba/tests/krb5/raw_testcase.py | 36 +- python/samba/tests/security.py | 464 ++++++++++++++- source3/rpc_server/mdssvc/mdssvc.c | 23 +- 11 files changed, 2452 insertions(+), 44 deletions(-) create mode 100644 libcli/security/claims_transformation.h create mode 100644 libcli/security/claims_transformation.l create mode 100644 libcli/security/claims_transformation.y Changeset truncated at 500 lines: diff --git a/libcli/security/claims_transformation.h b/libcli/security/claims_transformation.h new file mode 100644 index 00000000000..e61922a26a8 --- /dev/null +++ b/libcli/security/claims_transformation.h @@ -0,0 +1,144 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#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); + +char *claims_tf_policy_wrap_xml(TALLOC_CTX *mem_ctx, + const char *rules_string); + +bool claims_tf_policy_unwrap_xml(const DATA_BLOB *attr_val, + DATA_BLOB *rules); + +#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..ff6c0478f7e --- /dev/null +++ b/libcli/security/claims_transformation.l @@ -0,0 +1,534 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +%{ +#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) { -- Samba Shared Repository