libcli: Simplify debug_conditional_ace()
[samba4-gss.git] / libcli / security / sddl_conditional_ace.c
blob62323a300a0ce53963297266119ae246523a14cd
1 /*
2 * Unix SMB implementation.
3 * Functions for understanding conditional ACEs
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 #include "includes.h"
20 #include "librpc/gen_ndr/ndr_security.h"
21 #include "librpc/gen_ndr/conditional_ace.h"
22 #include "libcli/security/security.h"
23 #include "libcli/security/conditional_ace.h"
24 #include "libcli/security/claims-conversions.h"
25 #include "lib/util/tsort.h"
26 #include "lib/util/bytearray.h"
29 /* We're only dealing with utf-8 here. Honestly. */
30 #undef strncasecmp
33 #define SDDL_FLAG_EXPECTING_UNARY_OP 1
34 #define SDDL_FLAG_EXPECTING_BINARY_OP 2
35 #define SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP 4
36 #define SDDL_FLAG_EXPECTING_LOCAL_ATTR 8
37 #define SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR 16
38 #define SDDL_FLAG_EXPECTING_LITERAL 32
39 #define SDDL_FLAG_EXPECTING_PAREN 64
40 #define SDDL_FLAG_EXPECTING_PAREN_LITERAL 128
41 #define SDDL_FLAG_NOT_EXPECTING_END_PAREN 256
43 #define SDDL_FLAG_DEVICE 512
45 #define SDDL_FLAG_IS_UNARY_OP (1 << 20)
46 #define SDDL_FLAG_IS_BINARY_OP (1 << 21)
49 #define SDDL_FLAGS_EXPR_START (SDDL_FLAG_EXPECTING_UNARY_OP | \
50 SDDL_FLAG_EXPECTING_LOCAL_ATTR | \
51 SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
52 SDDL_FLAG_EXPECTING_PAREN)
54 #define SDDL_FLAGS_MEMBER_OP (SDDL_FLAG_EXPECTING_LITERAL | \
55 SDDL_FLAG_EXPECTING_PAREN_LITERAL | \
56 SDDL_FLAG_IS_UNARY_OP)
58 #define SDDL_FLAGS_RELATIONAL_OP (SDDL_FLAG_EXPECTING_LITERAL | \
59 SDDL_FLAG_EXPECTING_PAREN_LITERAL | \
60 SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
61 SDDL_FLAG_IS_BINARY_OP)
63 #define SDDL_FLAGS_CONTAINS_OP (SDDL_FLAG_EXPECTING_LITERAL | \
64 SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
65 SDDL_FLAG_IS_BINARY_OP)
67 #define SDDL_FLAGS_EXISTS_OP (SDDL_FLAG_EXPECTING_LOCAL_ATTR | \
68 SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
69 SDDL_FLAG_IS_UNARY_OP)
71 #define SDDL_FLAGS_LOGIC_OP (SDDL_FLAG_EXPECTING_LOCAL_ATTR | \
72 SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
73 SDDL_FLAG_EXPECTING_PAREN | \
74 SDDL_FLAG_EXPECTING_UNARY_OP | \
75 SDDL_FLAG_IS_BINARY_OP)
77 #define SDDL_FLAGS_ATTRIBUTE (SDDL_FLAG_EXPECTING_BINARY_OP | \
78 SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP)
80 #define SDDL_FLAGS_LITERAL SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP
82 #define SDDL_FLAGS_PAREN_END (SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP | \
83 SDDL_FLAG_EXPECTING_BINARY_OP)
85 enum {
86 SDDL_NOT_AN_OP = 0,
87 SDDL_PRECEDENCE_EXISTS,
88 SDDL_PRECEDENCE_COMMON,
89 SDDL_PRECEDENCE_NOT,
90 SDDL_PRECEDENCE_AND,
91 SDDL_PRECEDENCE_OR,
92 SDDL_PRECEDENCE_PAREN_END,
93 SDDL_PRECEDENCE_PAREN_START,
96 struct ace_condition_sddl_compiler_context {
97 TALLOC_CTX *mem_ctx;
98 const uint8_t *sddl;
99 uint32_t length;
100 uint32_t offset;
101 uint32_t stack_depth;
102 uint32_t max_program_length;
103 uint32_t approx_size;
104 struct ace_condition_script *program;
105 struct ace_condition_token *stack;
106 struct ace_condition_token *target;
107 uint32_t *target_len;
108 const char *message;
109 uint32_t message_offset;
110 struct dom_sid *domain_sid;
111 uint32_t state;
112 uint8_t last_token_type;
113 bool allow_device;
116 struct sddl_data {
117 const char *name;
118 uint32_t flags;
119 uint8_t op_precedence;
120 uint8_t nargs;
123 static const struct sddl_data sddl_strings[256] = {
124 /* operators */
125 [CONDITIONAL_ACE_TOKEN_MEMBER_OF] = {
126 "Member_of",
127 SDDL_FLAGS_MEMBER_OP,
128 SDDL_PRECEDENCE_COMMON,
131 [CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF] = {
132 "Device_Member_of",
133 SDDL_FLAGS_MEMBER_OP|SDDL_FLAG_DEVICE,
134 SDDL_PRECEDENCE_COMMON,
137 [CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY] = {
138 /* [MS-DTYP] says "_Any", but windows prefers '_any' */
139 "Member_of_any",
140 SDDL_FLAGS_MEMBER_OP,
141 SDDL_PRECEDENCE_COMMON,
144 [CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY] = {
145 "Device_Member_of_Any",
146 SDDL_FLAGS_MEMBER_OP|SDDL_FLAG_DEVICE,
147 SDDL_PRECEDENCE_COMMON,
150 [CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF] = {
151 "Not_Member_of",
152 SDDL_FLAGS_MEMBER_OP,
153 SDDL_PRECEDENCE_COMMON,
156 [CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF] = {
157 "Not_Device_Member_of",
158 SDDL_FLAGS_MEMBER_OP|SDDL_FLAG_DEVICE,
159 SDDL_PRECEDENCE_COMMON,
162 [CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY] = {
163 "Not_Member_of_Any",
164 SDDL_FLAGS_MEMBER_OP,
165 SDDL_PRECEDENCE_COMMON,
168 [CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY] = {
169 "Not_Device_Member_of_Any",
170 SDDL_FLAGS_MEMBER_OP|SDDL_FLAG_DEVICE,
171 SDDL_PRECEDENCE_COMMON,
174 [CONDITIONAL_ACE_TOKEN_EQUAL] = {
175 "==",
176 SDDL_FLAGS_RELATIONAL_OP,
177 SDDL_PRECEDENCE_COMMON,
180 [CONDITIONAL_ACE_TOKEN_NOT_EQUAL] = {
181 "!=",
182 SDDL_FLAGS_RELATIONAL_OP,
183 SDDL_PRECEDENCE_COMMON,
186 [CONDITIONAL_ACE_TOKEN_LESS_THAN] = {
187 "<",
188 SDDL_FLAGS_RELATIONAL_OP,
189 SDDL_PRECEDENCE_COMMON,
192 [CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL] = {
193 "<=",
194 SDDL_FLAGS_RELATIONAL_OP,
195 SDDL_PRECEDENCE_COMMON,
198 [CONDITIONAL_ACE_TOKEN_GREATER_THAN] = {
199 ">",
200 SDDL_FLAGS_RELATIONAL_OP,
201 SDDL_PRECEDENCE_COMMON,
204 [CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL] = {
205 ">=",
206 SDDL_FLAGS_RELATIONAL_OP,
207 SDDL_PRECEDENCE_COMMON,
210 [CONDITIONAL_ACE_TOKEN_CONTAINS] = {
211 "Contains",
212 SDDL_FLAGS_CONTAINS_OP,
213 SDDL_PRECEDENCE_COMMON,
216 [CONDITIONAL_ACE_TOKEN_ANY_OF] = {
217 "Any_of",
218 SDDL_FLAGS_CONTAINS_OP,
219 SDDL_PRECEDENCE_COMMON,
222 [CONDITIONAL_ACE_TOKEN_NOT_CONTAINS] = {
223 "Not_Contains",
224 SDDL_FLAGS_CONTAINS_OP,
225 SDDL_PRECEDENCE_COMMON,
228 [CONDITIONAL_ACE_TOKEN_NOT_ANY_OF] = {
229 "Not_Any_of",
230 SDDL_FLAGS_CONTAINS_OP,
231 SDDL_PRECEDENCE_COMMON,
234 [CONDITIONAL_ACE_TOKEN_AND] = {
235 "&&",
236 SDDL_FLAGS_LOGIC_OP,
237 SDDL_PRECEDENCE_AND,
240 [CONDITIONAL_ACE_TOKEN_OR] = {
241 "||",
242 SDDL_FLAGS_LOGIC_OP,
243 SDDL_PRECEDENCE_OR,
246 [CONDITIONAL_ACE_TOKEN_NOT] = {
247 "!",
248 (SDDL_FLAG_EXPECTING_PAREN |
249 SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR |
250 SDDL_FLAG_IS_UNARY_OP),
251 SDDL_PRECEDENCE_NOT,
254 [CONDITIONAL_ACE_TOKEN_EXISTS] = {
255 "Exists",
256 SDDL_FLAGS_EXISTS_OP,
257 SDDL_PRECEDENCE_EXISTS,
260 [CONDITIONAL_ACE_TOKEN_NOT_EXISTS] = {
261 "Not_Exists",
262 SDDL_FLAGS_EXISTS_OP,
263 SDDL_PRECEDENCE_EXISTS,
266 /* pseudo-operator pseudo-tokens */
267 [CONDITIONAL_ACE_SAMBA_SDDL_PAREN] = {
268 "(",
270 SDDL_PRECEDENCE_PAREN_START,
273 [CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END] = {
274 ")",
275 SDDL_FLAGS_PAREN_END,
276 SDDL_PRECEDENCE_PAREN_END,
281 * non-operators.
282 * The names here are only used for error messages.
284 * some of them will never actually be encountered (e.g. 8-bit
285 * integers).
287 [CONDITIONAL_ACE_TOKEN_INT8] = {
288 .name = "8-bit integer",
289 .flags = SDDL_FLAGS_LITERAL,
290 SDDL_NOT_AN_OP,
293 [CONDITIONAL_ACE_TOKEN_INT16] = {
294 "16-bit integer",
295 SDDL_FLAGS_LITERAL,
296 SDDL_NOT_AN_OP,
299 [CONDITIONAL_ACE_TOKEN_INT32] = {
300 "32-bit integer",
301 SDDL_FLAGS_LITERAL,
302 SDDL_NOT_AN_OP,
305 [CONDITIONAL_ACE_TOKEN_INT64] = {
306 "64-bit integer",
307 SDDL_FLAGS_LITERAL,
308 SDDL_NOT_AN_OP,
312 [CONDITIONAL_ACE_TOKEN_UNICODE] = {
313 "unicode",
314 SDDL_FLAGS_LITERAL,
315 SDDL_NOT_AN_OP,
318 [CONDITIONAL_ACE_TOKEN_OCTET_STRING] = {
319 "byte string",
320 SDDL_FLAGS_LITERAL,
321 SDDL_NOT_AN_OP,
324 [CONDITIONAL_ACE_TOKEN_COMPOSITE] = {
325 "composite list",
326 SDDL_FLAGS_LITERAL,
327 SDDL_NOT_AN_OP,
330 [CONDITIONAL_ACE_TOKEN_SID] = {
331 "SID",
332 SDDL_FLAGS_LITERAL,
333 SDDL_NOT_AN_OP,
336 [CONDITIONAL_ACE_LOCAL_ATTRIBUTE] = {
337 "local attribute",
338 SDDL_FLAGS_ATTRIBUTE,
339 SDDL_NOT_AN_OP,
342 [CONDITIONAL_ACE_USER_ATTRIBUTE] = {
343 "user attribute",
344 SDDL_FLAGS_ATTRIBUTE,
345 SDDL_NOT_AN_OP,
348 [CONDITIONAL_ACE_RESOURCE_ATTRIBUTE] = {
349 "resource attribute",
350 SDDL_FLAGS_ATTRIBUTE,
351 SDDL_NOT_AN_OP,
354 [CONDITIONAL_ACE_DEVICE_ATTRIBUTE] = {
355 "device attribute",
356 SDDL_FLAGS_ATTRIBUTE|SDDL_FLAG_DEVICE,
357 SDDL_NOT_AN_OP,
360 [CONDITIONAL_ACE_SAMBA_RESULT_BOOL] = {
361 "boolean result",
363 SDDL_NOT_AN_OP,
366 [CONDITIONAL_ACE_SAMBA_RESULT_NULL] = {
367 "null result",
369 SDDL_NOT_AN_OP,
372 [CONDITIONAL_ACE_SAMBA_RESULT_ERROR] = {
373 "error result",
375 SDDL_NOT_AN_OP,
380 struct sddl_attr_type{
381 const char *name;
382 uint8_t code;
386 * These are the prefixes for non-local attribute types. [MS-DTYP]
387 * styles them in title case ("@User."), but Windows itself seems to
388 * prefer all-caps, so that is how we render them.
390 static const struct sddl_attr_type sddl_attr_types[] = {
391 {"USER.", CONDITIONAL_ACE_USER_ATTRIBUTE},
392 {"RESOURCE.", CONDITIONAL_ACE_RESOURCE_ATTRIBUTE},
393 {"DEVICE.", CONDITIONAL_ACE_DEVICE_ATTRIBUTE},
397 struct sddl_write_context {
398 TALLOC_CTX *mem_ctx;
399 char *sddl;
400 size_t len;
401 size_t alloc_len;
404 static bool sddl_write(struct sddl_write_context *ctx,
405 const char *s)
407 size_t len = strlen(s);
408 if (ctx->alloc_len - ctx->len <= len ||
409 ctx->sddl == NULL) {
410 size_t old = ctx->alloc_len;
411 ctx->alloc_len = old + MAX(old / 2, len + 50);
412 if (ctx->alloc_len <= old ||
413 ctx->alloc_len - ctx->len <= len) {
414 return false;
416 ctx->sddl = talloc_realloc(ctx->mem_ctx, ctx->sddl,
417 char, ctx->alloc_len);
419 if (ctx->sddl == NULL) {
420 return false;
423 memcpy(ctx->sddl + ctx->len, s, len);
424 ctx->len += len;
425 ctx->sddl[ctx->len] = 0;
426 return true;
430 * This is a helper function to create a representation of a
431 * conditional ACE. This is not SDDL, more like a disassembly,
432 * but it uses some of the same tables.
434 char *debug_conditional_ace(TALLOC_CTX *mem_ctx,
435 struct ace_condition_script *program)
437 size_t i;
438 size_t depth = 0;
439 char stack[] = " ";
440 char line[120];
441 struct sddl_write_context ctx = {
442 .mem_ctx = mem_ctx
445 for (i = 0; i < program->length; i++) {
446 struct ace_condition_token *tok = &program->tokens[i];
447 struct sddl_data s = sddl_strings[tok->type];
448 char *utf8 = NULL;
449 int utf8_len;
450 char type;
451 char nom[40];
452 snprintf(nom, sizeof(nom), "\033[1;33m%20s\033[0m", s.name);
453 switch (tok->type) {
454 case CONDITIONAL_ACE_TOKEN_INT8:
455 case CONDITIONAL_ACE_TOKEN_INT16:
456 case CONDITIONAL_ACE_TOKEN_INT32:
457 case CONDITIONAL_ACE_TOKEN_INT64:
458 if (tok->data.int64.sign > 3 ||
459 tok->data.int64.base > 3) {
460 goto error;
462 snprintf(line, sizeof(line),
463 "%s %"PRIi64" %c%c\n",
464 nom,
465 tok->data.int64.value,
466 "?+-_"[tok->data.int64.sign],
467 "?odh"[tok->data.int64.base]
469 type = 'i';
470 break;
472 case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
473 case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
474 case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
475 case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
476 case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
477 case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
478 case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
479 case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
480 snprintf(line, sizeof(line),
481 "%s bool\n",
484 type = 'b';
485 break;
487 case CONDITIONAL_ACE_TOKEN_EQUAL:
488 case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
489 case CONDITIONAL_ACE_TOKEN_LESS_THAN:
490 case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
491 case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
492 case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
493 case CONDITIONAL_ACE_TOKEN_CONTAINS:
494 case CONDITIONAL_ACE_TOKEN_ANY_OF:
495 case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
496 case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
497 case CONDITIONAL_ACE_TOKEN_AND:
498 case CONDITIONAL_ACE_TOKEN_OR:
499 snprintf(line, sizeof(line),
500 "%s bool\n",
503 type = 'b';
504 break;
506 case CONDITIONAL_ACE_TOKEN_EXISTS:
507 case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
508 case CONDITIONAL_ACE_TOKEN_NOT:
509 snprintf(line, sizeof(line),
510 "%s bool\n",
513 type = 'b';
514 break;
516 case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
517 case CONDITIONAL_ACE_USER_ATTRIBUTE:
518 case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
519 case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
520 snprintf(line, sizeof(line),
521 "%s.%s (any type)\n",
522 nom,
523 tok->data.unicode.value
525 type = '?';
526 break;
528 case CONDITIONAL_ACE_TOKEN_UNICODE:
529 snprintf(line, sizeof(line),
530 "%s.%s (any type)\n",
531 nom,
532 tok->data.unicode.value
534 type = 'u';
535 break;
537 case CONDITIONAL_ACE_TOKEN_OCTET_STRING: {
538 char hex[21];
539 utf8_len = MIN(tok->data.bytes.length, 9);
540 hex_encode_buf(hex, tok->data.bytes.data, utf8_len);
542 snprintf(line, sizeof(line),
543 "%s %.*s (%d)\n",
544 nom, utf8_len * 2, hex, utf8_len);
545 type = 'o';
546 break;
548 case CONDITIONAL_ACE_TOKEN_SID:
549 utf8 = sddl_encode_sid(mem_ctx,
550 &tok->data.sid.sid,
551 NULL);
552 snprintf(line, sizeof(line),
553 "%s (%s)\n",
554 nom, utf8);
555 type = 'S';
556 break;
557 case CONDITIONAL_ACE_TOKEN_COMPOSITE:
558 snprintf(line, sizeof(line),
559 "%s %"PRIu32" direct members\n",
560 nom, tok->data.composite.n_members);
561 type = 'C';
562 break;
564 case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING:
565 snprintf(line, sizeof(line),
566 "%s\n", nom);
567 type = '0';
568 break;
569 default:
570 snprintf(line, sizeof(line),
571 "unknown opcode %#02x\n", tok->type);
572 type = '!';
573 break;
576 if (s.nargs > depth) {
577 snprintf(nom, sizeof(nom),
578 "UNDER: -%zu", s.nargs - depth);
579 depth = 0;
580 sddl_write(&ctx, nom);
581 } else if (depth >= strlen(stack)) {
582 snprintf(nom, sizeof(nom),
583 "depth %zu", s.nargs - depth);
584 depth -= (s.nargs - 1);
585 sddl_write(&ctx, nom);
586 } else {
587 depth -= s.nargs;
588 stack[depth] = type;
589 depth++;
590 if (depth < strlen(stack)) {
591 stack[depth] = ' ';
593 sddl_write(&ctx, stack);
595 sddl_write(&ctx, line);
597 if (depth == 1 && stack[0] == 'b') {
598 snprintf(line, sizeof(line),
599 "\033[1;32mGOOD: finishes on a single bool\033[0m\n");
600 } else {
601 snprintf(line, sizeof(line),
602 "\033[1;31mBAD: should finish with a bool\033[0m\n");
604 sddl_write(&ctx, line);
605 return ctx.sddl;
607 error:
608 TALLOC_FREE(ctx.sddl);
609 return NULL;
613 struct sddl_node {
614 struct ace_condition_token *tok;
615 struct sddl_node *lhs;
616 struct sddl_node *rhs;
617 bool wants_parens;
620 static bool sddl_write_int(struct sddl_write_context *ctx,
621 const struct ace_condition_token *tok)
623 int64_t v = tok->data.int64.value;
624 uint8_t sign = tok->data.int64.sign;
625 uint8_t base = tok->data.int64.base;
626 char buf[26]; /* oct(1<<63) + sign + \0 */
627 char sign_char;
628 if (sign > CONDITIONAL_ACE_INT_SIGN_NONE ||
629 base > CONDITIONAL_ACE_INT_BASE_16) {
630 return false;
634 * we have 9 combinations of base/sign (+ some invalid combinations of
635 * actual sign vs claimed sign).
637 if (sign == CONDITIONAL_ACE_INT_SIGN_NONE) {
638 /* octal and hex will end up unsigned! */
639 if (base == CONDITIONAL_ACE_INT_BASE_8) {
640 snprintf(buf, sizeof(buf), "0%"PRIo64, v);
641 } else if (base == CONDITIONAL_ACE_INT_BASE_10) {
642 snprintf(buf, sizeof(buf), "%"PRId64, v);
643 } else {
644 snprintf(buf, sizeof(buf), "0x%"PRIx64, v);
646 return sddl_write(ctx, buf);
648 if (sign == CONDITIONAL_ACE_INT_SIGN_POSITIVE && v < 0) {
649 return false;
651 if (sign == CONDITIONAL_ACE_INT_SIGN_NEGATIVE && v > 0) {
652 /* note we allow "-0", because we will parse it. */
653 return false;
655 sign_char = (sign == CONDITIONAL_ACE_INT_SIGN_NEGATIVE) ? '-' : '+';
657 * We can use "%+ld" for the decimal sign (except -0), but
658 * "%+lx" and "%+lo" are invalid because %o and %x are
659 * unsigned.
661 if (base == CONDITIONAL_ACE_INT_BASE_10) {
662 if (v == 0) {
663 snprintf(buf, sizeof(buf), "%c0", sign_char);
664 } else {
665 snprintf(buf, sizeof(buf), "%+"PRId64, v);
667 return sddl_write(ctx, buf);
670 if (v == INT64_MIN) {
672 * llabs(INT64_MIN) will be undefined.
673 * The lengths we must go to to round trip!
675 if (base == CONDITIONAL_ACE_INT_BASE_8) {
676 return sddl_write(ctx, "-01000000000000000000000");
678 return sddl_write(ctx, "-0x8000000000000000");
681 if (base == CONDITIONAL_ACE_INT_BASE_8) {
682 snprintf(buf, sizeof(buf), "%c0%llo", sign_char, llabs(v));
683 } else {
684 snprintf(buf, sizeof(buf), "%c0x%llx", sign_char, llabs(v));
686 return sddl_write(ctx, buf);
690 static bool sddl_should_escape_utf16(uint16_t c)
692 if (c <= ' ' || c > 126) {
693 return true;
696 switch (c) {
697 case '!':
698 case '"':
699 case '&':
700 case '(':
701 case ')':
702 case '<':
703 case '=':
704 case '>':
705 case '|':
706 case '%':
707 return true;
710 return false;
713 static bool sddl_encode_attr_name(TALLOC_CTX *mem_ctx,
714 const char *src,
715 char **dest,
716 size_t *dest_len)
718 size_t i, j;
719 bool ok;
720 uint16_t *utf16 = NULL;
721 char *escaped = NULL;
722 size_t utf16_byte_len;
723 size_t utf16_len;
724 size_t src_len = strlen(src);
725 size_t escapees;
726 size_t required;
727 *dest = NULL;
730 * Writing the string escapes can only really happen in
731 * utf-16.
733 ok = convert_string_talloc(mem_ctx,
734 CH_UTF8, CH_UTF16LE,
735 src, src_len,
736 &utf16, &utf16_byte_len);
737 if (!ok) {
738 return false;
740 utf16_len = utf16_byte_len / 2;
742 escapees = 0;
743 for (i = 0; i < utf16_len; i++) {
744 uint16_t c = utf16[i];
745 if (sddl_should_escape_utf16(c)) {
746 escapees++;
748 if (c == 0) {
749 /* we can't have '\0' (or "%0000") in a name. */
750 TALLOC_FREE(utf16);
751 return false;
755 required = src_len + escapees * 5;
756 escaped = talloc_size(mem_ctx, required + 1);
757 if (escaped == NULL) {
758 TALLOC_FREE(utf16);
759 return false;
762 if (escapees == 0) {
763 /* there is nothing to escape: the original string is fine */
764 memcpy(escaped, src, src_len);
765 escaped[src_len] = '\0';
766 *dest = escaped;
767 *dest_len = src_len;
768 TALLOC_FREE(utf16);
769 return true;
772 for (i = 0, j = 0; i < utf16_len && j < required; i++) {
773 uint16_t c = utf16[i];
774 if (sddl_should_escape_utf16(c)) {
775 if (j + 5 >= required) {
776 TALLOC_FREE(escaped);
777 TALLOC_FREE(utf16);
778 return false;
780 snprintf(escaped + j, 6, "%%%04x", c);
781 j += 5;
782 } else {
783 escaped[j] = c;
784 j++;
787 escaped[j] = '\0';
789 *dest = escaped;
790 *dest_len = j;
792 TALLOC_FREE(utf16);
793 return true;
796 static bool sddl_write_attr(struct sddl_write_context *ctx,
797 struct ace_condition_token *tok)
799 char *name = NULL;
800 size_t name_len;
801 size_t i;
802 bool ok = sddl_encode_attr_name(ctx->mem_ctx,
803 tok->data.local_attr.value,
804 &name, &name_len);
805 if (!ok) {
806 return false;
808 for (i = 0; i < ARRAY_SIZE(sddl_attr_types); i++) {
809 struct sddl_attr_type x = sddl_attr_types[i];
810 if (x.code == tok->type) {
811 ok = sddl_write(ctx, "@");
812 if (! ok) {
813 return false;
815 ok = sddl_write(ctx, x.name);
816 if (! ok) {
817 return false;
819 break;
823 ok = sddl_write(ctx, name);
824 talloc_free(name);
825 return ok;
829 static bool sddl_write_unicode(struct sddl_write_context *ctx,
830 const struct ace_condition_token *tok)
832 char *quoted = NULL;
833 bool ok;
835 * We rely on tok->data.unicode.value being
836 * nul-terminated.
838 if (strchr(tok->data.unicode.value, '"') != NULL) {
840 * There is a double quote in this string, but SDDL
841 * has no mechanism for escaping these (or anything
842 * else) in unicode strings.
844 * The only thing to do is fail.
846 * This cannot happen with an ACE created from SDDL,
847 * because the same no-escapes rule applies on the way
848 * in.
850 return false;
853 quoted = talloc_asprintf(ctx->mem_ctx, "\"%s\"",
854 tok->data.unicode.value);
855 if (quoted == NULL) {
856 return false;
858 ok = sddl_write(ctx, quoted);
859 TALLOC_FREE(quoted);
860 return ok;
863 static bool sddl_write_octet_string(struct sddl_write_context *ctx,
864 const struct ace_condition_token *tok)
866 bool ok;
867 char *hex = hex_encode_talloc(ctx->mem_ctx,
868 tok->data.bytes.data,
869 tok->data.bytes.length);
870 ok = sddl_write(ctx, "#");
871 if (!ok) {
872 return false;
874 ok = sddl_write(ctx, hex);
875 talloc_free(hex);
876 return ok;
880 * For octet strings, the Resource attribute ACE SDDL differs from conditional
881 * ACE SDDL, lacking the leading '#'.
883 static bool sddl_write_ra_octet_string(struct sddl_write_context *ctx,
884 const struct ace_condition_token *tok)
886 bool ok;
887 char *hex = hex_encode_talloc(ctx->mem_ctx,
888 tok->data.bytes.data,
889 tok->data.bytes.length);
890 ok = sddl_write(ctx, hex);
891 talloc_free(hex);
892 return ok;
896 static bool sddl_write_sid(struct sddl_write_context *ctx,
897 const struct ace_condition_token *tok)
899 bool ok;
900 char *sddl = NULL;
901 char *sid = sddl_encode_sid(ctx->mem_ctx,
902 &tok->data.sid.sid,
903 NULL);
904 if (sid == NULL) {
905 return false;
907 sddl = talloc_asprintf(ctx->mem_ctx, "SID(%s)", sid);
908 if (sddl == NULL) {
909 talloc_free(sid);
910 return false;
912 ok = sddl_write(ctx, sddl);
913 talloc_free(sid);
914 talloc_free(sddl);
915 return ok;
918 static bool sddl_write_composite(struct sddl_write_context *ctx,
919 struct ace_condition_token *tok)
922 * Looks like {1, 2, 3, "four", {"woah, nesting", {6}}, SID(BA)}.
924 struct ace_condition_composite *c = &tok->data.composite;
925 uint32_t i;
926 bool ok;
927 ok = sddl_write(ctx, "{");
928 if (!ok) {
929 return false;
931 for (i = 0; i < c->n_members; i++) {
932 struct ace_condition_token *t = &c->tokens[i];
933 if (i > 0) {
934 ok = sddl_write(ctx, ", ");
935 if (!ok) {
936 return false;
939 switch (t->type) {
940 case CONDITIONAL_ACE_TOKEN_INT8:
941 case CONDITIONAL_ACE_TOKEN_INT16:
942 case CONDITIONAL_ACE_TOKEN_INT32:
943 case CONDITIONAL_ACE_TOKEN_INT64:
944 ok = sddl_write_int(ctx, t);
945 break;
946 case CONDITIONAL_ACE_TOKEN_UNICODE:
947 ok = sddl_write_unicode(ctx, t);
948 break;
949 case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
950 ok = sddl_write_octet_string(ctx, t);
951 break;
952 case CONDITIONAL_ACE_TOKEN_SID:
953 ok = sddl_write_sid(ctx, t);
954 break;
955 case CONDITIONAL_ACE_TOKEN_COMPOSITE:
956 return false;
957 default:
958 return false;
960 if (!ok) {
961 return false;
964 ok = sddl_write(ctx, "}");
965 return ok;
968 static bool sddl_write_node(struct sddl_write_context *ctx,
969 struct sddl_node *node)
971 struct ace_condition_token *tok = node->tok;
972 switch (tok->type) {
973 case CONDITIONAL_ACE_TOKEN_INT8:
974 case CONDITIONAL_ACE_TOKEN_INT16:
975 case CONDITIONAL_ACE_TOKEN_INT32:
976 case CONDITIONAL_ACE_TOKEN_INT64:
977 return sddl_write_int(ctx, tok);
979 case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
980 case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
981 case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
982 case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
983 case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
984 case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
985 case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
986 case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
987 case CONDITIONAL_ACE_TOKEN_EQUAL:
988 case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
989 case CONDITIONAL_ACE_TOKEN_LESS_THAN:
990 case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
991 case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
992 case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
993 case CONDITIONAL_ACE_TOKEN_CONTAINS:
994 case CONDITIONAL_ACE_TOKEN_ANY_OF:
995 case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
996 case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
997 case CONDITIONAL_ACE_TOKEN_AND:
998 case CONDITIONAL_ACE_TOKEN_OR:
999 case CONDITIONAL_ACE_TOKEN_EXISTS:
1000 case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
1001 case CONDITIONAL_ACE_TOKEN_NOT:
1002 return sddl_write(ctx, sddl_strings[tok->type].name);
1004 case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
1005 case CONDITIONAL_ACE_USER_ATTRIBUTE:
1006 case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
1007 case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
1008 return sddl_write_attr(ctx, tok);
1010 case CONDITIONAL_ACE_TOKEN_UNICODE:
1011 return sddl_write_unicode(ctx, tok);
1013 case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
1014 return sddl_write_octet_string(ctx, tok);
1016 case CONDITIONAL_ACE_TOKEN_SID:
1017 return sddl_write_sid(ctx, tok);
1019 case CONDITIONAL_ACE_TOKEN_COMPOSITE:
1020 return sddl_write_composite(ctx, tok);
1022 case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING:
1024 * This is only expected at the very end, which we
1025 * can't (and don't need to) check here, but we can at
1026 * least ensure it's the end of a sub-expression.
1028 return (node->rhs == NULL);
1029 default:
1030 return false;
1032 /* not expecting to get here */
1033 return false;
1037 static inline bool sddl_wants_outer_parens(struct sddl_node *node)
1040 * Binary ops (having a LHS) are always parenthesised "(a == 2)"
1042 * Member-of ops are too, for some reason.
1044 return (node->lhs != NULL ||
1045 node->tok->type == CONDITIONAL_ACE_TOKEN_MEMBER_OF ||
1046 node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF ||
1047 node->tok->type == CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY ||
1048 node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY ||
1049 node->tok->type == CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF ||
1050 node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF ||
1051 node->tok->type == CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY ||
1052 node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY);
1056 static inline bool sddl_wants_inner_parens(struct sddl_node *node,
1057 struct sddl_node *child)
1060 * logical operators are serialised with parentheses around their
1061 * arguments (for NOT it is obligatory).
1063 if (node->tok->type != CONDITIONAL_ACE_TOKEN_NOT &&
1064 node->tok->type != CONDITIONAL_ACE_TOKEN_AND &&
1065 node->tok->type != CONDITIONAL_ACE_TOKEN_OR) {
1066 return false;
1068 if (sddl_wants_outer_parens(child)) {
1069 return false;
1071 return true;
1075 static void sddl_tree_resolve_parens(struct sddl_node *node)
1077 if (sddl_wants_outer_parens(node)) {
1078 node->wants_parens = true;
1080 if (node->lhs != NULL) {
1081 bool p = sddl_wants_inner_parens(node, node->lhs);
1082 node->lhs->wants_parens = p;
1083 sddl_tree_resolve_parens(node->lhs);
1085 if (node->rhs != NULL) {
1086 bool p = sddl_wants_inner_parens(node, node->rhs);
1087 node->rhs->wants_parens = p;
1088 sddl_tree_resolve_parens(node->rhs);
1092 static bool sddl_tree_to_sddl(struct sddl_write_context *ctx,
1093 struct sddl_node *node)
1095 bool ok;
1096 if (node->wants_parens) {
1097 ok = sddl_write(ctx, "(");
1098 if (! ok) {
1099 return false;
1103 if (node->lhs != NULL) {
1104 ok = sddl_tree_to_sddl(ctx, node->lhs);
1105 if (! ok) {
1106 return false;
1108 ok = sddl_write(ctx, " ");
1109 if (!ok) {
1110 return false;
1114 ok = sddl_write_node(ctx, node);
1115 if (!ok) {
1116 return false;
1118 if (node->rhs != NULL) {
1119 /* NOT is a special case: "!(x)", not "! (x)" */
1120 if (node->tok->type != CONDITIONAL_ACE_TOKEN_NOT) {
1121 ok = sddl_write(ctx, " ");
1122 if (!ok) {
1123 return false;
1127 ok = sddl_tree_to_sddl(ctx, node->rhs);
1128 if (! ok) {
1129 return false;
1132 if (node->wants_parens) {
1133 ok = sddl_write(ctx, ")");
1134 if (!ok) {
1135 return false;
1138 return true;
1142 * Convert conditional ACE conditions into SDDL conditions.
1144 * @param mem_ctx
1145 * @param program
1146 * @return a string or NULL on error.
1148 char *sddl_from_conditional_ace(TALLOC_CTX *mem_ctx,
1149 struct ace_condition_script *program)
1151 size_t i;
1152 char *sddl = NULL;
1153 struct sddl_node *nodes = NULL;
1154 struct sddl_node **trees = NULL;
1155 size_t n_trees = 0;
1156 struct ace_condition_token *tok = NULL;
1157 struct sddl_data s;
1158 bool ok;
1159 struct sddl_write_context ctx = {
1160 .mem_ctx = mem_ctx
1163 if (program->length == 0) {
1165 * The empty program is a special case.
1167 return talloc_strdup(mem_ctx, "()");
1169 nodes = talloc_zero_array(mem_ctx,
1170 struct sddl_node,
1171 program->length);
1172 if (nodes == NULL) {
1173 talloc_free(sddl);
1174 return NULL;
1176 trees = talloc_array(mem_ctx,
1177 struct sddl_node*,
1178 program->length);
1179 if (trees == NULL) {
1180 talloc_free(sddl);
1181 talloc_free(nodes);
1182 return NULL;
1186 * This loop constructs a tree, which we then traverse to get the
1187 * SDDL. Consider this transformation:
1189 * {A, B, ==, C, D, ==, &&} => "((A == B) && (C == D))"
1191 * We keep an array of sub-trees, and add to it in sequence. When the
1192 * thing we're adding takes arguments, we pop those off the tree list.
1193 * So it would go through this sequence:
1195 * len items
1196 * 1: A
1197 * 2: A, B
1198 * 1: ==(A, B)
1199 * 2: ==(A, B), C
1200 * 3: ==(A, B), C, D
1201 * 2: ==(A, B), ==(C, D)
1202 * 1 &&(==(A, B), ==(C, D))
1204 * Without building a tree it would be difficult to know how many
1205 * parentheses to put before A.
1207 * (A == B == C) should become
1208 * {A B == C ==} which should be the same as
1209 * ((A == B) == C)
1212 for (i = 0; i < program->length; i++) {
1213 tok = &program->tokens[i];
1214 s = sddl_strings[tok->type];
1215 nodes[i].tok = tok;
1216 if (s.nargs > n_trees) {
1217 goto error;
1219 if (s.nargs >= 1) {
1221 * Read this note if you're trying to follow
1222 * [MS-DTYP]. MS-DTYP uses 'LHS' to describe the
1223 * operand of unary operators even though they are
1224 * always displayed on the right of the operator. It
1225 * makes everything much simpler to use rhs
1226 * instead.
1228 n_trees--;
1229 nodes[i].rhs = trees[n_trees];
1231 if (s.nargs == 2) {
1232 n_trees--;
1233 nodes[i].lhs = trees[n_trees];
1236 trees[n_trees] = &nodes[i];
1237 n_trees++;
1240 if (n_trees != 1) {
1241 goto error;
1245 * First we walk the tree to work out where to put parentheses (to
1246 * match the canonical Windows representation).
1248 * Doing it in the same traverse as the writing would be possible but
1249 * trickier to get right.
1251 sddl_tree_resolve_parens(trees[0]);
1252 trees[0]->wants_parens = true;
1255 * Clamber over the tree, writing the string.
1257 ok = sddl_tree_to_sddl(&ctx, trees[0]);
1259 if (! ok) {
1260 goto error;
1263 talloc_free(trees);
1264 talloc_free(nodes);
1265 return ctx.sddl;
1267 error:
1268 talloc_free(sddl);
1269 talloc_free(trees);
1270 talloc_free(nodes);
1271 return NULL;
1276 static void comp_error(struct ace_condition_sddl_compiler_context *comp,
1277 const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
1279 static void comp_error(struct ace_condition_sddl_compiler_context *comp,
1280 const char *fmt, ...)
1282 char *msg = NULL;
1283 va_list ap;
1284 va_start(ap, fmt);
1285 msg = talloc_vasprintf(comp->mem_ctx, fmt, ap);
1286 va_end(ap);
1287 if (msg == NULL) {
1288 goto fail;
1291 if (comp->message == NULL) {
1293 * Previously unset message; prepend the position.
1295 * This is the common case.
1297 comp->message_offset = comp->offset;
1298 comp->message = msg;
1299 return;
1302 * There's a message already so we'll try to append.
1303 * This is unlikely to happen.
1305 comp->message = talloc_asprintf(comp->mem_ctx,
1306 "%s AND THEN %s",
1307 comp->message,
1308 msg);
1309 TALLOC_FREE(msg);
1310 if (comp->message == NULL) {
1311 goto fail;
1313 DBG_NOTICE("%s\n", comp->message);
1314 return;
1315 fail:
1316 comp->message = talloc_strdup(comp->mem_ctx,
1317 "failed to set error message");
1318 DBG_WARNING("%s\n", comp->message);
1325 conditional-ace = "(" conditional-ace-type ";" [ace-flag-string] ";" ace-rights
1326 ";" [object- guid] ";" [inherit-object-guid] ";" sid-string ";" "(" cond-expr
1327 ")" ")"
1329 wspace = 1*(%x09-0D / %x20)
1331 literal-SID = "SID(" sid-string ")"
1333 term = [wspace] (memberof-op / exists-op / rel-op / contains-op / anyof-op /
1334 attr-name / rel- op2) [wspace]
1336 cond-expr = term / term [wspace] ("||" / "&&" ) [wspace] cond-expr / (["!"]
1337 [wspace] "(" cond-expr ")")
1339 memberof-op = ( "Member_of" / "Not_Member_of" / "Member_of_Any" /
1340 "Not_Member_of_Any" / "Device_Member_of" / "Device_Member_of_Any" /
1341 "Not_Device_Member_of" / "Not_Device_Member_of_Any" ) wspace sid-array
1343 exists-op = ( "Exists" / "Not_Exists") wspace attr-name
1345 rel-op = attr-name [wspace] ("<" / "<=" / ">" / ">=") [wspace] (attr-name2 /
1346 value) ; only scalars
1348 rel-op2 = attr-name [wspace] ("==" / "!=") [wspace] ( attr-name2 / value-array )
1349 ; scalar or list
1351 contains-op = attr-name wspace ("Contains" / "Not_Contains") wspace (attr-name2
1352 / value- array)
1354 anyof-op = attr-name wspace ("Any_of" / "Not_Any_of") wspace (attr-name2 /
1355 value-array)
1358 attr-name1 = attr-char1 *(attr-char1 / "@")
1360 attr-char1 = 1*(ALPHA / DIGIT / ":" / "." / "/" / "_")
1364 attr-name2 = ("@user." / "@device." / "@resource.") 1*attr-char2
1365 ; new prefixed name form
1366 attr-char2 = attr-char1 / lit-char
1367 attr-name = attr-name1 / attr-name2
1372 static inline bool is_wspace(uint8_t c)
1374 /* wspace := %x09-0D | %x20 */
1375 return (c == ' ' || c == '\x09' || c == '\x0A' ||
1376 c == '\x0B' || c == '\x0C' || c == '\x0D');
1379 static inline bool is_attr_char1(uint8_t c)
1382 * attr-char1 = 1*(ALPHA / DIGIT / ":" / "." / "/" / "_")
1383 * (ALPHA and DIGIT being ASCII only).
1385 * These are used for local attributes, which we don't really
1386 * expect to see in Samba AD.
1388 * One example is "WIN://SYSAPPID", which is used in conditional ACEs
1389 * that seem to relate to software installers; another is
1390 * "APPID://PATH", used by Windows Applocker.
1392 return (((c >= 'a') && (c <= 'z')) ||
1393 ((c >= 'A') && (c <= 'Z')) ||
1394 ((c >= '0') && (c <= '9')) ||
1395 c == ':' || c == '.' || c == '/' || c == '_');
1399 static ssize_t read_attr2_string(
1400 struct ace_condition_sddl_compiler_context *comp,
1401 struct ace_condition_unicode *dest)
1404 * our SDDL is utf-8, but we need to convert to utf-16 and
1405 * parse the escapes, then back to utf-8, because that's how
1406 * the claims will appear.
1408 * attr_char2 is used for attribute names that follow "@Class."
1409 * specifiers. They can consume 5 characters to specify a single code
1410 * unit, using "%1234" style escapes. Certain characters must be
1411 * encoded this way, while others must be literal values. Because the
1412 * %1234 refers to a utf-16 code unit, we really need to do the work
1413 * in that codespace.
1415 bool ok;
1416 uint16_t *utf16 = NULL;
1417 size_t utf16_byte_len;
1418 size_t utf16_chars;
1419 size_t utf8_len;
1420 size_t src_len;
1421 ssize_t i, j;
1422 ssize_t max_len = comp->length - comp->offset;
1423 const uint8_t *src = comp->sddl + comp->offset;
1425 for (i = 0; i < max_len; i++) {
1426 uint8_t c = src[i];
1428 * A double‐byte that must be escaped but isn't tells us that
1429 * the attribute name has ended.
1431 * The exception is '%', which must also be escaped
1432 * (as "%0025"), but is obviously still expected in
1433 * the escaped string.
1435 if (strchr("!&()><=| \"", c) != NULL || is_wspace(c)) {
1436 break;
1439 if (i == max_len) {
1440 /* too long, because we need at least one ')' */
1441 comp_error(comp, "interminable attribute name");
1442 return -1;
1444 if (i == 0) {
1445 /* too short! like "User.>= 4" */
1446 comp_error(comp, "empty attribute name");
1447 return -1;
1450 if (unlikely(i > CONDITIONAL_ACE_MAX_LENGTH)) {
1452 * This is imprecise; the limit for the whole ACL is 64k.
1453 * However there could be many escapes in the SDDL name which
1454 * would reduce down to single utf16 code units in the
1455 * compiled string.
1457 comp_error(comp, "attribute is way too long (%zu)", i);
1458 return -1;
1461 src_len = i;
1463 ok = convert_string_talloc(comp->mem_ctx,
1464 CH_UTF8, CH_UTF16LE,
1465 src, src_len,
1466 &utf16, &utf16_byte_len);
1467 if (!ok) {
1468 comp_error(comp, "could not convert to utf-16");
1469 return -1;
1472 * utf16_byte_len is in bytes, we want to count uint16s.
1474 utf16_chars = utf16_byte_len / 2;
1476 /* now the escapes. */
1477 for (i = 0, j = 0;
1478 j < utf16_chars && i < utf16_chars;
1479 j++) {
1480 uint16_t c = utf16[i];
1481 if (c == '%') {
1482 uint16_t v = 0;
1483 size_t end = i + 5;
1485 * we need to read 4 hex characters.
1486 * hex_byte() won't help because that is 8-bit.
1488 if (end > utf16_chars) {
1489 comp_error(comp,
1490 "insufficient room for %% escape");
1491 talloc_free(utf16);
1492 return -1;
1494 for (i++; i < end; i++) {
1495 v <<= 4;
1496 c = utf16[i];
1497 if (c >= '0' && c <= '9') {
1498 v += c - '0';
1499 } else if (c >= 'A' && c <= 'F') {
1500 v += c - 'A' + 10;
1501 } else if (c >= 'a' && c <= 'f') {
1502 v += c - 'a' + 10;
1503 } else {
1504 comp_error(comp, "invalid %% escape");
1505 talloc_free(utf16);
1506 return -1;
1510 * from MS-DTYP 2.5.1.1 Syntax (text, not ABNF), some
1511 * characters must be literals, not escaped.
1513 if ((v >= '0' && v <= '9') ||
1514 (v >= 'A' && v <= 'Z') ||
1515 (v >= 'a' && v <= 'z') ||
1516 (v < 127 &&
1517 strchr("#$'*+-;?@[\\]^_`{}~:/.", v) != NULL)) {
1518 comp_error(comp, "invalid %% escape: "
1519 "'%%%04x' should be literal '%c'",
1520 v, v);
1521 talloc_free(utf16);
1522 return -1;
1524 utf16[j] = v;
1525 continue;
1528 * Note the characters "!&()><=|% \"" must be escaped per
1529 * [MS-DTYP], but as we found the bounds of this string using
1530 * those in utf-8 at the top of this function, we are not
1531 * going to find them in the utf-16 now.
1533 * Also, per [MS-DTYP], un-escaped whitespace is allowed, but
1534 * effectively disallowed by Samba.
1536 utf16[j] = utf16[i];
1537 i++;
1540 ok = convert_string_talloc(comp->mem_ctx,
1541 CH_UTF16LE, CH_UTF8,
1542 utf16, j * 2,
1543 &dest->value, &utf8_len);
1544 TALLOC_FREE(utf16);
1545 if (!ok) {
1546 comp_error(comp, "could not convert to utf-16");
1547 return -1;
1550 /* returning bytes consumed, not necessarily the length of token */
1551 return src_len;
1556 static bool eat_whitespace(struct ace_condition_sddl_compiler_context *comp,
1557 bool trailing)
1560 * Advance the offset to the first non-whitespace character.
1562 * If trailing is false, there has to be something before the end of
1563 * the string.
1565 while (comp->offset < comp->length) {
1566 if (! is_wspace(comp->sddl[comp->offset])) {
1567 break;
1569 comp->offset++;
1571 if ((!trailing) && comp->offset == comp->length) {
1572 comp_error(comp, "input ends unexpectedly");
1573 return false;
1575 return true;
1578 static bool pop_sddl_token(struct ace_condition_sddl_compiler_context *comp,
1579 struct ace_condition_token *token);
1581 static bool write_sddl_token(struct ace_condition_sddl_compiler_context *comp,
1582 struct ace_condition_token token);
1584 static bool pop_write_sddl_token(
1585 struct ace_condition_sddl_compiler_context *comp);
1588 static bool flush_stack_tokens(struct ace_condition_sddl_compiler_context *comp,
1589 uint8_t type)
1591 bool ok;
1592 uint8_t precedence = sddl_strings[type].op_precedence;
1593 if (precedence == SDDL_PRECEDENCE_PAREN_START) {
1594 /* paren has a special role */
1595 return true;
1598 * Any operators on the top of the stack that have a "higher"
1599 * precedence (tighter binding) to this one get popped off and written
1600 * to the output. "higher" is in quotes because it means lower enum
1601 * value.
1603 * This works for binary operators, for example, with "(a == b == c)"
1604 * (which is equivalent to "((a == b) == c)" via the left-to-right
1605 * rule), we have:
1606 * TOKEN dest PROGRAM STACK
1608 * a p
1609 * == s a
1610 * b p a ==
1611 * == s a b ==
1612 * flush stack
1613 * s->p a b == ==
1614 * c p a b ==
1615 * ) a b == c ==
1616 * flush stack
1617 * a b == c ==
1619 * but it is not right for unary operators, as in "(!(!(Exists
1620 * a)))". As it turns out though, >= works for the unary
1621 * operators and syntactic rules we have.
1623 while (comp->stack_depth > 0) {
1624 struct ace_condition_token *op =
1625 &comp->stack[comp->stack_depth - 1];
1626 if(sddl_strings[op->type].op_precedence > precedence) {
1627 break;
1629 if(sddl_strings[op->type].op_precedence == precedence &&
1630 sddl_strings[op->type].flags & SDDL_FLAG_IS_UNARY_OP) {
1631 break;
1634 ok = pop_write_sddl_token(comp);
1635 if (! ok) {
1636 comp_error(comp,
1637 "could not flush '%s' to program",
1638 sddl_strings[op->type].name);
1639 return false;
1642 return true;
1645 static bool push_sddl_token(struct ace_condition_sddl_compiler_context *comp,
1646 struct ace_condition_token token)
1648 if (comp->stack_depth >= CONDITIONAL_ACE_MAX_TOKENS - 1) {
1649 comp_error(comp, "excessive recursion");
1650 return false;
1652 if (sddl_strings[token.type].op_precedence == SDDL_NOT_AN_OP) {
1653 comp_error(comp,
1654 "wrong kind of token for the SDDL stack: %s",
1655 sddl_strings[token.type].name);
1656 return false;
1659 * Any operators on the top of the stack that have a "greater" or
1660 * equal precedence to this one get popped off and written to the
1661 * output.
1663 flush_stack_tokens(comp, token.type);
1665 token.data.op.sddl_position = comp->offset;
1667 comp->stack[comp->stack_depth] = token;
1668 comp->stack_depth++;
1669 if (token.type != CONDITIONAL_ACE_SAMBA_SDDL_PAREN) {
1670 comp->last_token_type = token.type;
1672 return true;
1675 static bool pop_sddl_token(struct ace_condition_sddl_compiler_context *comp,
1676 struct ace_condition_token *token)
1678 if (comp->stack_depth == 0) {
1679 comp_error(comp, "misbalanced expression");
1680 return false;
1682 comp->stack_depth--;
1683 *token = comp->stack[comp->stack_depth];
1684 return true;
1688 static bool write_sddl_token(struct ace_condition_sddl_compiler_context *comp,
1689 struct ace_condition_token token)
1692 * This is adding a token to the program. Normally it will be to the
1693 * main program list, but if we are constructing a composite list, then
1694 * will be redirected there (via comp->target).
1696 * We also conservatively track the overall size, so we don't waste
1697 * time compiling something that is way too big.
1699 DBG_INFO("writing %"PRIu32" %x %s\n",
1700 *comp->target_len,
1701 token.type,
1702 sddl_strings[token.type].name);
1703 comp->approx_size++;
1704 if (comp->approx_size > CONDITIONAL_ACE_MAX_TOKENS) {
1705 comp_error(comp, "program is too long "
1706 "(over %d tokens)",
1707 CONDITIONAL_ACE_MAX_TOKENS);
1708 return false;
1710 if (token.type != CONDITIONAL_ACE_SAMBA_SDDL_PAREN) {
1711 comp->last_token_type = token.type;
1713 comp->target[*comp->target_len] = token;
1714 (*comp->target_len)++;
1715 return true;
1718 static bool pop_write_sddl_token(
1719 struct ace_condition_sddl_compiler_context *comp)
1721 bool ok;
1722 struct ace_condition_token token = {};
1723 ok = pop_sddl_token(comp, &token);
1724 if (!ok) {
1725 comp_error(comp, "could not pop from op stack");
1726 return false;
1728 if (comp->target != comp->program->tokens) {
1729 comp_error(comp, "compiler is seriously confused");
1730 return false;
1733 ok = write_sddl_token(comp, token);
1734 if (!ok) {
1735 comp_error(comp,
1736 "could not write '%s' to program",
1737 sddl_strings[token.type].name);
1738 return false;
1740 DBG_INFO(" written '%s'\n", sddl_strings[token.type].name);
1741 return true;
1746 static bool parse_expression(struct ace_condition_sddl_compiler_context *comp);
1747 static bool parse_composite(struct ace_condition_sddl_compiler_context *comp);
1752 static bool parse_oppy_op(struct ace_condition_sddl_compiler_context *comp)
1755 * These ones look like operators and are operators.
1757 bool ok;
1758 struct ace_condition_token token = {};
1759 uint8_t c, d;
1760 uint32_t flag = SDDL_FLAG_EXPECTING_BINARY_OP;
1762 if (comp->offset + 1 >= comp->length) {
1763 comp_error(comp, "syntax error");
1764 return false;
1767 token.data.sddl_op.start = comp->offset;
1770 * These are all one or two characters long, and we always have room
1771 * to peek ahead.
1773 c = comp->sddl[comp->offset];
1774 d = comp->sddl[comp->offset + 1];
1776 if (c == '!') {
1777 if (d == '=') {
1778 comp->offset++;
1779 token.type = CONDITIONAL_ACE_TOKEN_NOT_EQUAL;
1781 } else {
1782 token.type = CONDITIONAL_ACE_TOKEN_NOT;
1783 flag = SDDL_FLAG_EXPECTING_UNARY_OP;
1785 } else if (c == '=' && d == '=') {
1786 comp->offset++;
1787 token.type = CONDITIONAL_ACE_TOKEN_EQUAL;
1788 } else if (c == '>') {
1789 if (d == '=') {
1790 comp->offset++;
1791 token.type = CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL;
1793 } else {
1794 token.type = CONDITIONAL_ACE_TOKEN_GREATER_THAN;
1796 } else if (c == '<') {
1797 if (d == '=') {
1798 comp->offset++;
1799 token.type = CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL;
1801 } else {
1802 token.type = CONDITIONAL_ACE_TOKEN_LESS_THAN;
1804 } else if (c == '&' && d == '&') {
1805 comp->offset++;
1806 token.type = CONDITIONAL_ACE_TOKEN_AND;
1807 flag = SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP;
1808 } else if (c == '|' && d == '|') {
1809 comp->offset++;
1810 token.type = CONDITIONAL_ACE_TOKEN_OR;
1811 flag = SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP;
1812 } else {
1813 comp_error(comp, "unknown operator");
1814 return false;
1817 if ((comp->state & flag) == 0) {
1818 comp_error(comp, "unexpected operator");
1819 return false;
1822 comp->offset++;
1824 ok = push_sddl_token(comp, token);
1825 if (!ok) {
1826 return false;
1829 ok = eat_whitespace(comp, true);
1830 return ok;
1833 static bool parse_unicode(struct ace_condition_sddl_compiler_context *comp)
1836 * This looks like "hello" (including the double quotes).
1838 * Fortunately (for now), there is no mechanism for escaping
1839 * double quotes in conditional ace strings, so we can simply
1840 * look for the second quote without worrying about things
1841 * like «\\\"».
1843 struct ace_condition_token token = {};
1844 char *s = NULL;
1845 const uint8_t *src = NULL;
1846 char *utf16 = NULL;
1847 size_t len, max_len;
1848 bool ok;
1849 if (comp->sddl[comp->offset] != '"') {
1850 comp_error(comp, "was expecting '\"' for Unicode string");
1851 return false;
1853 comp->offset++;
1854 src = comp->sddl + comp->offset;
1855 max_len = comp->length - comp->offset;
1856 /* strnchr */
1857 for (len = 0; len < max_len; len++) {
1858 if (src[len] == '"') {
1859 break;
1862 if (len == max_len) {
1863 comp_error(comp, "unterminated unicode string");
1864 return false;
1868 * Look, this is wasteful, but it probably doesn't matter. We want to
1869 * check that the string we're putting into the descriptor is valid,
1870 * or we'll see errors down the track.
1872 ok = convert_string_talloc(comp->mem_ctx,
1873 CH_UTF8, CH_UTF16LE,
1874 src, len,
1875 &utf16, NULL);
1876 if (!ok) {
1877 comp_error(comp, "not valid unicode");
1878 return false;
1880 TALLOC_FREE(utf16);
1882 s = talloc_array_size(comp->mem_ctx, 1, len + 1);
1883 if (s == NULL) {
1884 comp_error(comp, "allocation error");
1885 return false;
1887 memcpy(s, src, len);
1888 s[len] = 0;
1889 comp->offset += len + 1; /* +1 for the final quote */
1890 token.type = CONDITIONAL_ACE_TOKEN_UNICODE;
1891 token.data.unicode.value = s;
1893 return write_sddl_token(comp, token);
1897 static bool parse_octet_string(struct ace_condition_sddl_compiler_context *comp)
1900 * This looks like '#hhhh...', where each 'hh' is hex for a byte, with
1901 * the weird and annoying complication that '#' can be used to mean
1902 * '0'.
1904 struct ace_condition_token token = {};
1905 size_t length, i;
1907 if (comp->sddl[comp->offset] != '#') {
1908 comp_error(comp, "was expecting '#' for octet string");
1909 return false;
1911 comp->offset++;
1912 length = strspn((const char*)(comp->sddl + comp->offset),
1913 "#0123456789abcdefABCDEF");
1915 if (length & 1) {
1916 comp_error(comp, "octet string has odd number of hex digits");
1917 return false;
1920 length /= 2;
1922 token.data.bytes = data_blob_talloc_zero(comp->mem_ctx, length);
1923 token.type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
1925 for (i = 0; i < length; i++) {
1927 * Why not just strhex_to_str()?
1929 * Because we need to treat '#' as '0' in octet string values,
1930 * so all of the following are the same
1931 * (equaling {0x10, 0x20, 0x30, 0x0}).
1933 * #10203000
1934 * #10203###
1935 * #1#2#3###
1936 * #10203#00
1938 bool ok;
1939 char pair[2];
1940 size_t j = comp->offset + i * 2;
1941 pair[0] = (comp->sddl[j] == '#') ? '0' : comp->sddl[j];
1942 pair[1] = (comp->sddl[j + 1] == '#') ? '0' : comp->sddl[j + 1];
1944 ok = hex_byte(pair, &token.data.bytes.data[i]);
1945 if (!ok) {
1946 talloc_free(token.data.bytes.data);
1947 comp_error(comp, "inexplicable error in octet string");
1948 return false;
1951 comp->offset += length * 2;
1952 return write_sddl_token(comp, token);
1956 static bool parse_ra_octet_string(struct ace_condition_sddl_compiler_context *comp)
1959 * Resource attribute octet strings resemble conditional ace octet
1960 * strings, but have some important differences:
1962 * 1. The '#' at the start is optional, and if present is
1963 * counted as a zero.
1965 * 2. An odd number of characters is implicitly left-padded with a zero.
1967 * That is, "abc" means "0abc", "#12" means "0012", "f##"
1968 * means "0f00", and "##" means 00.
1970 struct ace_condition_token token = {};
1971 size_t string_length, bytes_length, i, j;
1972 bool ok;
1973 char pair[2];
1975 string_length = strspn((const char*)(comp->sddl + comp->offset),
1976 "#0123456789abcdefABCDEF");
1978 bytes_length = (string_length + 1) / 2;
1980 if (bytes_length == 0) {
1981 comp_error(comp, "zero length octet bytes");
1982 return false;
1985 token.data.bytes = data_blob_talloc_zero(comp->mem_ctx, bytes_length);
1986 if (token.data.bytes.data == NULL) {
1987 return false;
1989 token.type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
1991 j = comp->offset;
1992 i = 0;
1993 if (string_length & 1) {
1995 * An odd number of characters means the first
1996 * character gains an implicit 0 for the high nybble.
1998 pair[0] = 0;
1999 pair[1] = (comp->sddl[0] == '#') ? '0' : comp->sddl[0];
2001 ok = hex_byte(pair, &token.data.bytes.data[i]);
2002 if (!ok) {
2003 goto fail;
2005 j++;
2006 i++;
2009 for (; i < bytes_length; i++) {
2011 * Why not just strhex_to_str() ?
2013 * Because we need to treat '#' as '0' in octet string values.
2015 if (comp->length - j < 2) {
2016 goto fail;
2019 pair[0] = (comp->sddl[j] == '#') ? '0' : comp->sddl[j];
2020 pair[1] = (comp->sddl[j + 1] == '#') ? '0' : comp->sddl[j + 1];
2022 ok = hex_byte(pair, &token.data.bytes.data[i]);
2023 if (!ok) {
2024 goto fail;
2026 j += 2;
2028 comp->offset = j;
2029 return write_sddl_token(comp, token);
2031 fail:
2032 comp_error(comp, "inexplicable error in octet string");
2033 talloc_free(token.data.bytes.data);
2034 return false;
2038 static bool parse_sid(struct ace_condition_sddl_compiler_context *comp)
2040 struct dom_sid *sid = NULL;
2041 const uint8_t *sidstr = NULL;
2042 struct ace_condition_token token = {};
2043 size_t end;
2044 if (comp->length - comp->offset < 7) {
2045 /* minimum: "SID(AA)" */
2046 comp_error(comp, "no room for a complete SID");
2047 return false;
2049 /* conditional ACE SID string */
2050 if (comp->sddl[comp->offset ] != 'S' ||
2051 comp->sddl[comp->offset + 1] != 'I' ||
2052 comp->sddl[comp->offset + 2] != 'D' ||
2053 comp->sddl[comp->offset + 3] != '(') {
2054 comp_error(comp, "malformed SID() constructor");
2055 return false;
2056 } else {
2057 comp->offset += 4;
2060 sidstr = comp->sddl + comp->offset;
2062 sid = sddl_decode_sid(comp->mem_ctx,
2063 (const char **)&sidstr,
2064 comp->domain_sid);
2066 if (sid == NULL) {
2067 comp_error(comp, "could not parse SID");
2068 return false;
2070 end = sidstr - comp->sddl;
2071 if (end >= comp->length || end < comp->offset) {
2072 comp_error(comp, "apparent overflow in SID parsing");
2073 return false;
2075 comp->offset = end;
2077 * offset is now at the end of the SID, but we need to account
2078 * for the ')'.
2080 if (comp->sddl[comp->offset] != ')') {
2081 comp_error(comp, "expected ')' to follow SID");
2082 return false;
2084 comp->offset++;
2086 token.type = CONDITIONAL_ACE_TOKEN_SID;
2087 token.data.sid.sid = *sid;
2088 return write_sddl_token(comp, token);
2093 static bool parse_ra_sid(struct ace_condition_sddl_compiler_context *comp)
2095 struct dom_sid *sid = NULL;
2096 const uint8_t *sidstr = NULL;
2097 struct ace_condition_token token = {};
2098 size_t end;
2100 if ((comp->state & SDDL_FLAG_EXPECTING_LITERAL) == 0) {
2101 comp_error(comp, "did not expect a SID here");
2102 return false;
2105 * Here we are parsing a resource attribute ACE which doesn't
2106 * have the SID() wrapper around the SID string (unlike a
2107 * conditional ACE).
2109 * The resource ACE doesn't need this because there is no
2110 * ambiguity with local attribute names, besides which the
2111 * type has already been specified earlier in the ACE.
2113 if (comp->length - comp->offset < 2){
2114 comp_error(comp, "no room for a complete SID");
2115 return false;
2118 sidstr = comp->sddl + comp->offset;
2120 sid = sddl_decode_sid(comp->mem_ctx,
2121 (const char **)&sidstr,
2122 comp->domain_sid);
2124 if (sid == NULL) {
2125 comp_error(comp, "could not parse SID");
2126 return false;
2128 end = sidstr - comp->sddl;
2129 if (end >= comp->length || end < comp->offset) {
2130 comp_error(comp, "apparent overflow in SID parsing");
2131 return false;
2133 comp->offset = end;
2134 token.type = CONDITIONAL_ACE_TOKEN_SID;
2135 token.data.sid.sid = *sid;
2136 return write_sddl_token(comp, token);
2140 static bool parse_int(struct ace_condition_sddl_compiler_context *comp)
2143 * This one is relatively simple. strtoll() does the work.
2145 long long v;
2146 struct ace_condition_token token = {};
2147 const char *start = (const char *)comp->sddl + comp->offset;
2148 char *end = NULL;
2149 const char *first_digit = start;
2150 size_t len;
2151 errno = 0;
2152 v = strtoll(start, &end, 0);
2153 if (errno != 0) {
2154 comp_error(comp, "bad integer: %s", strerror(errno));
2155 return false;
2157 len = end - start;
2159 if (len == 0) {
2160 comp_error(comp, "unexpected non-integer");
2161 return false;
2163 if (comp->offset + len > comp->length) {
2164 comp_error(comp, "impossible integer length: %zu!", len);
2165 return false;
2168 comp->offset += len;
2171 * Record the base and sign, which are used for recreating the SDDL.
2173 * 'Sign' indicates whether there is a '+' or '-' sign. Base indicates
2174 * whether the number was in hex, octal, or decimal. These make no
2175 * difference to the evaluation of the ACE, just the display.
2177 * This would not work reliably if eat_whitespace() is not called
2178 * before parse_int(), but a) we know it is, and b) we don't *really*
2179 * care if we lose these display hints.
2181 if (*start == '-') {
2182 token.data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NEGATIVE;
2183 first_digit++;
2184 } else if (*start == '+') {
2185 token.data.int64.sign = CONDITIONAL_ACE_INT_SIGN_POSITIVE;
2186 first_digit++;
2187 } else {
2188 token.data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
2190 if (*first_digit == '0' && (end - first_digit) > 1) {
2191 if ((end - first_digit > 2) &&
2192 (first_digit[1] == 'x' ||
2193 first_digit[1] == 'X')) {
2194 token.data.int64.base = CONDITIONAL_ACE_INT_BASE_16;
2195 } else {
2196 token.data.int64.base = CONDITIONAL_ACE_INT_BASE_8;
2198 } else {
2199 token.data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
2202 token.data.int64.value = v;
2203 token.type = CONDITIONAL_ACE_TOKEN_INT64;
2204 return write_sddl_token(comp, token);
2208 static bool parse_uint(struct ace_condition_sddl_compiler_context *comp)
2210 struct ace_condition_token *tok = NULL;
2211 bool ok = parse_int(comp);
2212 if (ok == false) {
2213 return false;
2216 * check that the token's value is positive.
2218 if (comp->target_len == 0) {
2219 return false;
2221 tok = &comp->target[*comp->target_len - 1];
2222 if (tok->type != CONDITIONAL_ACE_TOKEN_INT64) {
2223 return false;
2225 if (tok->data.int64.value < 0) {
2226 comp_error(comp, "invalid resource ACE value for unsigned TU claim");
2227 return false;
2229 return true;
2233 static bool parse_bool(struct ace_condition_sddl_compiler_context *comp)
2235 struct ace_condition_token *tok = NULL;
2236 bool ok = parse_int(comp);
2237 if (ok == false || comp->target_len == 0) {
2238 return false;
2241 * check that the token is 0 or 1.
2243 tok = &comp->target[*comp->target_len - 1];
2244 if (tok->type != CONDITIONAL_ACE_TOKEN_INT64) {
2245 return false;
2247 if (tok->data.int64.value != 0 && tok->data.int64.value != 1) {
2248 comp_error(comp, "invalid resource ACE Boolean value");
2249 return false;
2251 return true;
2255 static bool could_be_an_int(struct ace_condition_sddl_compiler_context *comp)
2257 const char *start = (const char*)(comp->sddl + comp->offset);
2258 char* end = NULL;
2260 if ((comp->state & SDDL_FLAG_EXPECTING_LITERAL) == 0) {
2261 return false;
2264 errno = 0;
2266 * See, we don't care about the strtoll return value, only
2267 * whether it succeeds or not and what it finds at the end. If
2268 * it succeeds, parse_int() will do it again for the value.
2270 * Note that an out of range int will raise ERANGE (probably
2271 * 34), so it will be read as a local attribute.
2273 strtoll(start, &end, 0);
2274 if (errno != 0 ||
2275 end == start ||
2276 end >= (const char*)comp->sddl + comp->length) {
2277 return false;
2280 * We know *some* characters form an int, but if we run right
2281 * into other attr1 characters (basically, letters), we won't
2282 * count it as an int.
2284 * For example, the "17" in "17p" is not an int. The "17" in
2285 * "17||" is.
2287 if (is_attr_char1(*end)) {
2288 return false;
2290 return true;
2294 static bool parse_word(struct ace_condition_sddl_compiler_context *comp)
2297 * Sometimes a bare word must be a local attribute, while in other
2298 * cases it could also be a member-of or exists operator. Sometimes it
2299 * could actually be a SID, which we discover when we've read as far
2300 * as "SID(". Sometimes it might be a literal integer (attribute
2301 * names can also consist entirely of digits).
2303 * When it is an operator name, we have the complication that a match
2304 * does not necessarily end the token. Consider "Member_of_Any" which
2305 * contains the operator "Member_of". According to [MS-DTYP], a space
2306 * is not necessary between the operator and the next token, but it
2307 * does seem to be required for Windows 2022.
2309 * Also, "Member_of" et. al. *could* be valid local attributes, which
2310 * would make "(Member_of == 123)" a valid expression that we will
2311 * fail to parse. This is not much of an issue for Samba AD where
2312 * local attributes are not used.
2314 * Operators are matched case-insensitively.
2316 * There's another kind of attribute that starts with a '@', which we
2317 * deal with in parse_attr2(). Those ones have full unicode glory;
2318 * these ones are ASCII only.
2320 size_t i, j, k;
2321 bool ok;
2322 uint8_t candidates[8];
2323 size_t n_candidates = 0;
2324 struct ace_condition_token token = {};
2325 bool expecting_unary = comp->state & SDDL_FLAG_EXPECTING_UNARY_OP;
2326 bool expecting_binary = comp->state & SDDL_FLAG_EXPECTING_BINARY_OP;
2327 bool expecting_attr = comp->state & SDDL_FLAG_EXPECTING_LOCAL_ATTR;
2328 bool expecting_literal = comp->state & SDDL_FLAG_EXPECTING_LITERAL;
2329 const uint8_t *start = comp->sddl + comp->offset;
2330 uint8_t c = start[0];
2331 char *s = NULL;
2332 if (! is_attr_char1(*start)) {
2333 /* we shouldn't get here, because we peeked first */
2334 return false;
2338 * We'll look for a SID first, because it simplifies the rest.
2340 if (expecting_literal &&
2341 comp->offset + 4 < comp->length &&
2342 start[0] == 'S' &&
2343 start[1] == 'I' &&
2344 start[2] == 'D' &&
2345 start[3] == '(') {
2346 /* actually, we are parsing a SID. */
2347 return parse_sid(comp);
2350 if (expecting_binary || expecting_unary) {
2352 * Collect up the operators that can possibly be used
2353 * here, including only those that start with the
2354 * current letter and have the right arity/syntax.
2356 * We don't expect more than 5 (for 'N', beginning the
2357 * "Not_..." unary ops), and we'll winnow them down as
2358 * we progress through the word.
2360 int uc = toupper(c);
2361 for (i = 0; i < 256; i++) {
2362 const struct sddl_data *d = &sddl_strings[i];
2363 if (sddl_strings[i].op_precedence != SDDL_NOT_AN_OP &&
2364 uc == toupper((unsigned char)d->name[0])) {
2365 if (d->flags & SDDL_FLAG_IS_UNARY_OP) {
2366 if (!expecting_unary) {
2367 continue;
2369 } else if (!expecting_binary) {
2370 continue;
2372 candidates[n_candidates] = i;
2373 n_candidates++;
2374 if (n_candidates == ARRAY_SIZE(candidates)) {
2375 /* impossible, really. */
2376 return false;
2380 } else if (could_be_an_int(comp)) {
2382 * if looks like an integer, and we expect an integer, it is
2383 * an integer. If we don't expect an integer, it is a local
2384 * attribute with a STUPID NAME. Or an error.
2386 return parse_int(comp);
2387 } else if (! expecting_attr) {
2388 comp_error(comp, "did not expect this word here");
2389 return false;
2392 i = 1;
2393 while (comp->offset + i < comp->length) {
2394 c = start[i];
2395 if (! is_attr_char1(c)) {
2396 break;
2398 if (n_candidates != 0) {
2400 * Filter out candidate operators that no longer
2401 * match.
2403 int uc = toupper(c);
2404 k = 0;
2405 for (j = 0; j < n_candidates; j++) {
2406 size_t o = candidates[j];
2407 uint8_t c2 = sddl_strings[o].name[i];
2408 if (uc == toupper(c2)) {
2409 candidates[k] = candidates[j];
2410 k++;
2413 n_candidates = k;
2415 i++;
2419 * We have finished and there is a complete word. If it could be an
2420 * operator we'll assume it is one.
2422 * A complication is we could have matched more than one operator, for
2423 * example "Member_of" and "Member_of_Any", so we have to look through
2424 * the list of candidates for the one that ends.
2426 if (n_candidates != 0) {
2427 for (j = 0; j < n_candidates; j++) {
2428 size_t o = candidates[j];
2429 if (sddl_strings[o].name[i] == '\0') {
2430 /* it is this one */
2432 if (!comp->allow_device &&
2433 (sddl_strings[o].flags & SDDL_FLAG_DEVICE))
2435 comp_error(
2436 comp,
2437 "a device‐relative expression "
2438 "will never evaluate to true "
2439 "in this context (did you "
2440 "intend a user‐relative "
2441 "expression?)");
2442 return false;
2445 token.type = o;
2446 token.data.sddl_op.start = comp->offset;
2447 comp->offset += i;
2448 ok = push_sddl_token(comp, token);
2449 return ok;
2454 * if looks like an integer, and we expect an integer, it is
2455 * an integer. If we don't expect an integer, it is a local
2456 * attribute with a STUPID NAME.
2458 if (could_be_an_int(comp)) {
2459 return parse_int(comp);
2462 if (! expecting_attr) {
2463 comp_error(comp, "word makes no sense here");
2464 return false;
2466 /* it's definitely an attribute name */
2467 token.type = CONDITIONAL_ACE_LOCAL_ATTRIBUTE;
2468 if (comp->offset + i >= comp->length) {
2469 comp_error(comp, "missing trailing ')'?");
2470 return false;
2473 s = talloc_memdup(comp->mem_ctx, start, i + 1);
2474 if (s == NULL) {
2475 comp_error(comp, "allocation error");
2476 return false;
2478 s[i] = 0;
2479 token.data.local_attr.value = s;
2480 comp->offset += i;
2481 return write_sddl_token(comp, token);
2484 static bool parse_attr2(struct ace_condition_sddl_compiler_context *comp)
2487 * Attributes in the form @class.attr
2489 * class can be "User", "Device", or "Resource", case insensitive.
2491 size_t i;
2492 bool ok;
2493 size_t len;
2494 struct ace_condition_token token = {};
2496 if ((comp->state & SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR) == 0) {
2497 comp_error(comp, "did not expect @attr here");
2498 return false;
2500 if (comp->sddl[comp->offset] != '@') {
2501 comp_error(comp, "Expected '@'");
2502 return false;
2504 comp->offset++;
2506 for (i = 0; i < ARRAY_SIZE(sddl_attr_types); i++) {
2507 int ret;
2508 size_t attr_len = strlen(sddl_attr_types[i].name);
2509 if (attr_len >= comp->length - comp->offset) {
2510 continue;
2512 ret = strncasecmp(sddl_attr_types[i].name,
2513 (const char *) (comp->sddl + comp->offset),
2514 attr_len);
2515 if (ret == 0) {
2516 const uint8_t code = sddl_attr_types[i].code;
2518 if (!comp->allow_device &&
2519 (sddl_strings[code].flags & SDDL_FLAG_DEVICE))
2521 comp_error(comp,
2522 "a device attribute is not "
2523 "applicable in this context (did "
2524 "you intend a user attribute?)");
2525 return false;
2528 token.type = code;
2529 comp->offset += attr_len;
2530 break;
2533 if (i == ARRAY_SIZE(sddl_attr_types)) {
2534 comp_error(comp, "unknown attribute class");
2535 return false;
2539 * Now we are past the class and the '.', and into the
2540 * attribute name. The attribute name can be almost
2541 * anything, but some characters need to be escaped.
2544 len = read_attr2_string(comp, &token.data.unicode);
2545 if (len == -1) {
2546 /* read_attr2_string has set a message */
2547 return false;
2549 ok = write_sddl_token(comp, token);
2550 if (! ok) {
2551 return false;
2553 comp->offset += len;
2554 ok = eat_whitespace(comp, false);
2555 return ok;
2558 static bool parse_literal(struct ace_condition_sddl_compiler_context *comp,
2559 bool in_composite)
2561 uint8_t c = comp->sddl[comp->offset];
2562 if (!(comp->state & SDDL_FLAG_EXPECTING_LITERAL)) {
2563 comp_error(comp, "did not expect to be parsing a literal now");
2564 return false;
2566 switch(c) {
2567 case '#':
2568 return parse_octet_string(comp);
2569 case '"':
2570 return parse_unicode(comp);
2571 case 'S':
2572 return parse_sid(comp);
2573 case '{':
2574 if (in_composite) {
2575 /* nested composites are not supported */
2576 return false;
2577 } else {
2578 return parse_composite(comp);
2580 default:
2581 if (strchr("1234567890-+", c) != NULL) {
2582 return parse_int(comp);
2585 if (c > 31 && c < 127) {
2586 comp_error(comp,
2587 "unexpected byte 0x%02x '%c' parsing literal", c, c);
2588 } else {
2589 comp_error(comp, "unexpected byte 0x%02x parsing literal", c);
2591 return false;
2595 static bool parse_composite(struct ace_condition_sddl_compiler_context *comp)
2598 * This jumps into a different parser, expecting a comma separated
2599 * list of literal values, which might include nested literal
2600 * composites.
2602 * To handle the nesting, we redirect the pointers that determine
2603 * where write_sddl_token() writes.
2605 bool ok;
2606 bool first = true;
2607 struct ace_condition_token token = {
2608 .type = CONDITIONAL_ACE_TOKEN_COMPOSITE
2610 uint32_t start = comp->offset;
2611 size_t alloc_size;
2612 struct ace_condition_token *old_target = comp->target;
2613 uint32_t *old_target_len = comp->target_len;
2615 if (comp->sddl[start] != '{') {
2616 comp_error(comp, "expected '{' for composite list");
2617 return false;
2619 if (!(comp->state & SDDL_FLAG_EXPECTING_LITERAL)) {
2620 comp_error(comp, "did not expect '{' for composite list");
2621 return false;
2623 comp->offset++; /* past '{' */
2626 * the worst case is one token for every two bytes: {1,1,1}, and we
2627 * allocate for that (counting commas and finding '}' gets hard because
2628 * string literals).
2630 alloc_size = MIN((comp->length - start) / 2 + 1,
2631 CONDITIONAL_ACE_MAX_LENGTH);
2633 token.data.composite.tokens = talloc_array(
2634 comp->mem_ctx,
2635 struct ace_condition_token,
2636 alloc_size);
2637 if (token.data.composite.tokens == NULL) {
2638 comp_error(comp, "allocation failure");
2639 return false;
2642 comp->target = token.data.composite.tokens;
2643 comp->target_len = &token.data.composite.n_members;
2646 * in this loop we are looking for:
2648 * a) possible whitespace.
2649 * b) a comma (or terminating '}')
2650 * c) more possible whitespace
2651 * d) a literal
2653 * Failures use a goto to reset comp->target, just in case we ever try
2654 * continuing after error.
2656 while (comp->offset < comp->length) {
2657 uint8_t c;
2658 ok = eat_whitespace(comp, false);
2659 if (! ok) {
2660 goto fail;
2662 c = comp->sddl[comp->offset];
2663 if (c == '}') {
2664 comp->offset++;
2665 break;
2667 if (!first) {
2668 if (c != ',') {
2669 comp_error(comp,
2670 "malformed composite (expected comma)");
2671 goto fail;
2673 comp->offset++;
2675 ok = eat_whitespace(comp, false);
2676 if (! ok) {
2677 goto fail;
2680 first = false;
2681 if (*comp->target_len >= alloc_size) {
2682 comp_error(comp,
2683 "Too many tokens in composite "
2684 "(>= %"PRIu32" tokens)",
2685 *comp->target_len);
2686 goto fail;
2688 ok = parse_literal(comp, true);
2689 if (!ok) {
2690 goto fail;
2693 comp->target = old_target;
2694 comp->target_len = old_target_len;
2695 write_sddl_token(comp, token);
2696 return true;
2697 fail:
2698 talloc_free(token.data.composite.tokens);
2699 comp->target = old_target;
2700 comp->target_len = old_target_len;
2701 return false;
2705 static bool parse_paren_literal(struct ace_condition_sddl_compiler_context *comp)
2707 bool ok;
2708 if (comp->sddl[comp->offset] != '(') {
2709 comp_error(comp, "expected '('");
2710 return false;
2712 comp->offset++;
2713 ok = parse_literal(comp, false);
2714 if (!ok) {
2715 return false;
2717 if (comp->sddl[comp->offset] != ')') {
2718 comp_error(comp, "expected ')'");
2719 return false;
2721 comp->offset++;
2722 return true;
2725 static bool parse_expression(struct ace_condition_sddl_compiler_context *comp)
2728 * This expects a parenthesised expression.
2730 bool ok;
2731 struct ace_condition_token token = {};
2732 uint32_t start = comp->offset;
2734 if (comp->state & SDDL_FLAG_EXPECTING_PAREN_LITERAL) {
2736 * Syntactically we allow parentheses to wrap a
2737 * literal value after a Member_of or >= op, but we
2738 * want to remember that it just wants a single
2739 * literal, not a general expression.
2741 return parse_paren_literal(comp);
2744 if (comp->sddl[start] != '(') {
2745 comp_error(comp, "expected '('");
2746 return false;
2749 if (!(comp->state & SDDL_FLAG_EXPECTING_PAREN)) {
2750 comp_error(comp, "did not expect '('");
2751 return false;
2754 token.type = CONDITIONAL_ACE_SAMBA_SDDL_PAREN;
2755 token.data.sddl_op.start = start;
2756 ok = push_sddl_token(comp, token);
2757 if (!ok) {
2758 return false;
2760 comp->offset++; /* over the '(' */
2761 comp->state = SDDL_FLAGS_EXPR_START;
2762 DBG_INFO("%3"PRIu32": (\n", comp->offset);
2764 comp->state |= SDDL_FLAG_NOT_EXPECTING_END_PAREN;
2766 while (comp->offset < comp->length) {
2767 uint8_t c;
2768 ok = eat_whitespace(comp, false);
2769 if (! ok) {
2770 return false;
2772 c = comp->sddl[comp->offset];
2773 if (c == '(') {
2774 ok = parse_expression(comp);
2775 } else if (c == ')') {
2776 if (comp->state & (SDDL_FLAG_IS_BINARY_OP |
2777 SDDL_FLAG_IS_UNARY_OP)) {
2779 * You can't have "(a ==)" or "(!)"
2781 comp_error(comp,
2782 "operator lacks right hand argument");
2783 return false;
2785 if (comp->state & SDDL_FLAG_NOT_EXPECTING_END_PAREN) {
2787 * You can't have "( )"
2789 comp_error(comp, "empty expression");
2790 return false;
2792 break;
2793 } else if (c == '@') {
2794 ok = parse_attr2(comp);
2795 } else if (strchr("!<>=&|", c)) {
2796 ok = parse_oppy_op(comp);
2797 } else if (is_attr_char1(c)) {
2798 ok = parse_word(comp);
2799 } else if (comp->state & SDDL_FLAG_EXPECTING_LITERAL) {
2800 ok = parse_literal(comp, false);
2801 } else {
2802 if (c > 31 && c < 127) {
2803 comp_error(comp,
2804 "unexpected byte 0x%02x '%c'", c, c);
2805 } else {
2806 comp_error(comp, "unexpected byte 0x%02x", c);
2808 ok = false;
2811 if (! ok) {
2812 return false;
2815 * what did we just find? Set what we expect accordingly.
2817 comp->state = sddl_strings[comp->last_token_type].flags;
2818 DBG_INFO("%3"PRIu32": %s\n",
2819 comp->offset,
2820 sddl_strings[comp->last_token_type].name);
2822 ok = eat_whitespace(comp, false);
2823 if (!ok) {
2824 return false;
2827 if (comp->sddl[comp->offset] != ')') {
2828 comp_error(comp, "expected ')' to match '(' at %"PRIu32, start);
2829 return false;
2832 * we won't comp->offset++ until after these other error checks, so
2833 * that their messages have consistent locations.
2835 ok = flush_stack_tokens(comp, CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END);
2836 if (!ok) {
2837 return false;
2839 if (comp->stack_depth == 0) {
2840 comp_error(comp, "mysterious nesting error between %"
2841 PRIu32" and here",
2842 start);
2843 return false;
2845 token = comp->stack[comp->stack_depth - 1];
2846 if (token.type != CONDITIONAL_ACE_SAMBA_SDDL_PAREN) {
2847 comp_error(comp, "nesting error between %"PRIu32" and here",
2848 start);
2849 return false;
2851 if (token.data.sddl_op.start != start) {
2852 comp_error(comp, "')' should match '(' at %"PRIu32
2853 ", not %"PRIu32,
2854 token.data.sddl_op.start, start);
2855 return false;
2857 comp->stack_depth--;
2858 DBG_INFO("%3"PRIu32": )\n", comp->offset);
2860 comp->offset++; /* for the ')' */
2861 comp->last_token_type = CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END;
2862 comp->state = sddl_strings[comp->last_token_type].flags;
2864 ok = eat_whitespace(comp, true);
2865 return ok;
2870 static bool init_compiler_context(
2871 TALLOC_CTX *mem_ctx,
2872 struct ace_condition_sddl_compiler_context *comp,
2873 const enum ace_condition_flags ace_condition_flags,
2874 const char *sddl,
2875 size_t max_length,
2876 size_t max_stack)
2878 struct ace_condition_script *program = NULL;
2880 comp->sddl = (const uint8_t*)sddl;
2881 comp->mem_ctx = mem_ctx;
2883 program = talloc_zero(mem_ctx, struct ace_condition_script);
2884 if (program == NULL) {
2885 return false;
2888 * For the moment, we allocate for the worst case up front.
2890 program->tokens = talloc_array(program,
2891 struct ace_condition_token,
2892 max_length);
2893 if (program->tokens == NULL) {
2894 TALLOC_FREE(program);
2895 return false;
2897 comp->program = program;
2898 comp->stack = talloc_array(program,
2899 struct ace_condition_token,
2900 max_stack + 1);
2901 if (comp->stack == NULL) {
2902 TALLOC_FREE(program);
2903 return false;
2905 comp->target = program->tokens;
2906 comp->target_len = &program->length;
2907 comp->length = strlen(sddl);
2908 comp->state = SDDL_FLAG_EXPECTING_PAREN;
2909 comp->allow_device = ace_condition_flags & ACE_CONDITION_FLAG_ALLOW_DEVICE;
2910 return true;
2914 * Compile SDDL conditional ACE conditions.
2916 * @param mem_ctx
2917 * @param sddl - the string to be parsed
2918 * @param ace_condition_flags - flags controlling compiler behaviour
2919 * @param message - on error, a pointer to a compiler message
2920 * @param message_offset - where the error occurred
2921 * @param consumed_length - how much of the SDDL was used
2922 * @return a struct ace_condition_script (or NULL).
2924 struct ace_condition_script * ace_conditions_compile_sddl(
2925 TALLOC_CTX *mem_ctx,
2926 const enum ace_condition_flags ace_condition_flags,
2927 const char *sddl,
2928 const char **message,
2929 size_t *message_offset,
2930 size_t *consumed_length)
2932 bool ok;
2933 struct ace_condition_sddl_compiler_context comp = {};
2935 *message = NULL;
2936 *message_offset = 0;
2938 ok = init_compiler_context(mem_ctx,
2939 &comp,
2940 ace_condition_flags,
2941 sddl,
2942 CONDITIONAL_ACE_MAX_LENGTH,
2943 CONDITIONAL_ACE_MAX_TOKENS);
2944 if (!ok) {
2945 return NULL;
2948 ok = parse_expression(&comp);
2949 if (!ok) {
2950 goto error;
2952 if (comp.stack_depth != 0) {
2953 comp_error(&comp, "incomplete expression");
2954 goto error;
2956 if (consumed_length != NULL) {
2957 *consumed_length = comp.offset;
2959 *message = comp.message;
2960 *message_offset = comp.message_offset;
2961 return comp.program;
2962 error:
2963 *message = comp.message;
2964 *message_offset = comp.message_offset;
2965 TALLOC_FREE(comp.program);
2966 return NULL;
2971 static bool parse_resource_attr_list(
2972 struct ace_condition_sddl_compiler_context *comp,
2973 char attr_type_char)
2976 * This is a bit like parse_composite() above, but with the following
2977 * differences:
2979 * - it doesn't want '{...}' around the list.
2980 * - if there is just one value, it is not a composite
2981 * - all the values must be the expected type.
2982 * - there is no nesting.
2983 * - SIDs are not written with SID(...) around them.
2985 bool ok;
2986 bool first = true;
2987 struct ace_condition_token composite = {
2988 .type = CONDITIONAL_ACE_TOKEN_COMPOSITE
2990 uint32_t start = comp->offset;
2991 size_t alloc_size;
2992 struct ace_condition_token *old_target = comp->target;
2993 uint32_t *old_target_len = comp->target_len;
2995 comp->state = SDDL_FLAG_EXPECTING_LITERAL;
2998 * the worst case is one token for every two bytes: {1,1,1}, and we
2999 * allocate for that (counting commas and finding '}' gets hard because
3000 * string literals).
3002 alloc_size = MIN((comp->length - start) / 2 + 1,
3003 CONDITIONAL_ACE_MAX_LENGTH);
3005 composite.data.composite.tokens = talloc_array(
3006 comp->mem_ctx,
3007 struct ace_condition_token,
3008 alloc_size);
3009 if (composite.data.composite.tokens == NULL) {
3010 comp_error(comp, "allocation failure");
3011 return false;
3014 comp->target = composite.data.composite.tokens;
3015 comp->target_len = &composite.data.composite.n_members;
3018 * in this loop we are looking for:
3020 * a) possible whitespace.
3021 * b) a comma (or terminating ')')
3022 * c) more possible whitespace
3023 * d) a literal, of the right type (checked after)
3025 * Failures use a goto to reset comp->target, just in case we ever try
3026 * continuing after error.
3028 while (comp->offset < comp->length) {
3029 uint8_t c;
3030 ok = eat_whitespace(comp, false);
3031 if (! ok) {
3032 goto fail;
3034 c = comp->sddl[comp->offset];
3035 if (c == ')') {
3036 break;
3038 if (!first) {
3039 if (c != ',') {
3040 comp_error(comp,
3041 "malformed resource attribute ACE "
3042 "(expected comma)");
3043 goto fail;
3045 comp->offset++;
3047 ok = eat_whitespace(comp, false);
3048 if (! ok) {
3049 goto fail;
3052 first = false;
3053 if (*comp->target_len >= alloc_size) {
3054 comp_error(comp,
3055 "Too many tokens in resource attribute ACE "
3056 "(>= %"PRIu32" tokens)",
3057 *comp->target_len);
3058 goto fail;
3060 switch(attr_type_char) {
3061 case 'X':
3062 ok = parse_ra_octet_string(comp);
3063 break;
3064 case 'S':
3065 ok = parse_unicode(comp);
3066 break;
3067 case 'U':
3068 ok = parse_uint(comp);
3069 break;
3070 case 'B':
3071 ok = parse_bool(comp);
3072 break;
3073 case 'I':
3074 ok = parse_int(comp);
3075 break;
3076 case 'D':
3077 ok = parse_ra_sid(comp);
3078 break;
3079 default:
3080 /* it's a mystery we got this far */
3081 comp_error(comp,
3082 "unknown attribute type T%c",
3083 attr_type_char);
3084 goto fail;
3086 if (!ok) {
3087 goto fail;
3090 if (*comp->target_len == 0) {
3091 goto fail;
3094 comp->target = old_target;
3095 comp->target_len = old_target_len;
3098 * If we only ended up collecting one token into the composite, we
3099 * write that instead.
3101 if (composite.data.composite.n_members == 1) {
3102 ok = write_sddl_token(comp, composite.data.composite.tokens[0]);
3103 talloc_free(composite.data.composite.tokens);
3104 } else {
3105 ok = write_sddl_token(comp, composite);
3107 if (! ok) {
3108 goto fail;
3111 return true;
3112 fail:
3113 comp->target = old_target;
3114 comp->target_len = old_target_len;
3115 TALLOC_FREE(composite.data.composite.tokens);
3116 return false;
3121 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *sddl_decode_resource_attr (
3122 TALLOC_CTX *mem_ctx,
3123 const char *str,
3124 size_t *length)
3127 * Resource attribute ACEs define claims in object SACLs. They look like
3129 * "(RA; «flags» ;;;;WD;( «attribute-data» ))"
3131 * attribute-data = DQUOTE 1*attr-char2 DQUOTE "," \
3132 * ( TI-attr / TU-attr / TS-attr / TD-attr / TX-attr / TB-attr )
3133 * TI-attr = "TI" "," attr-flags *("," int-64)
3134 * TU-attr = "TU" "," attr-flags *("," uint-64)
3135 * TS-attr = "TS" "," attr-flags *("," char-string)
3136 * TD-attr = "TD" "," attr-flags *("," sid-string)
3137 * TX-attr = "TX" "," attr-flags *("," octet-string)
3138 * TB-attr = "TB" "," attr-flags *("," ( "0" / "1" ) )
3140 * and the data types are *mostly* parsed in the SDDL way,
3141 * though there are significant differences for octet-strings.
3143 * At this point we only have the "(«attribute-data»)".
3145 * What we do is set up a conditional ACE compiler to be expecting a
3146 * literal, and ask it to parse the strings between the commas. It's a
3147 * hack.
3149 bool ok;
3150 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL;
3151 struct ace_condition_sddl_compiler_context comp = {};
3152 char attr_type;
3153 struct ace_condition_token *tok;
3154 uint32_t flags;
3155 size_t len;
3156 struct ace_condition_unicode attr_name = {};
3158 ok = init_compiler_context(mem_ctx,
3159 &comp,
3160 ACE_CONDITION_FLAG_ALLOW_DEVICE,
3161 str,
3164 if (!ok) {
3165 return NULL;
3167 if (comp.length < 6 || comp.length > CONDITIONAL_ACE_MAX_LENGTH) {
3168 DBG_WARNING("invalid resource attribute: '%s'\n", str);
3169 goto error;
3172 * Resource attribute ACEs list SIDs in a bare form "S-1-2-3", while
3173 * conditional ACEs use a wrapper syntax "SID(S-1-2-3)". As almost
3174 * everything is the same, we are reusing the conditional ACE parser,
3175 * with a flag set to tell the SID parser which form to expect.
3178 /* Most examples on the web have leading whitespace */
3179 ok = eat_whitespace(&comp, false);
3180 if (!ok) {
3181 return NULL;
3183 if (comp.sddl[comp.offset] != '(' ||
3184 comp.sddl[comp.offset + 1] != '"') {
3185 DBG_WARNING("invalid resource attribute -- expected '(\"'\n");
3186 goto error;
3188 comp.offset += 2;
3191 * Read the name. Here we are not reading a token into comp->program,
3192 * just into a unicode blob.
3194 len = read_attr2_string(&comp, &attr_name);
3196 if (len == -1) {
3197 DBG_WARNING("invalid resource attr name: %s\n", str);
3198 goto error;
3200 comp.offset += len;
3202 ok = eat_whitespace(&comp, false);
3203 if (comp.offset + 6 > comp.length) {
3204 DBG_WARNING("invalid resource attribute (too short): '%s'\n",
3205 str);
3206 goto error;
3209 * now we have the name. Next comes '",«T[IUSDXB]»,' followed
3210 * by the flags, which are a 32 bit number.
3212 if (comp.sddl[comp.offset] != '"' ||
3213 comp.sddl[comp.offset + 1] != ','||
3214 comp.sddl[comp.offset + 2] != 'T') {
3215 DBG_WARNING("expected '\",T[IUSDXB]' after attr name\n");
3216 goto error;
3218 attr_type = comp.sddl[comp.offset + 3];
3220 if (comp.sddl[comp.offset + 4] != ',') {
3221 DBG_WARNING("expected ',' after attr type\n");
3222 goto error;
3224 comp.offset += 5;
3225 comp.state = SDDL_FLAG_EXPECTING_LITERAL;
3226 ok = parse_literal(&comp, false);
3227 if (!ok ||
3228 comp.program->length != 1) {
3229 DBG_WARNING("invalid attr flags: %s\n", str);
3230 goto error;
3233 tok = &comp.program->tokens[0];
3234 if (tok->type != CONDITIONAL_ACE_TOKEN_INT64 ||
3235 tok->data.int64.value < 0 ||
3236 tok->data.int64.value > UINT32_MAX) {
3237 DBG_WARNING("invalid attr flags (want 32 bit int): %s\n", str);
3238 goto error;
3240 flags = tok->data.int64.value;
3241 if (flags & 0xff00) {
3242 DBG_WARNING("invalid attr flags, "
3243 "stepping on reserved 0xff00 range: %s\n",
3244 str);
3245 goto error;
3247 if (comp.offset + 3 > comp.length) {
3248 DBG_WARNING("invalid resource attribute (too short): '%s'\n",
3249 str);
3250 goto error;
3252 if (comp.sddl[comp.offset] != ',') {
3253 DBG_WARNING("invalid resource attribute ace\n");
3254 goto error;
3256 comp.offset++;
3258 ok = parse_resource_attr_list(&comp, attr_type);
3259 if (!ok || comp.program->length != 2) {
3260 DBG_WARNING("invalid attribute type or value: T%c, %s\n",
3261 attr_type, str);
3262 goto error;
3264 if (comp.sddl[comp.offset] != ')') {
3265 DBG_WARNING("expected trailing ')'\n");
3266 goto error;
3268 comp.offset++;
3269 *length = comp.offset;
3271 ok = ace_token_to_claim_v1(mem_ctx,
3272 attr_name.value,
3273 &comp.program->tokens[1],
3274 &claim,
3275 flags);
3276 if (!ok) {
3277 goto error;
3279 TALLOC_FREE(comp.program);
3280 return claim;
3281 error:
3282 TALLOC_FREE(comp.program);
3283 return NULL;
3287 static bool write_resource_attr_from_token(struct sddl_write_context *ctx,
3288 const struct ace_condition_token *tok)
3291 * this is a helper for sddl_resource_attr_from_claim(),
3292 * recursing into composites if necessary.
3294 bool ok;
3295 char *sid = NULL;
3296 size_t i;
3297 const struct ace_condition_composite *c = NULL;
3298 switch (tok->type) {
3299 case CONDITIONAL_ACE_TOKEN_INT64:
3301 * Note that this includes uint and bool claim types,
3302 * but we don't check the validity of the ranges (0|1
3303 * and >=0, respectively), rather we trust the claim
3304 * to be self-consistent in this regard. Going the
3305 * other way, string-to-claim, we do check.
3307 return sddl_write_int(ctx, tok);
3309 case CONDITIONAL_ACE_TOKEN_UNICODE:
3310 return sddl_write_unicode(ctx, tok);
3312 case CONDITIONAL_ACE_TOKEN_SID:
3313 /* unlike conditional ACE, SID does not have a "SID()" wrapper. */
3314 sid = sddl_encode_sid(ctx->mem_ctx, &tok->data.sid.sid, NULL);
3315 if (sid == NULL) {
3316 return false;
3318 return sddl_write(ctx, sid);
3320 case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
3321 return sddl_write_ra_octet_string(ctx, tok);
3323 case CONDITIONAL_ACE_TOKEN_COMPOSITE:
3325 * write each token, separated by commas. If there
3326 * were nested composites, this would flatten them,
3327 * but that isn't really possible because the token we
3328 * are dealing with came from a claim, which has no
3329 * facility for nesting.
3331 c = &tok->data.composite;
3332 for(i = 0; i < c->n_members; i++) {
3333 ok = write_resource_attr_from_token(ctx, &c->tokens[i]);
3334 if (!ok) {
3335 return false;
3337 if (i != c->n_members - 1) {
3338 ok = sddl_write(ctx, ",");
3339 if (!ok) {
3340 return false;
3344 return true;
3345 default:
3346 /* We really really don't expect to get here */
3347 return false;
3351 char *sddl_resource_attr_from_claim(
3352 TALLOC_CTX *mem_ctx,
3353 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim)
3355 char *s = NULL;
3356 char attr_type;
3357 bool ok;
3358 struct ace_condition_token tok = {};
3359 struct sddl_write_context ctx = {};
3360 TALLOC_CTX *tmp_ctx = NULL;
3361 char *name = NULL;
3362 size_t name_len;
3364 switch(claim->value_type) {
3365 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
3366 attr_type = 'I';
3367 break;
3368 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
3369 attr_type = 'U';
3370 break;
3371 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
3372 attr_type = 'S';
3373 break;
3374 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
3375 attr_type = 'D';
3376 break;
3377 case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
3378 attr_type = 'B';
3379 break;
3380 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
3381 attr_type = 'X';
3382 break;
3383 default:
3384 return NULL;
3387 tmp_ctx = talloc_new(mem_ctx);
3388 if (tmp_ctx == NULL) {
3389 return NULL;
3391 ctx.mem_ctx = tmp_ctx;
3393 ok = claim_v1_to_ace_composite_unchecked(tmp_ctx, claim, &tok);
3394 if (!ok) {
3395 TALLOC_FREE(tmp_ctx);
3396 return NULL;
3399 /* this will construct the proper string in ctx.sddl */
3400 ok = write_resource_attr_from_token(&ctx, &tok);
3401 if (!ok) {
3402 TALLOC_FREE(tmp_ctx);
3403 return NULL;
3406 /* escape the claim name */
3407 ok = sddl_encode_attr_name(tmp_ctx,
3408 claim->name,
3409 &name, &name_len);
3411 if (!ok) {
3412 TALLOC_FREE(tmp_ctx);
3413 return NULL;
3416 s = talloc_asprintf(mem_ctx,
3417 "(\"%s\",T%c,0x%x,%s)",
3418 name,
3419 attr_type,
3420 claim->flags,
3421 ctx.sddl);
3422 TALLOC_FREE(tmp_ctx);
3423 return s;
3427 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *parse_sddl_literal_as_claim(
3428 TALLOC_CTX *mem_ctx,
3429 const char *name,
3430 const char *str)
3433 * For testing purposes (and possibly for client tools), we
3434 * want to be able to create claim literals, and we might as
3435 * well use the SDDL syntax. So we pretend to be parsing SDDL
3436 * for one literal.
3438 bool ok;
3439 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL;
3440 struct ace_condition_sddl_compiler_context comp = {};
3442 ok = init_compiler_context(mem_ctx,
3443 &comp,
3444 ACE_CONDITION_FLAG_ALLOW_DEVICE,
3445 str,
3448 if (!ok) {
3449 return NULL;
3452 comp.state = SDDL_FLAG_EXPECTING_LITERAL;
3453 ok = parse_literal(&comp, false);
3455 if (!ok) {
3456 goto error;
3458 if (comp.program->length != 1) {
3459 goto error;
3462 ok = ace_token_to_claim_v1(mem_ctx,
3463 name,
3464 &comp.program->tokens[0],
3465 &claim,
3467 if (!ok) {
3468 goto error;
3470 TALLOC_FREE(comp.program);
3471 return claim;
3472 error:
3473 TALLOC_FREE(comp.program);
3474 return NULL;