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/>.
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. */
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)
87 SDDL_PRECEDENCE_EXISTS
,
88 SDDL_PRECEDENCE_COMMON
,
92 SDDL_PRECEDENCE_PAREN_END
,
93 SDDL_PRECEDENCE_PAREN_START
,
96 struct ace_condition_sddl_compiler_context
{
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
;
109 uint32_t message_offset
;
110 struct dom_sid
*domain_sid
;
112 uint8_t last_token_type
;
119 uint8_t op_precedence
;
123 static const struct sddl_data sddl_strings
[256] = {
125 [CONDITIONAL_ACE_TOKEN_MEMBER_OF
] = {
127 SDDL_FLAGS_MEMBER_OP
,
128 SDDL_PRECEDENCE_COMMON
,
131 [CONDITIONAL_ACE_TOKEN_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' */
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
] = {
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
] = {
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
] = {
176 SDDL_FLAGS_RELATIONAL_OP
,
177 SDDL_PRECEDENCE_COMMON
,
180 [CONDITIONAL_ACE_TOKEN_NOT_EQUAL
] = {
182 SDDL_FLAGS_RELATIONAL_OP
,
183 SDDL_PRECEDENCE_COMMON
,
186 [CONDITIONAL_ACE_TOKEN_LESS_THAN
] = {
188 SDDL_FLAGS_RELATIONAL_OP
,
189 SDDL_PRECEDENCE_COMMON
,
192 [CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL
] = {
194 SDDL_FLAGS_RELATIONAL_OP
,
195 SDDL_PRECEDENCE_COMMON
,
198 [CONDITIONAL_ACE_TOKEN_GREATER_THAN
] = {
200 SDDL_FLAGS_RELATIONAL_OP
,
201 SDDL_PRECEDENCE_COMMON
,
204 [CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL
] = {
206 SDDL_FLAGS_RELATIONAL_OP
,
207 SDDL_PRECEDENCE_COMMON
,
210 [CONDITIONAL_ACE_TOKEN_CONTAINS
] = {
212 SDDL_FLAGS_CONTAINS_OP
,
213 SDDL_PRECEDENCE_COMMON
,
216 [CONDITIONAL_ACE_TOKEN_ANY_OF
] = {
218 SDDL_FLAGS_CONTAINS_OP
,
219 SDDL_PRECEDENCE_COMMON
,
222 [CONDITIONAL_ACE_TOKEN_NOT_CONTAINS
] = {
224 SDDL_FLAGS_CONTAINS_OP
,
225 SDDL_PRECEDENCE_COMMON
,
228 [CONDITIONAL_ACE_TOKEN_NOT_ANY_OF
] = {
230 SDDL_FLAGS_CONTAINS_OP
,
231 SDDL_PRECEDENCE_COMMON
,
234 [CONDITIONAL_ACE_TOKEN_AND
] = {
240 [CONDITIONAL_ACE_TOKEN_OR
] = {
246 [CONDITIONAL_ACE_TOKEN_NOT
] = {
248 (SDDL_FLAG_EXPECTING_PAREN
|
249 SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR
|
250 SDDL_FLAG_IS_UNARY_OP
),
254 [CONDITIONAL_ACE_TOKEN_EXISTS
] = {
256 SDDL_FLAGS_EXISTS_OP
,
257 SDDL_PRECEDENCE_EXISTS
,
260 [CONDITIONAL_ACE_TOKEN_NOT_EXISTS
] = {
262 SDDL_FLAGS_EXISTS_OP
,
263 SDDL_PRECEDENCE_EXISTS
,
266 /* pseudo-operator pseudo-tokens */
267 [CONDITIONAL_ACE_SAMBA_SDDL_PAREN
] = {
270 SDDL_PRECEDENCE_PAREN_START
,
273 [CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END
] = {
275 SDDL_FLAGS_PAREN_END
,
276 SDDL_PRECEDENCE_PAREN_END
,
282 * The names here are only used for error messages.
284 * some of them will never actually be encountered (e.g. 8-bit
287 [CONDITIONAL_ACE_TOKEN_INT8
] = {
288 .name
= "8-bit integer",
289 .flags
= SDDL_FLAGS_LITERAL
,
293 [CONDITIONAL_ACE_TOKEN_INT16
] = {
299 [CONDITIONAL_ACE_TOKEN_INT32
] = {
305 [CONDITIONAL_ACE_TOKEN_INT64
] = {
312 [CONDITIONAL_ACE_TOKEN_UNICODE
] = {
318 [CONDITIONAL_ACE_TOKEN_OCTET_STRING
] = {
324 [CONDITIONAL_ACE_TOKEN_COMPOSITE
] = {
330 [CONDITIONAL_ACE_TOKEN_SID
] = {
336 [CONDITIONAL_ACE_LOCAL_ATTRIBUTE
] = {
338 SDDL_FLAGS_ATTRIBUTE
,
342 [CONDITIONAL_ACE_USER_ATTRIBUTE
] = {
344 SDDL_FLAGS_ATTRIBUTE
,
348 [CONDITIONAL_ACE_RESOURCE_ATTRIBUTE
] = {
349 "resource attribute",
350 SDDL_FLAGS_ATTRIBUTE
,
354 [CONDITIONAL_ACE_DEVICE_ATTRIBUTE
] = {
356 SDDL_FLAGS_ATTRIBUTE
|SDDL_FLAG_DEVICE
,
360 [CONDITIONAL_ACE_SAMBA_RESULT_BOOL
] = {
366 [CONDITIONAL_ACE_SAMBA_RESULT_NULL
] = {
372 [CONDITIONAL_ACE_SAMBA_RESULT_ERROR
] = {
380 struct sddl_attr_type
{
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
{
404 static bool sddl_write(struct sddl_write_context
*ctx
,
407 size_t len
= strlen(s
);
408 if (ctx
->alloc_len
- ctx
->len
<= len
||
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
) {
416 ctx
->sddl
= talloc_realloc(ctx
->mem_ctx
, ctx
->sddl
,
417 char, ctx
->alloc_len
);
419 if (ctx
->sddl
== NULL
) {
423 memcpy(ctx
->sddl
+ ctx
->len
, s
, len
);
425 ctx
->sddl
[ctx
->len
] = 0;
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
)
441 struct sddl_write_context 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
];
452 snprintf(nom
, sizeof(nom
), "\033[1;33m%20s\033[0m", s
.name
);
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) {
462 snprintf(line
, sizeof(line
),
463 "%s %"PRIi64
" %c%c\n",
465 tok
->data
.int64
.value
,
466 "?+-_"[tok
->data
.int64
.sign
],
467 "?odh"[tok
->data
.int64
.base
]
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
),
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
),
506 case CONDITIONAL_ACE_TOKEN_EXISTS
:
507 case CONDITIONAL_ACE_TOKEN_NOT_EXISTS
:
508 case CONDITIONAL_ACE_TOKEN_NOT
:
509 snprintf(line
, sizeof(line
),
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",
523 tok
->data
.unicode
.value
528 case CONDITIONAL_ACE_TOKEN_UNICODE
:
529 snprintf(line
, sizeof(line
),
530 "%s.%s (any type)\n",
532 tok
->data
.unicode
.value
537 case CONDITIONAL_ACE_TOKEN_OCTET_STRING
: {
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
),
544 nom
, utf8_len
* 2, hex
, utf8_len
);
548 case CONDITIONAL_ACE_TOKEN_SID
:
549 utf8
= sddl_encode_sid(mem_ctx
,
552 snprintf(line
, sizeof(line
),
557 case CONDITIONAL_ACE_TOKEN_COMPOSITE
:
558 snprintf(line
, sizeof(line
),
559 "%s %"PRIu32
" direct members\n",
560 nom
, tok
->data
.composite
.n_members
);
564 case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING
:
565 snprintf(line
, sizeof(line
),
570 snprintf(line
, sizeof(line
),
571 "unknown opcode %#02x\n", tok
->type
);
576 if (s
.nargs
> depth
) {
577 snprintf(nom
, sizeof(nom
),
578 "UNDER: -%zu", s
.nargs
- depth
);
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
);
590 if (depth
< strlen(stack
)) {
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");
601 snprintf(line
, sizeof(line
),
602 "\033[1;31mBAD: should finish with a bool\033[0m\n");
604 sddl_write(&ctx
, line
);
608 TALLOC_FREE(ctx
.sddl
);
614 struct ace_condition_token
*tok
;
615 struct sddl_node
*lhs
;
616 struct sddl_node
*rhs
;
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 */
628 if (sign
> CONDITIONAL_ACE_INT_SIGN_NONE
||
629 base
> CONDITIONAL_ACE_INT_BASE_16
) {
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
);
644 snprintf(buf
, sizeof(buf
), "0x%"PRIx64
, v
);
646 return sddl_write(ctx
, buf
);
648 if (sign
== CONDITIONAL_ACE_INT_SIGN_POSITIVE
&& v
< 0) {
651 if (sign
== CONDITIONAL_ACE_INT_SIGN_NEGATIVE
&& v
> 0) {
652 /* note we allow "-0", because we will parse it. */
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
661 if (base
== CONDITIONAL_ACE_INT_BASE_10
) {
663 snprintf(buf
, sizeof(buf
), "%c0", sign_char
);
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
));
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) {
713 static bool sddl_encode_attr_name(TALLOC_CTX
*mem_ctx
,
720 uint16_t *utf16
= NULL
;
721 char *escaped
= NULL
;
722 size_t utf16_byte_len
;
724 size_t src_len
= strlen(src
);
730 * Writing the string escapes can only really happen in
733 ok
= convert_string_talloc(mem_ctx
,
736 &utf16
, &utf16_byte_len
);
740 utf16_len
= utf16_byte_len
/ 2;
743 for (i
= 0; i
< utf16_len
; i
++) {
744 uint16_t c
= utf16
[i
];
745 if (sddl_should_escape_utf16(c
)) {
749 /* we can't have '\0' (or "%0000") in a name. */
755 required
= src_len
+ escapees
* 5;
756 escaped
= talloc_size(mem_ctx
, required
+ 1);
757 if (escaped
== NULL
) {
763 /* there is nothing to escape: the original string is fine */
764 memcpy(escaped
, src
, src_len
);
765 escaped
[src_len
] = '\0';
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
);
780 snprintf(escaped
+ j
, 6, "%%%04x", c
);
796 static bool sddl_write_attr(struct sddl_write_context
*ctx
,
797 struct ace_condition_token
*tok
)
802 bool ok
= sddl_encode_attr_name(ctx
->mem_ctx
,
803 tok
->data
.local_attr
.value
,
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
, "@");
815 ok
= sddl_write(ctx
, x
.name
);
823 ok
= sddl_write(ctx
, name
);
829 static bool sddl_write_unicode(struct sddl_write_context
*ctx
,
830 const struct ace_condition_token
*tok
)
835 * We rely on tok->data.unicode.value being
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
853 quoted
= talloc_asprintf(ctx
->mem_ctx
, "\"%s\"",
854 tok
->data
.unicode
.value
);
855 if (quoted
== NULL
) {
858 ok
= sddl_write(ctx
, quoted
);
863 static bool sddl_write_octet_string(struct sddl_write_context
*ctx
,
864 const struct ace_condition_token
*tok
)
867 char *hex
= hex_encode_talloc(ctx
->mem_ctx
,
868 tok
->data
.bytes
.data
,
869 tok
->data
.bytes
.length
);
870 ok
= sddl_write(ctx
, "#");
874 ok
= sddl_write(ctx
, hex
);
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
)
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
);
896 static bool sddl_write_sid(struct sddl_write_context
*ctx
,
897 const struct ace_condition_token
*tok
)
901 char *sid
= sddl_encode_sid(ctx
->mem_ctx
,
907 sddl
= talloc_asprintf(ctx
->mem_ctx
, "SID(%s)", sid
);
912 ok
= sddl_write(ctx
, sddl
);
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
;
927 ok
= sddl_write(ctx
, "{");
931 for (i
= 0; i
< c
->n_members
; i
++) {
932 struct ace_condition_token
*t
= &c
->tokens
[i
];
934 ok
= sddl_write(ctx
, ", ");
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
);
946 case CONDITIONAL_ACE_TOKEN_UNICODE
:
947 ok
= sddl_write_unicode(ctx
, t
);
949 case CONDITIONAL_ACE_TOKEN_OCTET_STRING
:
950 ok
= sddl_write_octet_string(ctx
, t
);
952 case CONDITIONAL_ACE_TOKEN_SID
:
953 ok
= sddl_write_sid(ctx
, t
);
955 case CONDITIONAL_ACE_TOKEN_COMPOSITE
:
964 ok
= sddl_write(ctx
, "}");
968 static bool sddl_write_node(struct sddl_write_context
*ctx
,
969 struct sddl_node
*node
)
971 struct ace_condition_token
*tok
= node
->tok
;
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
);
1032 /* not expecting to get here */
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
) {
1068 if (sddl_wants_outer_parens(child
)) {
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
)
1096 if (node
->wants_parens
) {
1097 ok
= sddl_write(ctx
, "(");
1103 if (node
->lhs
!= NULL
) {
1104 ok
= sddl_tree_to_sddl(ctx
, node
->lhs
);
1108 ok
= sddl_write(ctx
, " ");
1114 ok
= sddl_write_node(ctx
, node
);
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
, " ");
1127 ok
= sddl_tree_to_sddl(ctx
, node
->rhs
);
1132 if (node
->wants_parens
) {
1133 ok
= sddl_write(ctx
, ")");
1142 * Convert conditional ACE conditions into SDDL conditions.
1146 * @return a string or NULL on error.
1148 char *sddl_from_conditional_ace(TALLOC_CTX
*mem_ctx
,
1149 struct ace_condition_script
*program
)
1153 struct sddl_node
*nodes
= NULL
;
1154 struct sddl_node
**trees
= NULL
;
1156 struct ace_condition_token
*tok
= NULL
;
1159 struct sddl_write_context 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
,
1172 if (nodes
== NULL
) {
1176 trees
= talloc_array(mem_ctx
,
1179 if (trees
== 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:
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
1212 for (i
= 0; i
< program
->length
; i
++) {
1213 tok
= &program
->tokens
[i
];
1214 s
= sddl_strings
[tok
->type
];
1216 if (s
.nargs
> n_trees
) {
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
1229 nodes
[i
].rhs
= trees
[n_trees
];
1233 nodes
[i
].lhs
= trees
[n_trees
];
1236 trees
[n_trees
] = &nodes
[i
];
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]);
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
, ...)
1285 msg
= talloc_vasprintf(comp
->mem_ctx
, fmt
, ap
);
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
;
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
,
1310 if (comp
->message
== NULL
) {
1313 DBG_NOTICE("%s\n", comp
->message
);
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
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 )
1351 contains-op = attr-name wspace ("Contains" / "Not_Contains") wspace (attr-name2
1354 anyof-op = attr-name wspace ("Any_of" / "Not_Any_of") wspace (attr-name2 /
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.
1416 uint16_t *utf16
= NULL
;
1417 size_t utf16_byte_len
;
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
++) {
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
)) {
1440 /* too long, because we need at least one ')' */
1441 comp_error(comp
, "interminable attribute name");
1445 /* too short! like "User.>= 4" */
1446 comp_error(comp
, "empty attribute name");
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
1457 comp_error(comp
, "attribute is way too long (%zu)", i
);
1463 ok
= convert_string_talloc(comp
->mem_ctx
,
1464 CH_UTF8
, CH_UTF16LE
,
1466 &utf16
, &utf16_byte_len
);
1468 comp_error(comp
, "could not convert to utf-16");
1472 * utf16_byte_len is in bytes, we want to count uint16s.
1474 utf16_chars
= utf16_byte_len
/ 2;
1476 /* now the escapes. */
1478 j
< utf16_chars
&& i
< utf16_chars
;
1480 uint16_t c
= utf16
[i
];
1485 * we need to read 4 hex characters.
1486 * hex_byte() won't help because that is 8-bit.
1488 if (end
> utf16_chars
) {
1490 "insufficient room for %% escape");
1494 for (i
++; i
< end
; i
++) {
1497 if (c
>= '0' && c
<= '9') {
1499 } else if (c
>= 'A' && c
<= 'F') {
1501 } else if (c
>= 'a' && c
<= 'f') {
1504 comp_error(comp
, "invalid %% escape");
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') ||
1517 strchr("#$'*+-;?@[\\]^_`{}~:/.", v
) != NULL
)) {
1518 comp_error(comp
, "invalid %% escape: "
1519 "'%%%04x' should be literal '%c'",
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
];
1540 ok
= convert_string_talloc(comp
->mem_ctx
,
1541 CH_UTF16LE
, CH_UTF8
,
1543 &dest
->value
, &utf8_len
);
1546 comp_error(comp
, "could not convert to utf-16");
1550 /* returning bytes consumed, not necessarily the length of token */
1556 static bool eat_whitespace(struct ace_condition_sddl_compiler_context
*comp
,
1560 * Advance the offset to the first non-whitespace character.
1562 * If trailing is false, there has to be something before the end of
1565 while (comp
->offset
< comp
->length
) {
1566 if (! is_wspace(comp
->sddl
[comp
->offset
])) {
1571 if ((!trailing
) && comp
->offset
== comp
->length
) {
1572 comp_error(comp
, "input ends unexpectedly");
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
,
1592 uint8_t precedence
= sddl_strings
[type
].op_precedence
;
1593 if (precedence
== SDDL_PRECEDENCE_PAREN_START
) {
1594 /* paren has a special role */
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
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
1606 * TOKEN dest PROGRAM STACK
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
) {
1629 if(sddl_strings
[op
->type
].op_precedence
== precedence
&&
1630 sddl_strings
[op
->type
].flags
& SDDL_FLAG_IS_UNARY_OP
) {
1634 ok
= pop_write_sddl_token(comp
);
1637 "could not flush '%s' to program",
1638 sddl_strings
[op
->type
].name
);
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");
1652 if (sddl_strings
[token
.type
].op_precedence
== SDDL_NOT_AN_OP
) {
1654 "wrong kind of token for the SDDL stack: %s",
1655 sddl_strings
[token
.type
].name
);
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
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
;
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");
1682 comp
->stack_depth
--;
1683 *token
= comp
->stack
[comp
->stack_depth
];
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",
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 "
1707 CONDITIONAL_ACE_MAX_TOKENS
);
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
)++;
1718 static bool pop_write_sddl_token(
1719 struct ace_condition_sddl_compiler_context
*comp
)
1722 struct ace_condition_token token
= {};
1723 ok
= pop_sddl_token(comp
, &token
);
1725 comp_error(comp
, "could not pop from op stack");
1728 if (comp
->target
!= comp
->program
->tokens
) {
1729 comp_error(comp
, "compiler is seriously confused");
1733 ok
= write_sddl_token(comp
, token
);
1736 "could not write '%s' to program",
1737 sddl_strings
[token
.type
].name
);
1740 DBG_INFO(" written '%s'\n", sddl_strings
[token
.type
].name
);
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.
1758 struct ace_condition_token token
= {};
1760 uint32_t flag
= SDDL_FLAG_EXPECTING_BINARY_OP
;
1762 if (comp
->offset
+ 1 >= comp
->length
) {
1763 comp_error(comp
, "syntax error");
1767 token
.data
.sddl_op
.start
= comp
->offset
;
1770 * These are all one or two characters long, and we always have room
1773 c
= comp
->sddl
[comp
->offset
];
1774 d
= comp
->sddl
[comp
->offset
+ 1];
1779 token
.type
= CONDITIONAL_ACE_TOKEN_NOT_EQUAL
;
1782 token
.type
= CONDITIONAL_ACE_TOKEN_NOT
;
1783 flag
= SDDL_FLAG_EXPECTING_UNARY_OP
;
1785 } else if (c
== '=' && d
== '=') {
1787 token
.type
= CONDITIONAL_ACE_TOKEN_EQUAL
;
1788 } else if (c
== '>') {
1791 token
.type
= CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL
;
1794 token
.type
= CONDITIONAL_ACE_TOKEN_GREATER_THAN
;
1796 } else if (c
== '<') {
1799 token
.type
= CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL
;
1802 token
.type
= CONDITIONAL_ACE_TOKEN_LESS_THAN
;
1804 } else if (c
== '&' && d
== '&') {
1806 token
.type
= CONDITIONAL_ACE_TOKEN_AND
;
1807 flag
= SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP
;
1808 } else if (c
== '|' && d
== '|') {
1810 token
.type
= CONDITIONAL_ACE_TOKEN_OR
;
1811 flag
= SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP
;
1813 comp_error(comp
, "unknown operator");
1817 if ((comp
->state
& flag
) == 0) {
1818 comp_error(comp
, "unexpected operator");
1824 ok
= push_sddl_token(comp
, token
);
1829 ok
= eat_whitespace(comp
, true);
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
1843 struct ace_condition_token token
= {};
1845 const uint8_t *src
= NULL
;
1847 size_t len
, max_len
;
1849 if (comp
->sddl
[comp
->offset
] != '"') {
1850 comp_error(comp
, "was expecting '\"' for Unicode string");
1854 src
= comp
->sddl
+ comp
->offset
;
1855 max_len
= comp
->length
- comp
->offset
;
1857 for (len
= 0; len
< max_len
; len
++) {
1858 if (src
[len
] == '"') {
1862 if (len
== max_len
) {
1863 comp_error(comp
, "unterminated unicode string");
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
,
1877 comp_error(comp
, "not valid unicode");
1882 s
= talloc_array_size(comp
->mem_ctx
, 1, len
+ 1);
1884 comp_error(comp
, "allocation error");
1887 memcpy(s
, src
, len
);
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
1904 struct ace_condition_token token
= {};
1907 if (comp
->sddl
[comp
->offset
] != '#') {
1908 comp_error(comp
, "was expecting '#' for octet string");
1912 length
= strspn((const char*)(comp
->sddl
+ comp
->offset
),
1913 "#0123456789abcdefABCDEF");
1916 comp_error(comp
, "octet string has odd number of hex digits");
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}).
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
]);
1946 talloc_free(token
.data
.bytes
.data
);
1947 comp_error(comp
, "inexplicable error in octet string");
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
;
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");
1985 token
.data
.bytes
= data_blob_talloc_zero(comp
->mem_ctx
, bytes_length
);
1986 if (token
.data
.bytes
.data
== NULL
) {
1989 token
.type
= CONDITIONAL_ACE_TOKEN_OCTET_STRING
;
1993 if (string_length
& 1) {
1995 * An odd number of characters means the first
1996 * character gains an implicit 0 for the high nybble.
1999 pair
[1] = (comp
->sddl
[0] == '#') ? '0' : comp
->sddl
[0];
2001 ok
= hex_byte(pair
, &token
.data
.bytes
.data
[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) {
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
]);
2029 return write_sddl_token(comp
, token
);
2032 comp_error(comp
, "inexplicable error in octet string");
2033 talloc_free(token
.data
.bytes
.data
);
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
= {};
2044 if (comp
->length
- comp
->offset
< 7) {
2045 /* minimum: "SID(AA)" */
2046 comp_error(comp
, "no room for a complete SID");
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");
2060 sidstr
= comp
->sddl
+ comp
->offset
;
2062 sid
= sddl_decode_sid(comp
->mem_ctx
,
2063 (const char **)&sidstr
,
2067 comp_error(comp
, "could not parse SID");
2070 end
= sidstr
- comp
->sddl
;
2071 if (end
>= comp
->length
|| end
< comp
->offset
) {
2072 comp_error(comp
, "apparent overflow in SID parsing");
2077 * offset is now at the end of the SID, but we need to account
2080 if (comp
->sddl
[comp
->offset
] != ')') {
2081 comp_error(comp
, "expected ')' to follow SID");
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
= {};
2100 if ((comp
->state
& SDDL_FLAG_EXPECTING_LITERAL
) == 0) {
2101 comp_error(comp
, "did not expect a SID here");
2105 * Here we are parsing a resource attribute ACE which doesn't
2106 * have the SID() wrapper around the SID string (unlike a
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");
2118 sidstr
= comp
->sddl
+ comp
->offset
;
2120 sid
= sddl_decode_sid(comp
->mem_ctx
,
2121 (const char **)&sidstr
,
2125 comp_error(comp
, "could not parse SID");
2128 end
= sidstr
- comp
->sddl
;
2129 if (end
>= comp
->length
|| end
< comp
->offset
) {
2130 comp_error(comp
, "apparent overflow in SID parsing");
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.
2146 struct ace_condition_token token
= {};
2147 const char *start
= (const char *)comp
->sddl
+ comp
->offset
;
2149 const char *first_digit
= start
;
2152 v
= strtoll(start
, &end
, 0);
2154 comp_error(comp
, "bad integer: %s", strerror(errno
));
2160 comp_error(comp
, "unexpected non-integer");
2163 if (comp
->offset
+ len
> comp
->length
) {
2164 comp_error(comp
, "impossible integer length: %zu!", len
);
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
;
2184 } else if (*start
== '+') {
2185 token
.data
.int64
.sign
= CONDITIONAL_ACE_INT_SIGN_POSITIVE
;
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
;
2196 token
.data
.int64
.base
= CONDITIONAL_ACE_INT_BASE_8
;
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
);
2216 * check that the token's value is positive.
2218 if (comp
->target_len
== 0) {
2221 tok
= &comp
->target
[*comp
->target_len
- 1];
2222 if (tok
->type
!= CONDITIONAL_ACE_TOKEN_INT64
) {
2225 if (tok
->data
.int64
.value
< 0) {
2226 comp_error(comp
, "invalid resource ACE value for unsigned TU claim");
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) {
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
) {
2247 if (tok
->data
.int64
.value
!= 0 && tok
->data
.int64
.value
!= 1) {
2248 comp_error(comp
, "invalid resource ACE Boolean value");
2255 static bool could_be_an_int(struct ace_condition_sddl_compiler_context
*comp
)
2257 const char *start
= (const char*)(comp
->sddl
+ comp
->offset
);
2260 if ((comp
->state
& SDDL_FLAG_EXPECTING_LITERAL
) == 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);
2276 end
>= (const char*)comp
->sddl
+ comp
->length
) {
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
2287 if (is_attr_char1(*end
)) {
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.
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];
2332 if (! is_attr_char1(*start
)) {
2333 /* we shouldn't get here, because we peeked first */
2338 * We'll look for a SID first, because it simplifies the rest.
2340 if (expecting_literal
&&
2341 comp
->offset
+ 4 < comp
->length
&&
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
) {
2369 } else if (!expecting_binary
) {
2372 candidates
[n_candidates
] = i
;
2374 if (n_candidates
== ARRAY_SIZE(candidates
)) {
2375 /* impossible, really. */
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");
2393 while (comp
->offset
+ i
< comp
->length
) {
2395 if (! is_attr_char1(c
)) {
2398 if (n_candidates
!= 0) {
2400 * Filter out candidate operators that no longer
2403 int uc
= toupper(c
);
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
];
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
))
2437 "a device‐relative expression "
2438 "will never evaluate to true "
2439 "in this context (did you "
2440 "intend a user‐relative "
2446 token
.data
.sddl_op
.start
= comp
->offset
;
2448 ok
= push_sddl_token(comp
, token
);
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");
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 ')'?");
2473 s
= talloc_memdup(comp
->mem_ctx
, start
, i
+ 1);
2475 comp_error(comp
, "allocation error");
2479 token
.data
.local_attr
.value
= s
;
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.
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");
2500 if (comp
->sddl
[comp
->offset
] != '@') {
2501 comp_error(comp
, "Expected '@'");
2506 for (i
= 0; i
< ARRAY_SIZE(sddl_attr_types
); i
++) {
2508 size_t attr_len
= strlen(sddl_attr_types
[i
].name
);
2509 if (attr_len
>= comp
->length
- comp
->offset
) {
2512 ret
= strncasecmp(sddl_attr_types
[i
].name
,
2513 (const char *) (comp
->sddl
+ comp
->offset
),
2516 const uint8_t code
= sddl_attr_types
[i
].code
;
2518 if (!comp
->allow_device
&&
2519 (sddl_strings
[code
].flags
& SDDL_FLAG_DEVICE
))
2522 "a device attribute is not "
2523 "applicable in this context (did "
2524 "you intend a user attribute?)");
2529 comp
->offset
+= attr_len
;
2533 if (i
== ARRAY_SIZE(sddl_attr_types
)) {
2534 comp_error(comp
, "unknown attribute class");
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
);
2546 /* read_attr2_string has set a message */
2549 ok
= write_sddl_token(comp
, token
);
2553 comp
->offset
+= len
;
2554 ok
= eat_whitespace(comp
, false);
2558 static bool parse_literal(struct ace_condition_sddl_compiler_context
*comp
,
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");
2568 return parse_octet_string(comp
);
2570 return parse_unicode(comp
);
2572 return parse_sid(comp
);
2575 /* nested composites are not supported */
2578 return parse_composite(comp
);
2581 if (strchr("1234567890-+", c
) != NULL
) {
2582 return parse_int(comp
);
2585 if (c
> 31 && c
< 127) {
2587 "unexpected byte 0x%02x '%c' parsing literal", c
, c
);
2589 comp_error(comp
, "unexpected byte 0x%02x parsing literal", c
);
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
2602 * To handle the nesting, we redirect the pointers that determine
2603 * where write_sddl_token() writes.
2607 struct ace_condition_token token
= {
2608 .type
= CONDITIONAL_ACE_TOKEN_COMPOSITE
2610 uint32_t start
= comp
->offset
;
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");
2619 if (!(comp
->state
& SDDL_FLAG_EXPECTING_LITERAL
)) {
2620 comp_error(comp
, "did not expect '{' for composite list");
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
2630 alloc_size
= MIN((comp
->length
- start
) / 2 + 1,
2631 CONDITIONAL_ACE_MAX_LENGTH
);
2633 token
.data
.composite
.tokens
= talloc_array(
2635 struct ace_condition_token
,
2637 if (token
.data
.composite
.tokens
== NULL
) {
2638 comp_error(comp
, "allocation failure");
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
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
) {
2658 ok
= eat_whitespace(comp
, false);
2662 c
= comp
->sddl
[comp
->offset
];
2670 "malformed composite (expected comma)");
2675 ok
= eat_whitespace(comp
, false);
2681 if (*comp
->target_len
>= alloc_size
) {
2683 "Too many tokens in composite "
2684 "(>= %"PRIu32
" tokens)",
2688 ok
= parse_literal(comp
, true);
2693 comp
->target
= old_target
;
2694 comp
->target_len
= old_target_len
;
2695 write_sddl_token(comp
, token
);
2698 talloc_free(token
.data
.composite
.tokens
);
2699 comp
->target
= old_target
;
2700 comp
->target_len
= old_target_len
;
2705 static bool parse_paren_literal(struct ace_condition_sddl_compiler_context
*comp
)
2708 if (comp
->sddl
[comp
->offset
] != '(') {
2709 comp_error(comp
, "expected '('");
2713 ok
= parse_literal(comp
, false);
2717 if (comp
->sddl
[comp
->offset
] != ')') {
2718 comp_error(comp
, "expected ')'");
2725 static bool parse_expression(struct ace_condition_sddl_compiler_context
*comp
)
2728 * This expects a parenthesised expression.
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 '('");
2749 if (!(comp
->state
& SDDL_FLAG_EXPECTING_PAREN
)) {
2750 comp_error(comp
, "did not expect '('");
2754 token
.type
= CONDITIONAL_ACE_SAMBA_SDDL_PAREN
;
2755 token
.data
.sddl_op
.start
= start
;
2756 ok
= push_sddl_token(comp
, token
);
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
) {
2768 ok
= eat_whitespace(comp
, false);
2772 c
= comp
->sddl
[comp
->offset
];
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 "(!)"
2782 "operator lacks right hand argument");
2785 if (comp
->state
& SDDL_FLAG_NOT_EXPECTING_END_PAREN
) {
2787 * You can't have "( )"
2789 comp_error(comp
, "empty expression");
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);
2802 if (c
> 31 && c
< 127) {
2804 "unexpected byte 0x%02x '%c'", c
, c
);
2806 comp_error(comp
, "unexpected byte 0x%02x", c
);
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",
2820 sddl_strings
[comp
->last_token_type
].name
);
2822 ok
= eat_whitespace(comp
, false);
2827 if (comp
->sddl
[comp
->offset
] != ')') {
2828 comp_error(comp
, "expected ')' to match '(' at %"PRIu32
, start
);
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
);
2839 if (comp
->stack_depth
== 0) {
2840 comp_error(comp
, "mysterious nesting error between %"
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",
2851 if (token
.data
.sddl_op
.start
!= start
) {
2852 comp_error(comp
, "')' should match '(' at %"PRIu32
2854 token
.data
.sddl_op
.start
, start
);
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);
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
,
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
) {
2888 * For the moment, we allocate for the worst case up front.
2890 program
->tokens
= talloc_array(program
,
2891 struct ace_condition_token
,
2893 if (program
->tokens
== NULL
) {
2894 TALLOC_FREE(program
);
2897 comp
->program
= program
;
2898 comp
->stack
= talloc_array(program
,
2899 struct ace_condition_token
,
2901 if (comp
->stack
== NULL
) {
2902 TALLOC_FREE(program
);
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
;
2914 * Compile SDDL conditional ACE conditions.
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
,
2928 const char **message
,
2929 size_t *message_offset
,
2930 size_t *consumed_length
)
2933 struct ace_condition_sddl_compiler_context comp
= {};
2936 *message_offset
= 0;
2938 ok
= init_compiler_context(mem_ctx
,
2940 ace_condition_flags
,
2942 CONDITIONAL_ACE_MAX_LENGTH
,
2943 CONDITIONAL_ACE_MAX_TOKENS
);
2948 ok
= parse_expression(&comp
);
2952 if (comp
.stack_depth
!= 0) {
2953 comp_error(&comp
, "incomplete expression");
2956 if (consumed_length
!= NULL
) {
2957 *consumed_length
= comp
.offset
;
2959 *message
= comp
.message
;
2960 *message_offset
= comp
.message_offset
;
2961 return comp
.program
;
2963 *message
= comp
.message
;
2964 *message_offset
= comp
.message_offset
;
2965 TALLOC_FREE(comp
.program
);
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
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.
2987 struct ace_condition_token composite
= {
2988 .type
= CONDITIONAL_ACE_TOKEN_COMPOSITE
2990 uint32_t start
= comp
->offset
;
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
3002 alloc_size
= MIN((comp
->length
- start
) / 2 + 1,
3003 CONDITIONAL_ACE_MAX_LENGTH
);
3005 composite
.data
.composite
.tokens
= talloc_array(
3007 struct ace_condition_token
,
3009 if (composite
.data
.composite
.tokens
== NULL
) {
3010 comp_error(comp
, "allocation failure");
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
) {
3030 ok
= eat_whitespace(comp
, false);
3034 c
= comp
->sddl
[comp
->offset
];
3041 "malformed resource attribute ACE "
3042 "(expected comma)");
3047 ok
= eat_whitespace(comp
, false);
3053 if (*comp
->target_len
>= alloc_size
) {
3055 "Too many tokens in resource attribute ACE "
3056 "(>= %"PRIu32
" tokens)",
3060 switch(attr_type_char
) {
3062 ok
= parse_ra_octet_string(comp
);
3065 ok
= parse_unicode(comp
);
3068 ok
= parse_uint(comp
);
3071 ok
= parse_bool(comp
);
3074 ok
= parse_int(comp
);
3077 ok
= parse_ra_sid(comp
);
3080 /* it's a mystery we got this far */
3082 "unknown attribute type T%c",
3090 if (*comp
->target_len
== 0) {
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
);
3105 ok
= write_sddl_token(comp
, composite
);
3113 comp
->target
= old_target
;
3114 comp
->target_len
= old_target_len
;
3115 TALLOC_FREE(composite
.data
.composite
.tokens
);
3121 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*sddl_decode_resource_attr (
3122 TALLOC_CTX
*mem_ctx
,
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
3150 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
= NULL
;
3151 struct ace_condition_sddl_compiler_context comp
= {};
3153 struct ace_condition_token
*tok
;
3156 struct ace_condition_unicode attr_name
= {};
3158 ok
= init_compiler_context(mem_ctx
,
3160 ACE_CONDITION_FLAG_ALLOW_DEVICE
,
3167 if (comp
.length
< 6 || comp
.length
> CONDITIONAL_ACE_MAX_LENGTH
) {
3168 DBG_WARNING("invalid resource attribute: '%s'\n", str
);
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);
3183 if (comp
.sddl
[comp
.offset
] != '(' ||
3184 comp
.sddl
[comp
.offset
+ 1] != '"') {
3185 DBG_WARNING("invalid resource attribute -- expected '(\"'\n");
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
);
3197 DBG_WARNING("invalid resource attr name: %s\n", str
);
3202 ok
= eat_whitespace(&comp
, false);
3203 if (comp
.offset
+ 6 > comp
.length
) {
3204 DBG_WARNING("invalid resource attribute (too short): '%s'\n",
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");
3218 attr_type
= comp
.sddl
[comp
.offset
+ 3];
3220 if (comp
.sddl
[comp
.offset
+ 4] != ',') {
3221 DBG_WARNING("expected ',' after attr type\n");
3225 comp
.state
= SDDL_FLAG_EXPECTING_LITERAL
;
3226 ok
= parse_literal(&comp
, false);
3228 comp
.program
->length
!= 1) {
3229 DBG_WARNING("invalid attr flags: %s\n", str
);
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
);
3240 flags
= tok
->data
.int64
.value
;
3241 if (flags
& 0xff00) {
3242 DBG_WARNING("invalid attr flags, "
3243 "stepping on reserved 0xff00 range: %s\n",
3247 if (comp
.offset
+ 3 > comp
.length
) {
3248 DBG_WARNING("invalid resource attribute (too short): '%s'\n",
3252 if (comp
.sddl
[comp
.offset
] != ',') {
3253 DBG_WARNING("invalid resource attribute ace\n");
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",
3264 if (comp
.sddl
[comp
.offset
] != ')') {
3265 DBG_WARNING("expected trailing ')'\n");
3269 *length
= comp
.offset
;
3271 ok
= ace_token_to_claim_v1(mem_ctx
,
3273 &comp
.program
->tokens
[1],
3279 TALLOC_FREE(comp
.program
);
3282 TALLOC_FREE(comp
.program
);
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.
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
);
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
]);
3337 if (i
!= c
->n_members
- 1) {
3338 ok
= sddl_write(ctx
, ",");
3346 /* We really really don't expect to get here */
3351 char *sddl_resource_attr_from_claim(
3352 TALLOC_CTX
*mem_ctx
,
3353 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
)
3358 struct ace_condition_token tok
= {};
3359 struct sddl_write_context ctx
= {};
3360 TALLOC_CTX
*tmp_ctx
= NULL
;
3364 switch(claim
->value_type
) {
3365 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64
:
3368 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64
:
3371 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING
:
3374 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID
:
3377 case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN
:
3380 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING
:
3387 tmp_ctx
= talloc_new(mem_ctx
);
3388 if (tmp_ctx
== NULL
) {
3391 ctx
.mem_ctx
= tmp_ctx
;
3393 ok
= claim_v1_to_ace_composite_unchecked(tmp_ctx
, claim
, &tok
);
3395 TALLOC_FREE(tmp_ctx
);
3399 /* this will construct the proper string in ctx.sddl */
3400 ok
= write_resource_attr_from_token(&ctx
, &tok
);
3402 TALLOC_FREE(tmp_ctx
);
3406 /* escape the claim name */
3407 ok
= sddl_encode_attr_name(tmp_ctx
,
3412 TALLOC_FREE(tmp_ctx
);
3416 s
= talloc_asprintf(mem_ctx
,
3417 "(\"%s\",T%c,0x%x,%s)",
3422 TALLOC_FREE(tmp_ctx
);
3427 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*parse_sddl_literal_as_claim(
3428 TALLOC_CTX
*mem_ctx
,
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
3439 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
= NULL
;
3440 struct ace_condition_sddl_compiler_context comp
= {};
3442 ok
= init_compiler_context(mem_ctx
,
3444 ACE_CONDITION_FLAG_ALLOW_DEVICE
,
3452 comp
.state
= SDDL_FLAG_EXPECTING_LITERAL
;
3453 ok
= parse_literal(&comp
, false);
3458 if (comp
.program
->length
!= 1) {
3462 ok
= ace_token_to_claim_v1(mem_ctx
,
3464 &comp
.program
->tokens
[0],
3470 TALLOC_FREE(comp
.program
);
3473 TALLOC_FREE(comp
.program
);