Line data Source code
1 : /*
2 : * Unix SMB implementation.
3 : * Functions for understanding conditional ACEs
4 : *
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.
9 : *
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.
14 : *
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/>.
17 : */
18 :
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"
27 :
28 :
29 : /* We're only dealing with utf-8 here. Honestly. */
30 : #undef strncasecmp
31 :
32 :
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
42 :
43 : #define SDDL_FLAG_DEVICE 512
44 :
45 : #define SDDL_FLAG_IS_UNARY_OP (1 << 20)
46 : #define SDDL_FLAG_IS_BINARY_OP (1 << 21)
47 :
48 :
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)
53 :
54 : #define SDDL_FLAGS_MEMBER_OP (SDDL_FLAG_EXPECTING_LITERAL | \
55 : SDDL_FLAG_EXPECTING_PAREN_LITERAL | \
56 : SDDL_FLAG_IS_UNARY_OP)
57 :
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)
62 :
63 : #define SDDL_FLAGS_CONTAINS_OP (SDDL_FLAG_EXPECTING_LITERAL | \
64 : SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
65 : SDDL_FLAG_IS_BINARY_OP)
66 :
67 : #define SDDL_FLAGS_EXISTS_OP (SDDL_FLAG_EXPECTING_LOCAL_ATTR | \
68 : SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR | \
69 : SDDL_FLAG_IS_UNARY_OP)
70 :
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)
76 :
77 : #define SDDL_FLAGS_ATTRIBUTE (SDDL_FLAG_EXPECTING_BINARY_OP | \
78 : SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP)
79 :
80 : #define SDDL_FLAGS_LITERAL SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP
81 :
82 : #define SDDL_FLAGS_PAREN_END (SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP | \
83 : SDDL_FLAG_EXPECTING_BINARY_OP)
84 :
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,
94 : };
95 :
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;
114 : };
115 :
116 : struct sddl_data {
117 : const char *name;
118 : uint32_t flags;
119 : uint8_t op_precedence;
120 : uint8_t nargs;
121 : };
122 :
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,
129 : 1
130 : },
131 : [CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF] = {
132 : "Device_Member_of",
133 : SDDL_FLAGS_MEMBER_OP|SDDL_FLAG_DEVICE,
134 : SDDL_PRECEDENCE_COMMON,
135 : 1
136 : },
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,
142 : 1
143 : },
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,
148 : 1
149 : },
150 : [CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF] = {
151 : "Not_Member_of",
152 : SDDL_FLAGS_MEMBER_OP,
153 : SDDL_PRECEDENCE_COMMON,
154 : 1
155 : },
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,
160 : 1
161 : },
162 : [CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY] = {
163 : "Not_Member_of_Any",
164 : SDDL_FLAGS_MEMBER_OP,
165 : SDDL_PRECEDENCE_COMMON,
166 : 1
167 : },
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,
172 : 1
173 : },
174 : [CONDITIONAL_ACE_TOKEN_EQUAL] = {
175 : "==",
176 : SDDL_FLAGS_RELATIONAL_OP,
177 : SDDL_PRECEDENCE_COMMON,
178 : 2
179 : },
180 : [CONDITIONAL_ACE_TOKEN_NOT_EQUAL] = {
181 : "!=",
182 : SDDL_FLAGS_RELATIONAL_OP,
183 : SDDL_PRECEDENCE_COMMON,
184 : 2
185 : },
186 : [CONDITIONAL_ACE_TOKEN_LESS_THAN] = {
187 : "<",
188 : SDDL_FLAGS_RELATIONAL_OP,
189 : SDDL_PRECEDENCE_COMMON,
190 : 2
191 : },
192 : [CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL] = {
193 : "<=",
194 : SDDL_FLAGS_RELATIONAL_OP,
195 : SDDL_PRECEDENCE_COMMON,
196 : 2
197 : },
198 : [CONDITIONAL_ACE_TOKEN_GREATER_THAN] = {
199 : ">",
200 : SDDL_FLAGS_RELATIONAL_OP,
201 : SDDL_PRECEDENCE_COMMON,
202 : 2
203 : },
204 : [CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL] = {
205 : ">=",
206 : SDDL_FLAGS_RELATIONAL_OP,
207 : SDDL_PRECEDENCE_COMMON,
208 : 2
209 : },
210 : [CONDITIONAL_ACE_TOKEN_CONTAINS] = {
211 : "Contains",
212 : SDDL_FLAGS_CONTAINS_OP,
213 : SDDL_PRECEDENCE_COMMON,
214 : 2
215 : },
216 : [CONDITIONAL_ACE_TOKEN_ANY_OF] = {
217 : "Any_of",
218 : SDDL_FLAGS_CONTAINS_OP,
219 : SDDL_PRECEDENCE_COMMON,
220 : 2
221 : },
222 : [CONDITIONAL_ACE_TOKEN_NOT_CONTAINS] = {
223 : "Not_Contains",
224 : SDDL_FLAGS_CONTAINS_OP,
225 : SDDL_PRECEDENCE_COMMON,
226 : 2
227 : },
228 : [CONDITIONAL_ACE_TOKEN_NOT_ANY_OF] = {
229 : "Not_Any_of",
230 : SDDL_FLAGS_CONTAINS_OP,
231 : SDDL_PRECEDENCE_COMMON,
232 : 2
233 : },
234 : [CONDITIONAL_ACE_TOKEN_AND] = {
235 : "&&",
236 : SDDL_FLAGS_LOGIC_OP,
237 : SDDL_PRECEDENCE_AND,
238 : 2
239 : },
240 : [CONDITIONAL_ACE_TOKEN_OR] = {
241 : "||",
242 : SDDL_FLAGS_LOGIC_OP,
243 : SDDL_PRECEDENCE_OR,
244 : 2
245 : },
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,
252 : 1
253 : },
254 : [CONDITIONAL_ACE_TOKEN_EXISTS] = {
255 : "Exists",
256 : SDDL_FLAGS_EXISTS_OP,
257 : SDDL_PRECEDENCE_EXISTS,
258 : 1
259 : },
260 : [CONDITIONAL_ACE_TOKEN_NOT_EXISTS] = {
261 : "Not_Exists",
262 : SDDL_FLAGS_EXISTS_OP,
263 : SDDL_PRECEDENCE_EXISTS,
264 : 1
265 : },
266 : /* pseudo-operator pseudo-tokens */
267 : [CONDITIONAL_ACE_SAMBA_SDDL_PAREN] = {
268 : "(",
269 : 0,
270 : SDDL_PRECEDENCE_PAREN_START,
271 : 0
272 : },
273 : [CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END] = {
274 : ")",
275 : SDDL_FLAGS_PAREN_END,
276 : SDDL_PRECEDENCE_PAREN_END,
277 : 0
278 : },
279 :
280 : /*
281 : * non-operators.
282 : * The names here are only used for error messages.
283 : *
284 : * some of them will never actually be encountered (e.g. 8-bit
285 : * integers).
286 : */
287 : [CONDITIONAL_ACE_TOKEN_INT8] = {
288 : .name = "8-bit integer",
289 : .flags = SDDL_FLAGS_LITERAL,
290 : SDDL_NOT_AN_OP,
291 : 0
292 : },
293 : [CONDITIONAL_ACE_TOKEN_INT16] = {
294 : "16-bit integer",
295 : SDDL_FLAGS_LITERAL,
296 : SDDL_NOT_AN_OP,
297 : 0
298 : },
299 : [CONDITIONAL_ACE_TOKEN_INT32] = {
300 : "32-bit integer",
301 : SDDL_FLAGS_LITERAL,
302 : SDDL_NOT_AN_OP,
303 : 0
304 : },
305 : [CONDITIONAL_ACE_TOKEN_INT64] = {
306 : "64-bit integer",
307 : SDDL_FLAGS_LITERAL,
308 : SDDL_NOT_AN_OP,
309 : 0
310 : },
311 :
312 : [CONDITIONAL_ACE_TOKEN_UNICODE] = {
313 : "unicode",
314 : SDDL_FLAGS_LITERAL,
315 : SDDL_NOT_AN_OP,
316 : 0
317 : },
318 : [CONDITIONAL_ACE_TOKEN_OCTET_STRING] = {
319 : "byte string",
320 : SDDL_FLAGS_LITERAL,
321 : SDDL_NOT_AN_OP,
322 : 0
323 : },
324 : [CONDITIONAL_ACE_TOKEN_COMPOSITE] = {
325 : "composite list",
326 : SDDL_FLAGS_LITERAL,
327 : SDDL_NOT_AN_OP,
328 : 0
329 : },
330 : [CONDITIONAL_ACE_TOKEN_SID] = {
331 : "SID",
332 : SDDL_FLAGS_LITERAL,
333 : SDDL_NOT_AN_OP,
334 : 0
335 : },
336 : [CONDITIONAL_ACE_LOCAL_ATTRIBUTE] = {
337 : "local attribute",
338 : SDDL_FLAGS_ATTRIBUTE,
339 : SDDL_NOT_AN_OP,
340 : 0
341 : },
342 : [CONDITIONAL_ACE_USER_ATTRIBUTE] = {
343 : "user attribute",
344 : SDDL_FLAGS_ATTRIBUTE,
345 : SDDL_NOT_AN_OP,
346 : 0
347 : },
348 : [CONDITIONAL_ACE_RESOURCE_ATTRIBUTE] = {
349 : "resource attribute",
350 : SDDL_FLAGS_ATTRIBUTE,
351 : SDDL_NOT_AN_OP,
352 : 0
353 : },
354 : [CONDITIONAL_ACE_DEVICE_ATTRIBUTE] = {
355 : "device attribute",
356 : SDDL_FLAGS_ATTRIBUTE|SDDL_FLAG_DEVICE,
357 : SDDL_NOT_AN_OP,
358 : 0
359 : },
360 : [CONDITIONAL_ACE_SAMBA_RESULT_BOOL] = {
361 : "boolean result",
362 : 0,
363 : SDDL_NOT_AN_OP,
364 : 0
365 : },
366 : [CONDITIONAL_ACE_SAMBA_RESULT_NULL] = {
367 : "null result",
368 : 0,
369 : SDDL_NOT_AN_OP,
370 : 0
371 : },
372 : [CONDITIONAL_ACE_SAMBA_RESULT_ERROR] = {
373 : "error result",
374 : 0,
375 : SDDL_NOT_AN_OP,
376 : 0
377 : },
378 : };
379 :
380 : struct sddl_attr_type{
381 : const char *name;
382 : uint8_t code;
383 : };
384 :
385 : /*
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.
389 : */
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},
394 : };
395 :
396 :
397 : struct sddl_write_context {
398 : TALLOC_CTX *mem_ctx;
399 : char *sddl;
400 : size_t len;
401 : size_t alloc_len;
402 : };
403 :
404 1951 : static bool sddl_write(struct sddl_write_context *ctx,
405 : const char *s)
406 : {
407 1951 : size_t len = strlen(s);
408 1951 : if (ctx->alloc_len - ctx->len <= len ||
409 1759 : ctx->sddl == NULL) {
410 192 : size_t old = ctx->alloc_len;
411 192 : ctx->alloc_len = old + MAX(old / 2, len + 50);
412 192 : if (ctx->alloc_len <= old ||
413 192 : ctx->alloc_len - ctx->len <= len) {
414 0 : return false;
415 : }
416 192 : ctx->sddl = talloc_realloc(ctx->mem_ctx, ctx->sddl,
417 : char, ctx->alloc_len);
418 :
419 192 : if (ctx->sddl == NULL) {
420 0 : return false;
421 : }
422 : }
423 1951 : memcpy(ctx->sddl + ctx->len, s, len);
424 1951 : ctx->len += len;
425 1951 : ctx->sddl[ctx->len] = 0;
426 1951 : return true;
427 : }
428 :
429 : /*
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.
433 : */
434 0 : char *debug_conditional_ace(TALLOC_CTX *mem_ctx,
435 : struct ace_condition_script *program)
436 : {
437 0 : size_t i;
438 0 : size_t depth = 0;
439 0 : char stack[] = " ";
440 0 : char line[120];
441 0 : struct sddl_write_context ctx = {
442 : .mem_ctx = mem_ctx
443 : };
444 :
445 0 : for (i = 0; i < program->length; i++) {
446 0 : struct ace_condition_token *tok = &program->tokens[i];
447 0 : struct sddl_data s = sddl_strings[tok->type];
448 0 : char hex[21];
449 0 : char *utf8 = NULL;
450 0 : int utf8_len;
451 0 : char type;
452 0 : char nom[40];
453 0 : snprintf(nom, sizeof(nom), "\033[1;33m%20s\033[0m", s.name);
454 0 : switch (tok->type) {
455 0 : case CONDITIONAL_ACE_TOKEN_INT8:
456 : case CONDITIONAL_ACE_TOKEN_INT16:
457 : case CONDITIONAL_ACE_TOKEN_INT32:
458 : case CONDITIONAL_ACE_TOKEN_INT64:
459 0 : if (tok->data.int64.sign > 3 ||
460 0 : tok->data.int64.base > 3) {
461 0 : goto error;
462 : }
463 0 : snprintf(line, sizeof(line),
464 : "%s %"PRIi64" %c%c\n",
465 : nom,
466 : tok->data.int64.value,
467 0 : "?+-_"[tok->data.int64.sign],
468 0 : "?odh"[tok->data.int64.base]
469 : );
470 0 : type = 'i';
471 0 : break;
472 :
473 0 : case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
474 : case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
475 : case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
476 : case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
477 : case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
478 : case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
479 : case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
480 : case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
481 0 : snprintf(line, sizeof(line),
482 : "%s bool\n",
483 : nom
484 : );
485 0 : type = 'b';
486 0 : break;
487 :
488 0 : case CONDITIONAL_ACE_TOKEN_EQUAL:
489 : case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
490 : case CONDITIONAL_ACE_TOKEN_LESS_THAN:
491 : case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
492 : case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
493 : case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
494 : case CONDITIONAL_ACE_TOKEN_CONTAINS:
495 : case CONDITIONAL_ACE_TOKEN_ANY_OF:
496 : case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
497 : case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
498 : case CONDITIONAL_ACE_TOKEN_AND:
499 : case CONDITIONAL_ACE_TOKEN_OR:
500 0 : snprintf(line, sizeof(line),
501 : "%s bool\n",
502 : nom
503 : );
504 0 : type = 'b';
505 0 : break;
506 :
507 0 : case CONDITIONAL_ACE_TOKEN_EXISTS:
508 : case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
509 : case CONDITIONAL_ACE_TOKEN_NOT:
510 0 : snprintf(line, sizeof(line),
511 : "%s bool\n",
512 : nom
513 : );
514 0 : type = 'b';
515 0 : break;
516 :
517 0 : case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
518 : case CONDITIONAL_ACE_USER_ATTRIBUTE:
519 : case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
520 : case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
521 0 : snprintf(line, sizeof(line),
522 : "%s.%s (any type)\n",
523 : nom,
524 : tok->data.unicode.value
525 : );
526 0 : type = '?';
527 0 : break;
528 :
529 0 : case CONDITIONAL_ACE_TOKEN_UNICODE:
530 0 : snprintf(line, sizeof(line),
531 : "%s.%s (any type)\n",
532 : nom,
533 : tok->data.unicode.value
534 : );
535 0 : type = 'u';
536 0 : break;
537 :
538 0 : case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
539 0 : utf8_len = MIN(tok->data.bytes.length, 9);
540 0 : hex_encode_buf(hex, tok->data.bytes.data, utf8_len);
541 :
542 0 : snprintf(line, sizeof(line),
543 : "%s %.*s (%d)\n",
544 : nom, utf8_len * 2, hex, utf8_len);
545 0 : type = 'o';
546 0 : break;
547 0 : case CONDITIONAL_ACE_TOKEN_SID:
548 0 : utf8 = sddl_encode_sid(mem_ctx,
549 0 : &tok->data.sid.sid,
550 : NULL);
551 0 : snprintf(line, sizeof(line),
552 : "%s (%s)\n",
553 : nom, utf8);
554 0 : type = 'S';
555 0 : break;
556 0 : case CONDITIONAL_ACE_TOKEN_COMPOSITE:
557 0 : snprintf(line, sizeof(line),
558 : "%s %"PRIu32" direct members\n",
559 : nom, tok->data.composite.n_members);
560 0 : type = 'C';
561 0 : break;
562 :
563 0 : case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING:
564 0 : snprintf(line, sizeof(line),
565 : "%s\n", nom);
566 0 : type = '0';
567 0 : break;
568 0 : default:
569 0 : snprintf(line, sizeof(line),
570 0 : "unknown opcode %#02x\n", tok->type);
571 0 : type = '!';
572 0 : break;
573 : }
574 :
575 0 : if (s.nargs > depth) {
576 0 : snprintf(nom, sizeof(nom),
577 0 : "UNDER: -%zu", s.nargs - depth);
578 0 : depth = 0;
579 0 : sddl_write(&ctx, nom);
580 0 : } else if (depth >= strlen(stack)) {
581 0 : snprintf(nom, sizeof(nom),
582 0 : "depth %zu", s.nargs - depth);
583 0 : depth -= (s.nargs - 1);
584 0 : sddl_write(&ctx, nom);
585 : } else {
586 0 : depth -= s.nargs;
587 0 : stack[depth] = type;
588 0 : depth++;
589 0 : if (depth < strlen(stack)) {
590 0 : stack[depth] = ' ';
591 : }
592 0 : sddl_write(&ctx, stack);
593 : }
594 0 : sddl_write(&ctx, line);
595 : }
596 0 : if (depth == 1 && stack[0] == 'b') {
597 0 : snprintf(line, sizeof(line),
598 : "\033[1;32mGOOD: finishes on a single bool\033[0m\n");
599 : } else {
600 0 : snprintf(line, sizeof(line),
601 : "\033[1;31mBAD: should finish with a bool\033[0m\n");
602 : }
603 0 : sddl_write(&ctx, line);
604 0 : return ctx.sddl;
605 :
606 0 : error:
607 0 : TALLOC_FREE(ctx.sddl);
608 0 : return NULL;
609 : }
610 :
611 :
612 : struct sddl_node {
613 : struct ace_condition_token *tok;
614 : struct sddl_node *lhs;
615 : struct sddl_node *rhs;
616 : bool wants_parens;
617 : };
618 :
619 81 : static bool sddl_write_int(struct sddl_write_context *ctx,
620 : const struct ace_condition_token *tok)
621 : {
622 81 : int64_t v = tok->data.int64.value;
623 81 : uint8_t sign = tok->data.int64.sign;
624 81 : uint8_t base = tok->data.int64.base;
625 81 : char buf[26]; /* oct(1<<63) + sign + \0 */
626 81 : char sign_char;
627 81 : if (sign > CONDITIONAL_ACE_INT_SIGN_NONE ||
628 81 : base > CONDITIONAL_ACE_INT_BASE_16) {
629 0 : return false;
630 : }
631 :
632 : /*
633 : * we have 9 combinations of base/sign (+ some invalid combinations of
634 : * actual sign vs claimed sign).
635 : */
636 81 : if (sign == CONDITIONAL_ACE_INT_SIGN_NONE) {
637 : /* octal and hex will end up unsigned! */
638 67 : if (base == CONDITIONAL_ACE_INT_BASE_8) {
639 6 : snprintf(buf, sizeof(buf), "0%"PRIo64, v);
640 61 : } else if (base == CONDITIONAL_ACE_INT_BASE_10) {
641 54 : snprintf(buf, sizeof(buf), "%"PRId64, v);
642 : } else {
643 7 : snprintf(buf, sizeof(buf), "0x%"PRIx64, v);
644 : }
645 67 : return sddl_write(ctx, buf);
646 : }
647 14 : if (sign == CONDITIONAL_ACE_INT_SIGN_POSITIVE && v < 0) {
648 0 : return false;
649 : }
650 14 : if (sign == CONDITIONAL_ACE_INT_SIGN_NEGATIVE && v > 0) {
651 : /* note we allow "-0", because we will parse it. */
652 0 : return false;
653 : }
654 14 : sign_char = (sign == CONDITIONAL_ACE_INT_SIGN_NEGATIVE) ? '-' : '+';
655 : /*
656 : * We can use "%+ld" for the decimal sign (except -0), but
657 : * "%+lx" and "%+lo" are invalid because %o and %x are
658 : * unsigned.
659 : */
660 14 : if (base == CONDITIONAL_ACE_INT_BASE_10) {
661 4 : if (v == 0) {
662 4 : snprintf(buf, sizeof(buf), "%c0", sign_char);
663 : } else {
664 0 : snprintf(buf, sizeof(buf), "%+"PRId64, v);
665 : }
666 4 : return sddl_write(ctx, buf);
667 : }
668 :
669 10 : if (v == INT64_MIN) {
670 : /*
671 : * llabs(INT64_MIN) will be undefined.
672 : * The lengths we must go to to round trip!
673 : */
674 0 : if (base == CONDITIONAL_ACE_INT_BASE_8) {
675 0 : return sddl_write(ctx, "-01000000000000000000000");
676 : }
677 0 : return sddl_write(ctx, "-0x8000000000000000");
678 : }
679 :
680 10 : if (base == CONDITIONAL_ACE_INT_BASE_8) {
681 6 : snprintf(buf, sizeof(buf), "%c0%llo", sign_char, llabs(v));
682 : } else {
683 4 : snprintf(buf, sizeof(buf), "%c0x%llx", sign_char, llabs(v));
684 : }
685 10 : return sddl_write(ctx, buf);
686 : }
687 :
688 :
689 1110 : static bool sddl_should_escape_utf16(uint16_t c)
690 : {
691 1110 : if (c <= ' ' || c > 126) {
692 0 : return true;
693 : }
694 :
695 916 : switch (c) {
696 0 : case '!':
697 : case '"':
698 : case '&':
699 : case '(':
700 : case ')':
701 : case '<':
702 : case '=':
703 : case '>':
704 : case '|':
705 : case '%':
706 0 : return true;
707 : }
708 :
709 142 : return false;
710 : }
711 :
712 186 : static bool sddl_encode_attr_name(TALLOC_CTX *mem_ctx,
713 : const char *src,
714 : char **dest,
715 : size_t *dest_len)
716 : {
717 181 : size_t i, j;
718 181 : bool ok;
719 186 : uint16_t *utf16 = NULL;
720 186 : char *escaped = NULL;
721 181 : size_t utf16_byte_len;
722 181 : size_t utf16_len;
723 186 : size_t src_len = strlen(src);
724 181 : size_t escapees;
725 181 : size_t required;
726 186 : *dest = NULL;
727 :
728 : /*
729 : * Writing the string escapes can only really happen in
730 : * utf-16.
731 : */
732 186 : ok = convert_string_talloc(mem_ctx,
733 : CH_UTF8, CH_UTF16LE,
734 : src, src_len,
735 : &utf16, &utf16_byte_len);
736 186 : if (!ok) {
737 0 : return false;
738 : }
739 186 : utf16_len = utf16_byte_len / 2;
740 :
741 186 : escapees = 0;
742 1192 : for (i = 0; i < utf16_len; i++) {
743 1006 : uint16_t c = utf16[i];
744 1006 : if (sddl_should_escape_utf16(c)) {
745 97 : escapees++;
746 : }
747 1006 : if (c == 0) {
748 : /* we can't have '\0' (or "%0000") in a name. */
749 0 : TALLOC_FREE(utf16);
750 0 : return false;
751 : }
752 : }
753 :
754 186 : required = src_len + escapees * 5;
755 186 : escaped = talloc_size(mem_ctx, required + 1);
756 186 : if (escaped == NULL) {
757 0 : TALLOC_FREE(utf16);
758 0 : return false;
759 : }
760 :
761 186 : if (escapees == 0) {
762 : /* there is nothing to escape: the original string is fine */
763 183 : memcpy(escaped, src, src_len);
764 183 : escaped[src_len] = '\0';
765 183 : *dest = escaped;
766 183 : *dest_len = src_len;
767 183 : TALLOC_FREE(utf16);
768 183 : return true;
769 : }
770 :
771 107 : for (i = 0, j = 0; i < utf16_len && j < required; i++) {
772 104 : uint16_t c = utf16[i];
773 104 : if (sddl_should_escape_utf16(c)) {
774 97 : if (j + 5 >= required) {
775 0 : TALLOC_FREE(escaped);
776 0 : TALLOC_FREE(utf16);
777 0 : return false;
778 : }
779 97 : snprintf(escaped + j, 6, "%%%04x", c);
780 97 : j += 5;
781 : } else {
782 7 : escaped[j] = c;
783 7 : j++;
784 : }
785 : }
786 3 : escaped[j] = '\0';
787 :
788 3 : *dest = escaped;
789 3 : *dest_len = j;
790 :
791 3 : TALLOC_FREE(utf16);
792 0 : return true;
793 : }
794 :
795 179 : static bool sddl_write_attr(struct sddl_write_context *ctx,
796 : struct ace_condition_token *tok)
797 : {
798 179 : char *name = NULL;
799 174 : size_t name_len;
800 174 : size_t i;
801 179 : bool ok = sddl_encode_attr_name(ctx->mem_ctx,
802 : tok->data.local_attr.value,
803 : &name, &name_len);
804 179 : if (!ok) {
805 0 : return false;
806 : }
807 381 : for (i = 0; i < ARRAY_SIZE(sddl_attr_types); i++) {
808 340 : struct sddl_attr_type x = sddl_attr_types[i];
809 340 : if (x.code == tok->type) {
810 138 : ok = sddl_write(ctx, "@");
811 138 : if (! ok) {
812 0 : return false;
813 : }
814 138 : ok = sddl_write(ctx, x.name);
815 138 : if (! ok) {
816 0 : return false;
817 : }
818 138 : break;
819 : }
820 : }
821 :
822 179 : ok = sddl_write(ctx, name);
823 179 : talloc_free(name);
824 179 : return ok;
825 : }
826 :
827 :
828 40 : static bool sddl_write_unicode(struct sddl_write_context *ctx,
829 : const struct ace_condition_token *tok)
830 : {
831 40 : char *quoted = NULL;
832 35 : bool ok;
833 : /*
834 : * We rely on tok->data.unicode.value being
835 : * nul-terminated.
836 : */
837 40 : if (strchr(tok->data.unicode.value, '"') != NULL) {
838 : /*
839 : * There is a double quote in this string, but SDDL
840 : * has no mechanism for escaping these (or anything
841 : * else) in unicode strings.
842 : *
843 : * The only thing to do is fail.
844 : *
845 : * This cannot happen with an ACE created from SDDL,
846 : * because the same no-escapes rule applies on the way
847 : * in.
848 : */
849 0 : return false;
850 : }
851 :
852 40 : quoted = talloc_asprintf(ctx->mem_ctx, "\"%s\"",
853 5 : tok->data.unicode.value);
854 40 : if (quoted == NULL) {
855 0 : return false;
856 : }
857 40 : ok = sddl_write(ctx, quoted);
858 40 : TALLOC_FREE(quoted);
859 40 : return ok;
860 : }
861 :
862 7 : static bool sddl_write_octet_string(struct sddl_write_context *ctx,
863 : const struct ace_condition_token *tok)
864 : {
865 7 : bool ok;
866 14 : char *hex = hex_encode_talloc(ctx->mem_ctx,
867 7 : tok->data.bytes.data,
868 7 : tok->data.bytes.length);
869 7 : ok = sddl_write(ctx, "#");
870 7 : if (!ok) {
871 0 : return false;
872 : }
873 7 : ok = sddl_write(ctx, hex);
874 7 : talloc_free(hex);
875 7 : return ok;
876 : }
877 :
878 : /*
879 : * For octet strings, the Resource attribute ACE SDDL differs from conditional
880 : * ACE SDDL, lacking the leading '#'.
881 : */
882 6 : static bool sddl_write_ra_octet_string(struct sddl_write_context *ctx,
883 : const struct ace_condition_token *tok)
884 : {
885 6 : bool ok;
886 12 : char *hex = hex_encode_talloc(ctx->mem_ctx,
887 6 : tok->data.bytes.data,
888 6 : tok->data.bytes.length);
889 6 : ok = sddl_write(ctx, hex);
890 6 : talloc_free(hex);
891 6 : return ok;
892 : }
893 :
894 :
895 55 : static bool sddl_write_sid(struct sddl_write_context *ctx,
896 : const struct ace_condition_token *tok)
897 : {
898 44 : bool ok;
899 55 : char *sddl = NULL;
900 55 : char *sid = sddl_encode_sid(ctx->mem_ctx,
901 : &tok->data.sid.sid,
902 : NULL);
903 55 : if (sid == NULL) {
904 0 : return false;
905 : }
906 55 : sddl = talloc_asprintf(ctx->mem_ctx, "SID(%s)", sid);
907 55 : if (sddl == NULL) {
908 0 : talloc_free(sid);
909 0 : return false;
910 : }
911 55 : ok = sddl_write(ctx, sddl);
912 55 : talloc_free(sid);
913 55 : talloc_free(sddl);
914 55 : return ok;
915 : }
916 :
917 54 : static bool sddl_write_composite(struct sddl_write_context *ctx,
918 : struct ace_condition_token *tok)
919 : {
920 : /*
921 : * Looks like {1, 2, 3, "four", {"woah, nesting", {6}}, SID(BA)}.
922 : */
923 54 : struct ace_condition_composite *c = &tok->data.composite;
924 43 : uint32_t i;
925 43 : bool ok;
926 54 : ok = sddl_write(ctx, "{");
927 54 : if (!ok) {
928 0 : return false;
929 : }
930 133 : for (i = 0; i < c->n_members; i++) {
931 79 : struct ace_condition_token *t = &c->tokens[i];
932 79 : if (i > 0) {
933 29 : ok = sddl_write(ctx, ", ");
934 29 : if (!ok) {
935 0 : return false;
936 : }
937 : }
938 79 : switch (t->type) {
939 27 : case CONDITIONAL_ACE_TOKEN_INT8:
940 : case CONDITIONAL_ACE_TOKEN_INT16:
941 : case CONDITIONAL_ACE_TOKEN_INT32:
942 : case CONDITIONAL_ACE_TOKEN_INT64:
943 27 : ok = sddl_write_int(ctx, t);
944 27 : break;
945 7 : case CONDITIONAL_ACE_TOKEN_UNICODE:
946 7 : ok = sddl_write_unicode(ctx, t);
947 7 : break;
948 0 : case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
949 0 : ok = sddl_write_octet_string(ctx, t);
950 0 : break;
951 45 : case CONDITIONAL_ACE_TOKEN_SID:
952 45 : ok = sddl_write_sid(ctx, t);
953 45 : break;
954 0 : case CONDITIONAL_ACE_TOKEN_COMPOSITE:
955 0 : return false;
956 0 : default:
957 0 : return false;
958 : }
959 79 : if (!ok) {
960 0 : return false;
961 : }
962 : }
963 54 : ok = sddl_write(ctx, "}");
964 54 : return ok;
965 : }
966 :
967 568 : static bool sddl_write_node(struct sddl_write_context *ctx,
968 : struct sddl_node *node)
969 : {
970 568 : struct ace_condition_token *tok = node->tok;
971 568 : switch (tok->type) {
972 54 : case CONDITIONAL_ACE_TOKEN_INT8:
973 : case CONDITIONAL_ACE_TOKEN_INT16:
974 : case CONDITIONAL_ACE_TOKEN_INT32:
975 : case CONDITIONAL_ACE_TOKEN_INT64:
976 54 : return sddl_write_int(ctx, tok);
977 :
978 241 : case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
979 : case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
980 : case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
981 : case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
982 : case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
983 : case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
984 : case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
985 : case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
986 : case CONDITIONAL_ACE_TOKEN_EQUAL:
987 : case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
988 : case CONDITIONAL_ACE_TOKEN_LESS_THAN:
989 : case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
990 : case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
991 : case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
992 : case CONDITIONAL_ACE_TOKEN_CONTAINS:
993 : case CONDITIONAL_ACE_TOKEN_ANY_OF:
994 : case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
995 : case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
996 : case CONDITIONAL_ACE_TOKEN_AND:
997 : case CONDITIONAL_ACE_TOKEN_OR:
998 : case CONDITIONAL_ACE_TOKEN_EXISTS:
999 : case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
1000 : case CONDITIONAL_ACE_TOKEN_NOT:
1001 241 : return sddl_write(ctx, sddl_strings[tok->type].name);
1002 :
1003 179 : case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
1004 : case CONDITIONAL_ACE_USER_ATTRIBUTE:
1005 : case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
1006 : case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
1007 179 : return sddl_write_attr(ctx, tok);
1008 :
1009 23 : case CONDITIONAL_ACE_TOKEN_UNICODE:
1010 23 : return sddl_write_unicode(ctx, tok);
1011 :
1012 7 : case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
1013 7 : return sddl_write_octet_string(ctx, tok);
1014 :
1015 10 : case CONDITIONAL_ACE_TOKEN_SID:
1016 10 : return sddl_write_sid(ctx, tok);
1017 :
1018 54 : case CONDITIONAL_ACE_TOKEN_COMPOSITE:
1019 54 : return sddl_write_composite(ctx, tok);
1020 :
1021 0 : case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING:
1022 : /*
1023 : * This is only expected at the very end, which we
1024 : * can't (and don't need to) check here, but we can at
1025 : * least ensure it's the end of a sub-expression.
1026 : */
1027 0 : return (node->rhs == NULL);
1028 0 : default:
1029 0 : return false;
1030 : }
1031 : /* not expecting to get here */
1032 : return false;
1033 : }
1034 :
1035 :
1036 667 : static inline bool sddl_wants_outer_parens(struct sddl_node *node)
1037 : {
1038 : /*
1039 : * Binary ops (having a LHS) are always parenthesised "(a == 2)"
1040 : *
1041 : * Member-of ops are too, for some reason.
1042 : */
1043 1130 : return (node->lhs != NULL ||
1044 463 : node->tok->type == CONDITIONAL_ACE_TOKEN_MEMBER_OF ||
1045 26 : node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF ||
1046 26 : node->tok->type == CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY ||
1047 21 : node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY ||
1048 21 : node->tok->type == CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF ||
1049 21 : node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF ||
1050 1350 : node->tok->type == CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY ||
1051 21 : node->tok->type == CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY);
1052 : }
1053 :
1054 :
1055 406 : static inline bool sddl_wants_inner_parens(struct sddl_node *node,
1056 : struct sddl_node *child)
1057 : {
1058 : /*
1059 : * logical operators are serialised with parentheses around their
1060 : * arguments (for NOT it is obligatory).
1061 : */
1062 406 : if (node->tok->type != CONDITIONAL_ACE_TOKEN_NOT &&
1063 345 : node->tok->type != CONDITIONAL_ACE_TOKEN_AND &&
1064 21 : node->tok->type != CONDITIONAL_ACE_TOKEN_OR) {
1065 21 : return false;
1066 : }
1067 159 : if (sddl_wants_outer_parens(child)) {
1068 56 : return false;
1069 : }
1070 0 : return true;
1071 : }
1072 :
1073 :
1074 343 : static void sddl_tree_resolve_parens(struct sddl_node *node)
1075 : {
1076 939 : if (sddl_wants_outer_parens(node)) {
1077 216 : node->wants_parens = true;
1078 : }
1079 568 : if (node->lhs != NULL) {
1080 165 : bool p = sddl_wants_inner_parens(node, node->lhs);
1081 165 : node->lhs->wants_parens = p;
1082 165 : sddl_tree_resolve_parens(node->lhs);
1083 : }
1084 568 : if (node->rhs != NULL) {
1085 241 : bool p = sddl_wants_inner_parens(node, node->rhs);
1086 241 : node->rhs->wants_parens = p;
1087 241 : sddl_tree_resolve_parens(node->rhs);
1088 : }
1089 343 : }
1090 :
1091 568 : static bool sddl_tree_to_sddl(struct sddl_write_context *ctx,
1092 : struct sddl_node *node)
1093 : {
1094 531 : bool ok;
1095 568 : if (node->wants_parens) {
1096 265 : ok = sddl_write(ctx, "(");
1097 265 : if (! ok) {
1098 0 : return false;
1099 : }
1100 : }
1101 :
1102 568 : if (node->lhs != NULL) {
1103 165 : ok = sddl_tree_to_sddl(ctx, node->lhs);
1104 165 : if (! ok) {
1105 0 : return false;
1106 : }
1107 165 : ok = sddl_write(ctx, " ");
1108 165 : if (!ok) {
1109 0 : return false;
1110 : }
1111 : }
1112 :
1113 568 : ok = sddl_write_node(ctx, node);
1114 568 : if (!ok) {
1115 0 : return false;
1116 : }
1117 568 : if (node->rhs != NULL) {
1118 : /* NOT is a special case: "!(x)", not "! (x)" */
1119 241 : if (node->tok->type != CONDITIONAL_ACE_TOKEN_NOT) {
1120 218 : ok = sddl_write(ctx, " ");
1121 218 : if (!ok) {
1122 0 : return false;
1123 : }
1124 : }
1125 :
1126 241 : ok = sddl_tree_to_sddl(ctx, node->rhs);
1127 241 : if (! ok) {
1128 0 : return false;
1129 : }
1130 : }
1131 568 : if (node->wants_parens) {
1132 265 : ok = sddl_write(ctx, ")");
1133 265 : if (!ok) {
1134 0 : return false;
1135 : }
1136 : }
1137 37 : return true;
1138 : }
1139 :
1140 : /*
1141 : * Convert conditional ACE conditions into SDDL conditions.
1142 : *
1143 : * @param mem_ctx
1144 : * @param program
1145 : * @return a string or NULL on error.
1146 : */
1147 162 : char *sddl_from_conditional_ace(TALLOC_CTX *mem_ctx,
1148 : struct ace_condition_script *program)
1149 : {
1150 146 : size_t i;
1151 162 : char *sddl = NULL;
1152 162 : struct sddl_node *nodes = NULL;
1153 162 : struct sddl_node **trees = NULL;
1154 162 : size_t n_trees = 0;
1155 162 : struct ace_condition_token *tok = NULL;
1156 146 : struct sddl_data s;
1157 146 : bool ok;
1158 162 : struct sddl_write_context ctx = {
1159 : .mem_ctx = mem_ctx
1160 : };
1161 :
1162 162 : if (program->length == 0) {
1163 : /*
1164 : * The empty program is a special case.
1165 : */
1166 0 : return talloc_strdup(mem_ctx, "()");
1167 : }
1168 162 : nodes = talloc_zero_array(mem_ctx,
1169 : struct sddl_node,
1170 : program->length);
1171 162 : if (nodes == NULL) {
1172 0 : talloc_free(sddl);
1173 0 : return NULL;
1174 : }
1175 162 : trees = talloc_array(mem_ctx,
1176 : struct sddl_node*,
1177 : program->length);
1178 162 : if (trees == NULL) {
1179 0 : talloc_free(sddl);
1180 0 : talloc_free(nodes);
1181 0 : return NULL;
1182 : }
1183 :
1184 : /*
1185 : * This loop constructs a tree, which we then traverse to get the
1186 : * SDDL. Consider this transformation:
1187 : *
1188 : * {A, B, ==, C, D, ==, &&} => "((A == B) && (C == D))"
1189 : *
1190 : * We keep an array of sub-trees, and add to it in sequence. When the
1191 : * thing we're adding takes arguments, we pop those off the tree list.
1192 : * So it would go through this sequence:
1193 : *
1194 : * len items
1195 : * 1: A
1196 : * 2: A, B
1197 : * 1: ==(A, B)
1198 : * 2: ==(A, B), C
1199 : * 3: ==(A, B), C, D
1200 : * 2: ==(A, B), ==(C, D)
1201 : * 1 &&(==(A, B), ==(C, D))
1202 : *
1203 : * Without building a tree it would be difficult to know how many
1204 : * parentheses to put before A.
1205 : *
1206 : * (A == B == C) should become
1207 : * {A B == C ==} which should be the same as
1208 : * ((A == B) == C)
1209 : */
1210 :
1211 730 : for (i = 0; i < program->length; i++) {
1212 568 : tok = &program->tokens[i];
1213 568 : s = sddl_strings[tok->type];
1214 568 : nodes[i].tok = tok;
1215 568 : if (s.nargs > n_trees) {
1216 0 : goto error;
1217 : }
1218 568 : if (s.nargs >= 1) {
1219 : /*
1220 : * Read this note if you're trying to follow
1221 : * [MS-DTYP]. MS-DTYP uses 'LHS' to describe the
1222 : * operand of unary operators even though they are
1223 : * always displayed on the right of the operator. It
1224 : * makes everything much simpler to use rhs
1225 : * instead.
1226 : */
1227 241 : n_trees--;
1228 241 : nodes[i].rhs = trees[n_trees];
1229 :
1230 241 : if (s.nargs == 2) {
1231 165 : n_trees--;
1232 165 : nodes[i].lhs = trees[n_trees];
1233 : }
1234 : }
1235 568 : trees[n_trees] = &nodes[i];
1236 568 : n_trees++;
1237 : }
1238 :
1239 162 : if (n_trees != 1) {
1240 0 : goto error;
1241 : }
1242 :
1243 : /*
1244 : * First we walk the tree to work out where to put parentheses (to
1245 : * match the canonical Windows representation).
1246 : *
1247 : * Doing it in the same traverse as the writing would be possible but
1248 : * trickier to get right.
1249 : */
1250 162 : sddl_tree_resolve_parens(trees[0]);
1251 162 : trees[0]->wants_parens = true;
1252 :
1253 : /*
1254 : * Clamber over the tree, writing the string.
1255 : */
1256 162 : ok = sddl_tree_to_sddl(&ctx, trees[0]);
1257 :
1258 162 : if (! ok) {
1259 0 : goto error;
1260 : }
1261 :
1262 162 : talloc_free(trees);
1263 162 : talloc_free(nodes);
1264 162 : return ctx.sddl;
1265 :
1266 0 : error:
1267 0 : talloc_free(sddl);
1268 0 : talloc_free(trees);
1269 0 : talloc_free(nodes);
1270 0 : return NULL;
1271 : }
1272 :
1273 :
1274 :
1275 : static void comp_error(struct ace_condition_sddl_compiler_context *comp,
1276 : const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
1277 :
1278 43 : static void comp_error(struct ace_condition_sddl_compiler_context *comp,
1279 : const char *fmt, ...)
1280 : {
1281 43 : char *msg = NULL;
1282 39 : va_list ap;
1283 43 : va_start(ap, fmt);
1284 43 : msg = talloc_vasprintf(comp->mem_ctx, fmt, ap);
1285 43 : va_end(ap);
1286 43 : if (msg == NULL) {
1287 0 : goto fail;
1288 : }
1289 :
1290 43 : if (comp->message == NULL) {
1291 : /*
1292 : * Previously unset message; prepend the position.
1293 : *
1294 : * This is the common case.
1295 : */
1296 43 : comp->message_offset = comp->offset;
1297 43 : comp->message = msg;
1298 43 : return;
1299 : }
1300 : /*
1301 : * There's a message already so we'll try to append.
1302 : * This is unlikely to happen.
1303 : */
1304 0 : comp->message = talloc_asprintf(comp->mem_ctx,
1305 : "%s AND THEN %s",
1306 : comp->message,
1307 : msg);
1308 0 : TALLOC_FREE(msg);
1309 0 : if (comp->message == NULL) {
1310 0 : goto fail;
1311 : }
1312 0 : DBG_NOTICE("%s\n", comp->message);
1313 0 : return;
1314 0 : fail:
1315 0 : comp->message = talloc_strdup(comp->mem_ctx,
1316 : "failed to set error message");
1317 0 : DBG_WARNING("%s\n", comp->message);
1318 : }
1319 :
1320 :
1321 :
1322 :
1323 : /*
1324 : conditional-ace = "(" conditional-ace-type ";" [ace-flag-string] ";" ace-rights
1325 : ";" [object- guid] ";" [inherit-object-guid] ";" sid-string ";" "(" cond-expr
1326 : ")" ")"
1327 :
1328 : wspace = 1*(%x09-0D / %x20)
1329 :
1330 : literal-SID = "SID(" sid-string ")"
1331 :
1332 : term = [wspace] (memberof-op / exists-op / rel-op / contains-op / anyof-op /
1333 : attr-name / rel- op2) [wspace]
1334 :
1335 : cond-expr = term / term [wspace] ("||" / "&&" ) [wspace] cond-expr / (["!"]
1336 : [wspace] "(" cond-expr ")")
1337 :
1338 : memberof-op = ( "Member_of" / "Not_Member_of" / "Member_of_Any" /
1339 : "Not_Member_of_Any" / "Device_Member_of" / "Device_Member_of_Any" /
1340 : "Not_Device_Member_of" / "Not_Device_Member_of_Any" ) wspace sid-array
1341 :
1342 : exists-op = ( "Exists" / "Not_Exists") wspace attr-name
1343 :
1344 : rel-op = attr-name [wspace] ("<" / "<=" / ">" / ">=") [wspace] (attr-name2 /
1345 : value) ; only scalars
1346 :
1347 : rel-op2 = attr-name [wspace] ("==" / "!=") [wspace] ( attr-name2 / value-array )
1348 : ; scalar or list
1349 :
1350 : contains-op = attr-name wspace ("Contains" / "Not_Contains") wspace (attr-name2
1351 : / value- array)
1352 :
1353 : anyof-op = attr-name wspace ("Any_of" / "Not_Any_of") wspace (attr-name2 /
1354 : value-array)
1355 :
1356 :
1357 : attr-name1 = attr-char1 *(attr-char1 / "@")
1358 :
1359 : attr-char1 = 1*(ALPHA / DIGIT / ":" / "." / "/" / "_")
1360 :
1361 :
1362 :
1363 : attr-name2 = ("@user." / "@device." / "@resource.") 1*attr-char2
1364 : ; new prefixed name form
1365 : attr-char2 = attr-char1 / lit-char
1366 : attr-name = attr-name1 / attr-name2
1367 : */
1368 :
1369 :
1370 :
1371 134966 : static inline bool is_wspace(uint8_t c)
1372 : {
1373 : /* wspace := %x09-0D | %x20 */
1374 100269 : return (c == ' ' || c == '\x09' || c == '\x0A' ||
1375 215196 : c == '\x0B' || c == '\x0C' || c == '\x0D');
1376 : }
1377 :
1378 15041 : static inline bool is_attr_char1(uint8_t c)
1379 : {
1380 : /*
1381 : * attr-char1 = 1*(ALPHA / DIGIT / ":" / "." / "/" / "_")
1382 : * (ALPHA and DIGIT being ASCII only).
1383 : *
1384 : * These are used for local attributes, which we don't really
1385 : * expect to see in Samba AD.
1386 : *
1387 : * One example is "WIN://SYSAPPID", which is used in conditional ACEs
1388 : * that seem to relate to software installers; another is
1389 : * "APPID://PATH", used by Windows Applocker.
1390 : */
1391 15041 : return (((c >= 'a') && (c <= 'z')) ||
1392 2303 : ((c >= 'A') && (c <= 'Z')) ||
1393 1275 : ((c >= '0') && (c <= '9')) ||
1394 20207 : c == ':' || c == '.' || c == '/' || c == '_');
1395 : }
1396 :
1397 :
1398 1691 : static ssize_t read_attr2_string(
1399 : struct ace_condition_sddl_compiler_context *comp,
1400 : struct ace_condition_unicode *dest)
1401 : {
1402 : /*
1403 : * our SDDL is utf-8, but we need to convert to utf-16 and
1404 : * parse the escapes, then back to utf-8, because that's how
1405 : * the claims will appear.
1406 : *
1407 : * attr_char2 is used for attribute names that follow "@Class."
1408 : * specifiers. They can consume 5 characters to specify a single code
1409 : * unit, using "%1234" style escapes. Certain characters must be
1410 : * encoded this way, while others must be literal values. Because the
1411 : * %1234 refers to a utf-16 code unit, we really need to do the work
1412 : * in that codespace.
1413 : */
1414 1057 : bool ok;
1415 1691 : uint16_t *utf16 = NULL;
1416 1057 : size_t utf16_byte_len;
1417 1057 : size_t utf16_chars;
1418 1057 : size_t utf8_len;
1419 1057 : size_t src_len;
1420 1057 : ssize_t i, j;
1421 1691 : ssize_t max_len = comp->length - comp->offset;
1422 1691 : const uint8_t *src = comp->sddl + comp->offset;
1423 :
1424 95921 : for (i = 0; i < max_len; i++) {
1425 95921 : uint8_t c = src[i];
1426 : /*
1427 : * A double‐byte that must be escaped but isn't tells us that
1428 : * the attribute name has ended.
1429 : *
1430 : * The exception is '%', which must also be escaped
1431 : * (as "%0025"), but is obviously still expected in
1432 : * the escaped string.
1433 : */
1434 115960 : if (strchr("!&()><=| \"", c) != NULL || is_wspace(c)) {
1435 : break;
1436 : }
1437 : }
1438 1691 : if (i == max_len) {
1439 : /* too long, because we need at least one ')' */
1440 0 : comp_error(comp, "interminable attribute name");
1441 0 : return -1;
1442 : }
1443 1691 : if (i == 0) {
1444 : /* too short! like "User.>= 4" */
1445 0 : comp_error(comp, "empty attribute name");
1446 0 : return -1;
1447 : }
1448 :
1449 1691 : if (unlikely(i > CONDITIONAL_ACE_MAX_LENGTH)) {
1450 : /*
1451 : * This is imprecise; the limit for the whole ACL is 64k.
1452 : * However there could be many escapes in the SDDL name which
1453 : * would reduce down to single utf16 code units in the
1454 : * compiled string.
1455 : */
1456 0 : comp_error(comp, "attribute is way too long (%zu)", i);
1457 0 : return -1;
1458 : }
1459 :
1460 1691 : src_len = i;
1461 :
1462 1691 : ok = convert_string_talloc(comp->mem_ctx,
1463 : CH_UTF8, CH_UTF16LE,
1464 : src, src_len,
1465 : &utf16, &utf16_byte_len);
1466 1691 : if (!ok) {
1467 0 : comp_error(comp, "could not convert to utf-16");
1468 0 : return -1;
1469 : }
1470 : /*
1471 : * utf16_byte_len is in bytes, we want to count uint16s.
1472 : */
1473 1691 : utf16_chars = utf16_byte_len / 2;
1474 :
1475 : /* now the escapes. */
1476 1691 : for (i = 0, j = 0;
1477 65889 : j < utf16_chars && i < utf16_chars;
1478 64198 : j++) {
1479 64201 : uint16_t c = utf16[i];
1480 64201 : if (c == '%') {
1481 1513 : uint16_t v = 0;
1482 1513 : size_t end = i + 5;
1483 : /*
1484 : * we need to read 4 hex characters.
1485 : * hex_byte() won't help because that is 8-bit.
1486 : */
1487 1513 : if (end > utf16_chars) {
1488 0 : comp_error(comp,
1489 : "insufficient room for %% escape");
1490 0 : talloc_free(utf16);
1491 0 : return -1;
1492 : }
1493 7565 : for (i++; i < end; i++) {
1494 6052 : v <<= 4;
1495 6052 : c = utf16[i];
1496 6052 : if (c >= '0' && c <= '9') {
1497 5633 : v += c - '0';
1498 419 : } else if (c >= 'A' && c <= 'F') {
1499 0 : v += c - 'A' + 10;
1500 419 : } else if (c >= 'a' && c <= 'f') {
1501 419 : v += c - 'a' + 10;
1502 : } else {
1503 0 : comp_error(comp, "invalid %% escape");
1504 0 : talloc_free(utf16);
1505 0 : return -1;
1506 : }
1507 : }
1508 : /*
1509 : * from MS-DTYP 2.5.1.1 Syntax (text, not ABNF), some
1510 : * characters must be literals, not escaped.
1511 : */
1512 1513 : if ((v >= '0' && v <= '9') ||
1513 1513 : (v >= 'A' && v <= 'Z') ||
1514 1513 : (v >= 'a' && v <= 'z') ||
1515 1447 : (v < 127 &&
1516 1447 : strchr("#$'*+-;?@[\\]^_`{}~:/.", v) != NULL)) {
1517 3 : comp_error(comp, "invalid %% escape: "
1518 : "'%%%04x' should be literal '%c'",
1519 : v, v);
1520 3 : talloc_free(utf16);
1521 3 : return -1;
1522 : }
1523 1510 : utf16[j] = v;
1524 1510 : continue;
1525 : }
1526 : /*
1527 : * Note the characters "!&()><=|% \"" must be escaped per
1528 : * [MS-DTYP], but as we found the bounds of this string using
1529 : * those in utf-8 at the top of this function, we are not
1530 : * going to find them in the utf-16 now.
1531 : *
1532 : * Also, per [MS-DTYP], un-escaped whitespace is allowed, but
1533 : * effectively disallowed by Samba.
1534 : */
1535 62688 : utf16[j] = utf16[i];
1536 62688 : i++;
1537 : }
1538 :
1539 2742 : ok = convert_string_talloc(comp->mem_ctx,
1540 : CH_UTF16LE, CH_UTF8,
1541 1688 : utf16, j * 2,
1542 1688 : &dest->value, &utf8_len);
1543 1688 : TALLOC_FREE(utf16);
1544 1688 : if (!ok) {
1545 0 : comp_error(comp, "could not convert to utf-16");
1546 0 : return -1;
1547 : }
1548 :
1549 : /* returning bytes consumed, not necessarily the length of token */
1550 634 : return src_len;
1551 : }
1552 :
1553 :
1554 :
1555 31809 : static bool eat_whitespace(struct ace_condition_sddl_compiler_context *comp,
1556 : bool trailing)
1557 : {
1558 : /*
1559 : * Advance the offset to the first non-whitespace character.
1560 : *
1561 : * If trailing is false, there has to be something before the end of
1562 : * the string.
1563 : */
1564 40817 : while (comp->offset < comp->length) {
1565 48542 : if (! is_wspace(comp->sddl[comp->offset])) {
1566 6039 : break;
1567 : }
1568 9008 : comp->offset++;
1569 : }
1570 31809 : if ((!trailing) && comp->offset == comp->length) {
1571 0 : comp_error(comp, "input ends unexpectedly");
1572 0 : return false;
1573 : }
1574 6039 : return true;
1575 : }
1576 :
1577 : static bool pop_sddl_token(struct ace_condition_sddl_compiler_context *comp,
1578 : struct ace_condition_token *token);
1579 :
1580 : static bool write_sddl_token(struct ace_condition_sddl_compiler_context *comp,
1581 : struct ace_condition_token token);
1582 :
1583 : static bool pop_write_sddl_token(
1584 : struct ace_condition_sddl_compiler_context *comp);
1585 :
1586 :
1587 7098 : static bool flush_stack_tokens(struct ace_condition_sddl_compiler_context *comp,
1588 : uint8_t type)
1589 : {
1590 4795 : bool ok;
1591 7098 : uint8_t precedence = sddl_strings[type].op_precedence;
1592 7098 : if (precedence == SDDL_PRECEDENCE_PAREN_START) {
1593 : /* paren has a special role */
1594 795 : return true;
1595 : }
1596 : /*
1597 : * Any operators on the top of the stack that have a "higher"
1598 : * precedence (tighter binding) to this one get popped off and written
1599 : * to the output. "higher" is in quotes because it means lower enum
1600 : * value.
1601 : *
1602 : * This works for binary operators, for example, with "(a == b == c)"
1603 : * (which is equivalent to "((a == b) == c)" via the left-to-right
1604 : * rule), we have:
1605 : * TOKEN dest PROGRAM STACK
1606 : * (
1607 : * a p
1608 : * == s a
1609 : * b p a ==
1610 : * == s a b ==
1611 : * flush stack
1612 : * s->p a b == ==
1613 : * c p a b ==
1614 : * ) a b == c ==
1615 : * flush stack
1616 : * a b == c ==
1617 : *
1618 : * but it is not right for unary operators, as in "(!(!(Exists
1619 : * a)))". As it turns out though, >= works for the unary
1620 : * operators and syntactic rules we have.
1621 : */
1622 6834 : while (comp->stack_depth > 0) {
1623 6834 : struct ace_condition_token *op =
1624 6834 : &comp->stack[comp->stack_depth - 1];
1625 6834 : if(sddl_strings[op->type].op_precedence > precedence) {
1626 1508 : break;
1627 : }
1628 2193 : if(sddl_strings[op->type].op_precedence == precedence &&
1629 9 : sddl_strings[op->type].flags & SDDL_FLAG_IS_UNARY_OP) {
1630 0 : break;
1631 : }
1632 :
1633 2193 : ok = pop_write_sddl_token(comp);
1634 2193 : if (! ok) {
1635 0 : comp_error(comp,
1636 : "could not flush '%s' to program",
1637 0 : sddl_strings[op->type].name);
1638 0 : return false;
1639 : }
1640 : }
1641 1508 : return true;
1642 : }
1643 :
1644 4680 : static bool push_sddl_token(struct ace_condition_sddl_compiler_context *comp,
1645 : struct ace_condition_token token)
1646 : {
1647 4680 : if (comp->stack_depth >= CONDITIONAL_ACE_MAX_TOKENS - 1) {
1648 0 : comp_error(comp, "excessive recursion");
1649 0 : return false;
1650 : }
1651 4680 : if (sddl_strings[token.type].op_precedence == SDDL_NOT_AN_OP) {
1652 0 : comp_error(comp,
1653 : "wrong kind of token for the SDDL stack: %s",
1654 0 : sddl_strings[token.type].name);
1655 0 : return false;
1656 : }
1657 : /*
1658 : * Any operators on the top of the stack that have a "greater" or
1659 : * equal precedence to this one get popped off and written to the
1660 : * output.
1661 : */
1662 4680 : flush_stack_tokens(comp, token.type);
1663 :
1664 4680 : token.data.op.sddl_position = comp->offset;
1665 :
1666 4680 : comp->stack[comp->stack_depth] = token;
1667 4680 : comp->stack_depth++;
1668 4680 : if (token.type != CONDITIONAL_ACE_SAMBA_SDDL_PAREN) {
1669 2223 : comp->last_token_type = token.type;
1670 : }
1671 1512 : return true;
1672 : }
1673 :
1674 2193 : static bool pop_sddl_token(struct ace_condition_sddl_compiler_context *comp,
1675 : struct ace_condition_token *token)
1676 : {
1677 2193 : if (comp->stack_depth == 0) {
1678 0 : comp_error(comp, "misbalanced expression");
1679 0 : return false;
1680 : }
1681 2193 : comp->stack_depth--;
1682 2193 : *token = comp->stack[comp->stack_depth];
1683 2193 : return true;
1684 : }
1685 :
1686 :
1687 13023 : static bool write_sddl_token(struct ace_condition_sddl_compiler_context *comp,
1688 : struct ace_condition_token token)
1689 : {
1690 : /*
1691 : * This is adding a token to the program. Normally it will be to the
1692 : * main program list, but if we are constructing a composite list, then
1693 : * will be redirected there (via comp->target).
1694 : *
1695 : * We also conservatively track the overall size, so we don't waste
1696 : * time compiling something that is way too big.
1697 : */
1698 13023 : DBG_INFO("writing %"PRIu32" %x %s\n",
1699 : *comp->target_len,
1700 : token.type,
1701 : sddl_strings[token.type].name);
1702 13023 : comp->approx_size++;
1703 13023 : if (comp->approx_size > CONDITIONAL_ACE_MAX_TOKENS) {
1704 0 : comp_error(comp, "program is too long "
1705 : "(over %d tokens)",
1706 : CONDITIONAL_ACE_MAX_TOKENS);
1707 0 : return false;
1708 : }
1709 13023 : if (token.type != CONDITIONAL_ACE_SAMBA_SDDL_PAREN) {
1710 13023 : comp->last_token_type = token.type;
1711 : }
1712 13023 : comp->target[*comp->target_len] = token;
1713 13023 : (*comp->target_len)++;
1714 13023 : return true;
1715 : }
1716 :
1717 2193 : static bool pop_write_sddl_token(
1718 : struct ace_condition_sddl_compiler_context *comp)
1719 : {
1720 1479 : bool ok;
1721 2193 : struct ace_condition_token token = {};
1722 2193 : ok = pop_sddl_token(comp, &token);
1723 2193 : if (!ok) {
1724 0 : comp_error(comp, "could not pop from op stack");
1725 0 : return false;
1726 : }
1727 2193 : if (comp->target != comp->program->tokens) {
1728 0 : comp_error(comp, "compiler is seriously confused");
1729 0 : return false;
1730 : }
1731 :
1732 2193 : ok = write_sddl_token(comp, token);
1733 2193 : if (!ok) {
1734 0 : comp_error(comp,
1735 : "could not write '%s' to program",
1736 0 : sddl_strings[token.type].name);
1737 0 : return false;
1738 : }
1739 2193 : DBG_INFO(" written '%s'\n", sddl_strings[token.type].name);
1740 714 : return true;
1741 : }
1742 :
1743 :
1744 :
1745 : static bool parse_expression(struct ace_condition_sddl_compiler_context *comp);
1746 : static bool parse_composite(struct ace_condition_sddl_compiler_context *comp);
1747 :
1748 :
1749 :
1750 :
1751 1393 : static bool parse_oppy_op(struct ace_condition_sddl_compiler_context *comp)
1752 : {
1753 : /*
1754 : * These ones look like operators and are operators.
1755 : */
1756 1026 : bool ok;
1757 1393 : struct ace_condition_token token = {};
1758 1026 : uint8_t c, d;
1759 1393 : uint32_t flag = SDDL_FLAG_EXPECTING_BINARY_OP;
1760 :
1761 1393 : if (comp->offset + 1 >= comp->length) {
1762 0 : comp_error(comp, "syntax error");
1763 0 : return false;
1764 : }
1765 :
1766 1393 : token.data.sddl_op.start = comp->offset;
1767 :
1768 : /*
1769 : * These are all one or two characters long, and we always have room
1770 : * to peek ahead.
1771 : */
1772 1393 : c = comp->sddl[comp->offset];
1773 1393 : d = comp->sddl[comp->offset + 1];
1774 :
1775 1393 : if (c == '!') {
1776 213 : if (d == '=') {
1777 42 : comp->offset++;
1778 42 : token.type = CONDITIONAL_ACE_TOKEN_NOT_EQUAL;
1779 :
1780 : } else {
1781 171 : token.type = CONDITIONAL_ACE_TOKEN_NOT;
1782 171 : flag = SDDL_FLAG_EXPECTING_UNARY_OP;
1783 : }
1784 1180 : } else if (c == '=' && d == '=') {
1785 474 : comp->offset++;
1786 474 : token.type = CONDITIONAL_ACE_TOKEN_EQUAL;
1787 706 : } else if (c == '>') {
1788 73 : if (d == '=') {
1789 19 : comp->offset++;
1790 19 : token.type = CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL;
1791 :
1792 : } else {
1793 54 : token.type = CONDITIONAL_ACE_TOKEN_GREATER_THAN;
1794 : }
1795 633 : } else if (c == '<') {
1796 111 : if (d == '=') {
1797 10 : comp->offset++;
1798 10 : token.type = CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL;
1799 :
1800 : } else {
1801 101 : token.type = CONDITIONAL_ACE_TOKEN_LESS_THAN;
1802 : }
1803 522 : } else if (c == '&' && d == '&') {
1804 418 : comp->offset++;
1805 418 : token.type = CONDITIONAL_ACE_TOKEN_AND;
1806 418 : flag = SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP;
1807 104 : } else if (c == '|' && d == '|') {
1808 103 : comp->offset++;
1809 103 : token.type = CONDITIONAL_ACE_TOKEN_OR;
1810 103 : flag = SDDL_FLAG_EXPECTING_BINARY_LOGIC_OP;
1811 : } else {
1812 1 : comp_error(comp, "unknown operator");
1813 1 : return false;
1814 : }
1815 :
1816 1392 : if ((comp->state & flag) == 0) {
1817 5 : comp_error(comp, "unexpected operator");
1818 5 : return false;
1819 : }
1820 :
1821 1387 : comp->offset++;
1822 :
1823 1387 : ok = push_sddl_token(comp, token);
1824 1387 : if (!ok) {
1825 0 : return false;
1826 : }
1827 :
1828 1387 : ok = eat_whitespace(comp, true);
1829 1387 : return ok;
1830 : }
1831 :
1832 5595 : static bool parse_unicode(struct ace_condition_sddl_compiler_context *comp)
1833 : {
1834 : /*
1835 : * This looks like "hello" (including the double quotes).
1836 : *
1837 : * Fortunately (for now), there is no mechanism for escaping
1838 : * double quotes in conditional ace strings, so we can simply
1839 : * look for the second quote without worrying about things
1840 : * like «\\\"».
1841 : */
1842 5595 : struct ace_condition_token token = {};
1843 5595 : char *s = NULL;
1844 5595 : const uint8_t *src = NULL;
1845 5595 : char *utf16 = NULL;
1846 5369 : size_t len, max_len;
1847 5369 : bool ok;
1848 5595 : if (comp->sddl[comp->offset] != '"') {
1849 0 : comp_error(comp, "was expecting '\"' for Unicode string");
1850 0 : return false;
1851 : }
1852 5595 : comp->offset++;
1853 5595 : src = comp->sddl + comp->offset;
1854 5595 : max_len = comp->length - comp->offset;
1855 : /* strnchr */
1856 50100 : for (len = 0; len < max_len; len++) {
1857 50100 : if (src[len] == '"') {
1858 226 : break;
1859 : }
1860 : }
1861 5595 : if (len == max_len) {
1862 0 : comp_error(comp, "unterminated unicode string");
1863 0 : return false;
1864 : }
1865 :
1866 : /*
1867 : * Look, this is wasteful, but it probably doesn't matter. We want to
1868 : * check that the string we're putting into the descriptor is valid,
1869 : * or we'll see errors down the track.
1870 : */
1871 5595 : ok = convert_string_talloc(comp->mem_ctx,
1872 : CH_UTF8, CH_UTF16LE,
1873 : src, len,
1874 : &utf16, NULL);
1875 5595 : if (!ok) {
1876 0 : comp_error(comp, "not valid unicode");
1877 0 : return false;
1878 : }
1879 5595 : TALLOC_FREE(utf16);
1880 :
1881 5595 : s = talloc_array_size(comp->mem_ctx, 1, len + 1);
1882 5595 : if (s == NULL) {
1883 0 : comp_error(comp, "allocation error");
1884 0 : return false;
1885 : }
1886 5595 : memcpy(s, src, len);
1887 5595 : s[len] = 0;
1888 5595 : comp->offset += len + 1; /* +1 for the final quote */
1889 5595 : token.type = CONDITIONAL_ACE_TOKEN_UNICODE;
1890 5595 : token.data.unicode.value = s;
1891 :
1892 5595 : return write_sddl_token(comp, token);
1893 : }
1894 :
1895 :
1896 15 : static bool parse_octet_string(struct ace_condition_sddl_compiler_context *comp)
1897 : {
1898 : /*
1899 : * This looks like '#hhhh...', where each 'hh' is hex for a byte, with
1900 : * the weird and annoying complication that '#' can be used to mean
1901 : * '0'.
1902 : */
1903 15 : struct ace_condition_token token = {};
1904 15 : size_t length, i;
1905 :
1906 15 : if (comp->sddl[comp->offset] != '#') {
1907 0 : comp_error(comp, "was expecting '#' for octet string");
1908 0 : return false;
1909 : }
1910 15 : comp->offset++;
1911 15 : length = strspn((const char*)(comp->sddl + comp->offset),
1912 : "#0123456789abcdefABCDEF");
1913 :
1914 15 : if (length & 1) {
1915 2 : comp_error(comp, "octet string has odd number of hex digits");
1916 2 : return false;
1917 : }
1918 :
1919 13 : length /= 2;
1920 :
1921 13 : token.data.bytes = data_blob_talloc_zero(comp->mem_ctx, length);
1922 13 : token.type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
1923 :
1924 60 : for (i = 0; i < length; i++) {
1925 : /*
1926 : * Why not just strhex_to_str()?
1927 : *
1928 : * Because we need to treat '#' as '0' in octet string values,
1929 : * so all of the following are the same
1930 : * (equaling {0x10, 0x20, 0x30, 0x0}).
1931 : *
1932 : * #10203000
1933 : * #10203###
1934 : * #1#2#3###
1935 : * #10203#00
1936 : */
1937 47 : bool ok;
1938 47 : char pair[2];
1939 47 : size_t j = comp->offset + i * 2;
1940 47 : pair[0] = (comp->sddl[j] == '#') ? '0' : comp->sddl[j];
1941 47 : pair[1] = (comp->sddl[j + 1] == '#') ? '0' : comp->sddl[j + 1];
1942 :
1943 47 : ok = hex_byte(pair, &token.data.bytes.data[i]);
1944 47 : if (!ok) {
1945 0 : talloc_free(token.data.bytes.data);
1946 0 : comp_error(comp, "inexplicable error in octet string");
1947 0 : return false;
1948 : }
1949 : }
1950 13 : comp->offset += length * 2;
1951 13 : return write_sddl_token(comp, token);
1952 : }
1953 :
1954 :
1955 522 : static bool parse_ra_octet_string(struct ace_condition_sddl_compiler_context *comp)
1956 : {
1957 : /*
1958 : * Resource attribute octet strings resemble conditional ace octet
1959 : * strings, but have some important differences:
1960 : *
1961 : * 1. The '#' at the start is optional, and if present is
1962 : * counted as a zero.
1963 : *
1964 : * 2. An odd number of characters is implicitly left-padded with a zero.
1965 : *
1966 : * That is, "abc" means "0abc", "#12" means "0012", "f##"
1967 : * means "0f00", and "##" means 00.
1968 : */
1969 522 : struct ace_condition_token token = {};
1970 522 : size_t string_length, bytes_length, i, j;
1971 522 : bool ok;
1972 522 : char pair[2];
1973 :
1974 522 : string_length = strspn((const char*)(comp->sddl + comp->offset),
1975 : "#0123456789abcdefABCDEF");
1976 :
1977 522 : bytes_length = (string_length + 1) / 2;
1978 :
1979 522 : if (bytes_length == 0) {
1980 0 : comp_error(comp, "zero length octet bytes");
1981 0 : return false;
1982 : }
1983 :
1984 522 : token.data.bytes = data_blob_talloc_zero(comp->mem_ctx, bytes_length);
1985 522 : if (token.data.bytes.data == NULL) {
1986 0 : return false;
1987 : }
1988 522 : token.type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
1989 :
1990 522 : j = comp->offset;
1991 522 : i = 0;
1992 522 : if (string_length & 1) {
1993 : /*
1994 : * An odd number of characters means the first
1995 : * character gains an implicit 0 for the high nybble.
1996 : */
1997 0 : pair[0] = 0;
1998 0 : pair[1] = (comp->sddl[0] == '#') ? '0' : comp->sddl[0];
1999 :
2000 0 : ok = hex_byte(pair, &token.data.bytes.data[i]);
2001 0 : if (!ok) {
2002 0 : goto fail;
2003 : }
2004 0 : j++;
2005 0 : i++;
2006 : }
2007 :
2008 1602 : for (; i < bytes_length; i++) {
2009 : /*
2010 : * Why not just strhex_to_str() ?
2011 : *
2012 : * Because we need to treat '#' as '0' in octet string values.
2013 : */
2014 1080 : if (comp->length - j < 2) {
2015 0 : goto fail;
2016 : }
2017 :
2018 1080 : pair[0] = (comp->sddl[j] == '#') ? '0' : comp->sddl[j];
2019 1080 : pair[1] = (comp->sddl[j + 1] == '#') ? '0' : comp->sddl[j + 1];
2020 :
2021 1080 : ok = hex_byte(pair, &token.data.bytes.data[i]);
2022 1080 : if (!ok) {
2023 0 : goto fail;
2024 : }
2025 1080 : j += 2;
2026 : }
2027 522 : comp->offset = j;
2028 522 : return write_sddl_token(comp, token);
2029 :
2030 0 : fail:
2031 0 : comp_error(comp, "inexplicable error in octet string");
2032 0 : talloc_free(token.data.bytes.data);
2033 0 : return false;
2034 : }
2035 :
2036 :
2037 796 : static bool parse_sid(struct ace_condition_sddl_compiler_context *comp)
2038 : {
2039 796 : struct dom_sid *sid = NULL;
2040 796 : const uint8_t *sidstr = NULL;
2041 796 : struct ace_condition_token token = {};
2042 498 : size_t end;
2043 796 : if (comp->length - comp->offset < 7) {
2044 : /* minimum: "SID(AA)" */
2045 1 : comp_error(comp, "no room for a complete SID");
2046 1 : return false;
2047 : }
2048 : /* conditional ACE SID string */
2049 795 : if (comp->sddl[comp->offset ] != 'S' ||
2050 795 : comp->sddl[comp->offset + 1] != 'I' ||
2051 795 : comp->sddl[comp->offset + 2] != 'D' ||
2052 795 : comp->sddl[comp->offset + 3] != '(') {
2053 0 : comp_error(comp, "malformed SID() constructor");
2054 0 : return false;
2055 : } else {
2056 795 : comp->offset += 4;
2057 : }
2058 :
2059 795 : sidstr = comp->sddl + comp->offset;
2060 :
2061 1292 : sid = sddl_decode_sid(comp->mem_ctx,
2062 : (const char **)&sidstr,
2063 795 : comp->domain_sid);
2064 :
2065 795 : if (sid == NULL) {
2066 2 : comp_error(comp, "could not parse SID");
2067 2 : return false;
2068 : }
2069 793 : end = sidstr - comp->sddl;
2070 793 : if (end >= comp->length || end < comp->offset) {
2071 0 : comp_error(comp, "apparent overflow in SID parsing");
2072 0 : return false;
2073 : }
2074 793 : comp->offset = end;
2075 : /*
2076 : * offset is now at the end of the SID, but we need to account
2077 : * for the ')'.
2078 : */
2079 793 : if (comp->sddl[comp->offset] != ')') {
2080 0 : comp_error(comp, "expected ')' to follow SID");
2081 0 : return false;
2082 : }
2083 793 : comp->offset++;
2084 :
2085 793 : token.type = CONDITIONAL_ACE_TOKEN_SID;
2086 793 : token.data.sid.sid = *sid;
2087 793 : return write_sddl_token(comp, token);
2088 : }
2089 :
2090 :
2091 :
2092 0 : static bool parse_ra_sid(struct ace_condition_sddl_compiler_context *comp)
2093 : {
2094 0 : struct dom_sid *sid = NULL;
2095 0 : const uint8_t *sidstr = NULL;
2096 0 : struct ace_condition_token token = {};
2097 0 : size_t end;
2098 :
2099 0 : if ((comp->state & SDDL_FLAG_EXPECTING_LITERAL) == 0) {
2100 0 : comp_error(comp, "did not expect a SID here");
2101 0 : return false;
2102 : }
2103 : /*
2104 : * Here we are parsing a resource attribute ACE which doesn't
2105 : * have the SID() wrapper around the SID string (unlike a
2106 : * conditional ACE).
2107 : *
2108 : * The resource ACE doesn't need this because there is no
2109 : * ambiguity with local attribute names, besides which the
2110 : * type has already been specified earlier in the ACE.
2111 : */
2112 0 : if (comp->length - comp->offset < 2){
2113 0 : comp_error(comp, "no room for a complete SID");
2114 0 : return false;
2115 : }
2116 :
2117 0 : sidstr = comp->sddl + comp->offset;
2118 :
2119 0 : sid = sddl_decode_sid(comp->mem_ctx,
2120 : (const char **)&sidstr,
2121 0 : comp->domain_sid);
2122 :
2123 0 : if (sid == NULL) {
2124 0 : comp_error(comp, "could not parse SID");
2125 0 : return false;
2126 : }
2127 0 : end = sidstr - comp->sddl;
2128 0 : if (end >= comp->length || end < comp->offset) {
2129 0 : comp_error(comp, "apparent overflow in SID parsing");
2130 0 : return false;
2131 : }
2132 0 : comp->offset = end;
2133 0 : token.type = CONDITIONAL_ACE_TOKEN_SID;
2134 0 : token.data.sid.sid = *sid;
2135 0 : return write_sddl_token(comp, token);
2136 : }
2137 :
2138 :
2139 1235 : static bool parse_int(struct ace_condition_sddl_compiler_context *comp)
2140 : {
2141 : /*
2142 : * This one is relatively simple. strtoll() does the work.
2143 : */
2144 1163 : long long v;
2145 1235 : struct ace_condition_token token = {};
2146 1235 : const char *start = (const char *)comp->sddl + comp->offset;
2147 1235 : char *end = NULL;
2148 1235 : const char *first_digit = start;
2149 1163 : size_t len;
2150 1235 : errno = 0;
2151 1235 : v = strtoll(start, &end, 0);
2152 1235 : if (errno != 0) {
2153 0 : comp_error(comp, "bad integer: %s", strerror(errno));
2154 0 : return false;
2155 : }
2156 1235 : len = end - start;
2157 :
2158 1235 : if (len == 0) {
2159 0 : comp_error(comp, "unexpected non-integer");
2160 0 : return false;
2161 : }
2162 1235 : if (comp->offset + len > comp->length) {
2163 0 : comp_error(comp, "impossible integer length: %zu!", len);
2164 0 : return false;
2165 : }
2166 :
2167 1235 : comp->offset += len;
2168 :
2169 : /*
2170 : * Record the base and sign, which are used for recreating the SDDL.
2171 : *
2172 : * 'Sign' indicates whether there is a '+' or '-' sign. Base indicates
2173 : * whether the number was in hex, octal, or decimal. These make no
2174 : * difference to the evaluation of the ACE, just the display.
2175 : *
2176 : * This would not work reliably if eat_whitespace() is not called
2177 : * before parse_int(), but a) we know it is, and b) we don't *really*
2178 : * care if we lose these display hints.
2179 : */
2180 1235 : if (*start == '-') {
2181 81 : token.data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NEGATIVE;
2182 81 : first_digit++;
2183 1154 : } else if (*start == '+') {
2184 2 : token.data.int64.sign = CONDITIONAL_ACE_INT_SIGN_POSITIVE;
2185 2 : first_digit++;
2186 : } else {
2187 1152 : token.data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
2188 : }
2189 1235 : if (*first_digit == '0' && (end - first_digit) > 1) {
2190 149 : if ((end - first_digit > 2) &&
2191 141 : (first_digit[1] == 'x' ||
2192 0 : first_digit[1] == 'X')) {
2193 100 : token.data.int64.base = CONDITIONAL_ACE_INT_BASE_16;
2194 : } else {
2195 49 : token.data.int64.base = CONDITIONAL_ACE_INT_BASE_8;
2196 : }
2197 : } else {
2198 1086 : token.data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
2199 : }
2200 :
2201 1235 : token.data.int64.value = v;
2202 1235 : token.type = CONDITIONAL_ACE_TOKEN_INT64;
2203 1235 : return write_sddl_token(comp, token);
2204 : }
2205 :
2206 :
2207 349 : static bool parse_uint(struct ace_condition_sddl_compiler_context *comp)
2208 : {
2209 349 : struct ace_condition_token *tok = NULL;
2210 349 : bool ok = parse_int(comp);
2211 349 : if (ok == false) {
2212 0 : return false;
2213 : }
2214 : /*
2215 : * check that the token's value is positive.
2216 : */
2217 349 : if (comp->target_len == 0) {
2218 0 : return false;
2219 : }
2220 349 : tok = &comp->target[*comp->target_len - 1];
2221 349 : if (tok->type != CONDITIONAL_ACE_TOKEN_INT64) {
2222 0 : return false;
2223 : }
2224 349 : if (tok->data.int64.value < 0) {
2225 0 : comp_error(comp, "invalid resource ACE value for unsigned TU claim");
2226 0 : return false;
2227 : }
2228 0 : return true;
2229 : }
2230 :
2231 :
2232 0 : static bool parse_bool(struct ace_condition_sddl_compiler_context *comp)
2233 : {
2234 0 : struct ace_condition_token *tok = NULL;
2235 0 : bool ok = parse_int(comp);
2236 0 : if (ok == false || comp->target_len == 0) {
2237 0 : return false;
2238 : }
2239 : /*
2240 : * check that the token is 0 or 1.
2241 : */
2242 0 : tok = &comp->target[*comp->target_len - 1];
2243 0 : if (tok->type != CONDITIONAL_ACE_TOKEN_INT64) {
2244 0 : return false;
2245 : }
2246 0 : if (tok->data.int64.value != 0 && tok->data.int64.value != 1) {
2247 0 : comp_error(comp, "invalid resource ACE Boolean value");
2248 0 : return false;
2249 : }
2250 0 : return true;
2251 : }
2252 :
2253 :
2254 322 : static bool could_be_an_int(struct ace_condition_sddl_compiler_context *comp)
2255 : {
2256 322 : const char *start = (const char*)(comp->sddl + comp->offset);
2257 322 : char* end = NULL;
2258 :
2259 322 : if ((comp->state & SDDL_FLAG_EXPECTING_LITERAL) == 0) {
2260 0 : return false;
2261 : }
2262 :
2263 139 : errno = 0;
2264 : /*
2265 : * See, we don't care about the strtoll return value, only
2266 : * whether it succeeds or not and what it finds at the end. If
2267 : * it succeeds, parse_int() will do it again for the value.
2268 : *
2269 : * Note that an out of range int will raise ERANGE (probably
2270 : * 34), so it will be read as a local attribute.
2271 : */
2272 139 : strtoll(start, &end, 0);
2273 139 : if (errno != 0 ||
2274 131 : end == start ||
2275 129 : end >= (const char*)comp->sddl + comp->length) {
2276 0 : return false;
2277 : }
2278 : /*
2279 : * We know *some* characters form an int, but if we run right
2280 : * into other attr1 characters (basically, letters), we won't
2281 : * count it as an int.
2282 : *
2283 : * For example, the "17" in "17p" is not an int. The "17" in
2284 : * "17||" is.
2285 : */
2286 129 : if (is_attr_char1(*end)) {
2287 0 : return false;
2288 : }
2289 28 : return true;
2290 : }
2291 :
2292 :
2293 1302 : static bool parse_word(struct ace_condition_sddl_compiler_context *comp)
2294 : {
2295 : /*
2296 : * Sometimes a bare word must be a local attribute, while in other
2297 : * cases it could also be a member-of or exists operator. Sometimes it
2298 : * could actually be a SID, which we discover when we've read as far
2299 : * as "SID(". Sometimes it might be a literal integer (attribute
2300 : * names can also consist entirely of digits).
2301 : *
2302 : * When it is an operator name, we have the complication that a match
2303 : * does not necessarily end the token. Consider "Member_of_Any" which
2304 : * contains the operator "Member_of". According to [MS-DTYP], a space
2305 : * is not necessary between the operator and the next token, but it
2306 : * does seem to be required for Windows 2022.
2307 : *
2308 : * Also, "Member_of" et. al. *could* be valid local attributes, which
2309 : * would make "(Member_of == 123)" a valid expression that we will
2310 : * fail to parse. This is not much of an issue for Samba AD where
2311 : * local attributes are not used.
2312 : *
2313 : * Operators are matched case-insensitively.
2314 : *
2315 : * There's another kind of attribute that starts with a '@', which we
2316 : * deal with in parse_attr2(). Those ones have full unicode glory;
2317 : * these ones are ASCII only.
2318 : */
2319 835 : size_t i, j, k;
2320 835 : bool ok;
2321 835 : uint8_t candidates[8];
2322 1302 : size_t n_candidates = 0;
2323 1302 : struct ace_condition_token token = {};
2324 1302 : bool expecting_unary = comp->state & SDDL_FLAG_EXPECTING_UNARY_OP;
2325 1302 : bool expecting_binary = comp->state & SDDL_FLAG_EXPECTING_BINARY_OP;
2326 1302 : bool expecting_attr = comp->state & SDDL_FLAG_EXPECTING_LOCAL_ATTR;
2327 1302 : bool expecting_literal = comp->state & SDDL_FLAG_EXPECTING_LITERAL;
2328 1302 : const uint8_t *start = comp->sddl + comp->offset;
2329 1302 : uint8_t c = start[0];
2330 1302 : char *s = NULL;
2331 2137 : if (! is_attr_char1(*start)) {
2332 : /* we shouldn't get here, because we peeked first */
2333 0 : return false;
2334 : }
2335 :
2336 : /*
2337 : * We'll look for a SID first, because it simplifies the rest.
2338 : */
2339 1302 : if (expecting_literal &&
2340 284 : comp->offset + 4 < comp->length &&
2341 161 : start[0] == 'S' &&
2342 145 : start[1] == 'I' &&
2343 145 : start[2] == 'D' &&
2344 145 : start[3] == '(') {
2345 : /* actually, we are parsing a SID. */
2346 145 : return parse_sid(comp);
2347 : }
2348 :
2349 1508 : if (expecting_binary || expecting_unary) {
2350 : /*
2351 : * Collect up the operators that can possibly be used
2352 : * here, including only those that start with the
2353 : * current letter and have the right arity/syntax.
2354 : *
2355 : * We don't expect more than 5 (for 'N', beginning the
2356 : * "Not_..." unary ops), and we'll winnow them down as
2357 : * we progress through the word.
2358 : */
2359 1015 : int uc = toupper(c);
2360 260855 : for (i = 0; i < 256; i++) {
2361 259840 : const struct sddl_data *d = &sddl_strings[i];
2362 259840 : if (sddl_strings[i].op_precedence != SDDL_NOT_AN_OP &&
2363 25375 : uc == toupper((unsigned char)d->name[0])) {
2364 1793 : if (d->flags & SDDL_FLAG_IS_UNARY_OP) {
2365 1260 : if (!expecting_unary) {
2366 190 : continue;
2367 : }
2368 533 : } else if (!expecting_binary) {
2369 116 : continue;
2370 : }
2371 1487 : candidates[n_candidates] = i;
2372 1487 : n_candidates++;
2373 1487 : if (n_candidates == ARRAY_SIZE(candidates)) {
2374 : /* impossible, really. */
2375 0 : return false;
2376 : }
2377 : }
2378 : }
2379 142 : } else if (could_be_an_int(comp)) {
2380 : /*
2381 : * if looks like an integer, and we expect an integer, it is
2382 : * an integer. If we don't expect an integer, it is a local
2383 : * attribute with a STUPID NAME. Or an error.
2384 : */
2385 129 : return parse_int(comp);
2386 13 : } else if (! expecting_attr) {
2387 11 : comp_error(comp, "did not expect this word here");
2388 11 : return false;
2389 : }
2390 :
2391 351 : i = 1;
2392 11287 : while (comp->offset + i < comp->length) {
2393 11287 : c = start[i];
2394 18109 : if (! is_attr_char1(c)) {
2395 351 : break;
2396 : }
2397 10270 : if (n_candidates != 0) {
2398 : /*
2399 : * Filter out candidate operators that no longer
2400 : * match.
2401 : */
2402 7401 : int uc = toupper(c);
2403 7401 : k = 0;
2404 20164 : for (j = 0; j < n_candidates; j++) {
2405 12763 : size_t o = candidates[j];
2406 12763 : uint8_t c2 = sddl_strings[o].name[i];
2407 12763 : if (uc == toupper(c2)) {
2408 12513 : candidates[k] = candidates[j];
2409 12513 : k++;
2410 : }
2411 : }
2412 3469 : n_candidates = k;
2413 : }
2414 10270 : i++;
2415 : }
2416 :
2417 : /*
2418 : * We have finished and there is a complete word. If it could be an
2419 : * operator we'll assume it is one.
2420 : *
2421 : * A complication is we could have matched more than one operator, for
2422 : * example "Member_of" and "Member_of_Any", so we have to look through
2423 : * the list of candidates for the one that ends.
2424 : */
2425 1017 : if (n_candidates != 0) {
2426 837 : for (j = 0; j < n_candidates; j++) {
2427 837 : size_t o = candidates[j];
2428 837 : if (sddl_strings[o].name[i] == '\0') {
2429 : /* it is this one */
2430 :
2431 837 : if (!comp->allow_device &&
2432 9 : (sddl_strings[o].flags & SDDL_FLAG_DEVICE))
2433 : {
2434 1 : comp_error(
2435 : comp,
2436 : "a device‐relative expression "
2437 : "will never evaluate to true "
2438 : "in this context (did you "
2439 : "intend a user‐relative "
2440 : "expression?)");
2441 1 : return false;
2442 : }
2443 :
2444 836 : token.type = o;
2445 836 : token.data.sddl_op.start = comp->offset;
2446 836 : comp->offset += i;
2447 836 : ok = push_sddl_token(comp, token);
2448 836 : return ok;
2449 : }
2450 : }
2451 : }
2452 : /*
2453 : * if looks like an integer, and we expect an integer, it is
2454 : * an integer. If we don't expect an integer, it is a local
2455 : * attribute with a STUPID NAME.
2456 : */
2457 180 : if (could_be_an_int(comp)) {
2458 0 : return parse_int(comp);
2459 : }
2460 :
2461 180 : if (! expecting_attr) {
2462 0 : comp_error(comp, "word makes no sense here");
2463 0 : return false;
2464 : }
2465 : /* it's definitely an attribute name */
2466 180 : token.type = CONDITIONAL_ACE_LOCAL_ATTRIBUTE;
2467 180 : if (comp->offset + i >= comp->length) {
2468 0 : comp_error(comp, "missing trailing ')'?");
2469 0 : return false;
2470 : }
2471 :
2472 180 : s = talloc_memdup(comp->mem_ctx, start, i + 1);
2473 180 : if (s == NULL) {
2474 0 : comp_error(comp, "allocation error");
2475 0 : return false;
2476 : }
2477 180 : s[i] = 0;
2478 180 : token.data.local_attr.value = s;
2479 180 : comp->offset += i;
2480 180 : return write_sddl_token(comp, token);
2481 : }
2482 :
2483 1587 : static bool parse_attr2(struct ace_condition_sddl_compiler_context *comp)
2484 : {
2485 : /*
2486 : * Attributes in the form @class.attr
2487 : *
2488 : * class can be "User", "Device", or "Resource", case insensitive.
2489 : */
2490 952 : size_t i;
2491 952 : bool ok;
2492 952 : size_t len;
2493 1587 : struct ace_condition_token token = {};
2494 :
2495 1587 : if ((comp->state & SDDL_FLAG_EXPECTING_NON_LOCAL_ATTR) == 0) {
2496 0 : comp_error(comp, "did not expect @attr here");
2497 0 : return false;
2498 : }
2499 1587 : if (comp->sddl[comp->offset] != '@') {
2500 0 : comp_error(comp, "Expected '@'");
2501 0 : return false;
2502 : }
2503 1587 : comp->offset++;
2504 :
2505 2780 : for (i = 0; i < ARRAY_SIZE(sddl_attr_types); i++) {
2506 2131 : int ret;
2507 2780 : size_t attr_len = strlen(sddl_attr_types[i].name);
2508 2780 : if (attr_len >= comp->length - comp->offset) {
2509 2 : continue;
2510 : }
2511 2778 : ret = strncasecmp(sddl_attr_types[i].name,
2512 2778 : (const char *) (comp->sddl + comp->offset),
2513 : attr_len);
2514 2778 : if (ret == 0) {
2515 1587 : const uint8_t code = sddl_attr_types[i].code;
2516 :
2517 1587 : if (!comp->allow_device &&
2518 6 : (sddl_strings[code].flags & SDDL_FLAG_DEVICE))
2519 : {
2520 1 : comp_error(comp,
2521 : "a device attribute is not "
2522 : "applicable in this context (did "
2523 : "you intend a user attribute?)");
2524 1 : return false;
2525 : }
2526 :
2527 1586 : token.type = code;
2528 1586 : comp->offset += attr_len;
2529 1586 : break;
2530 : }
2531 : }
2532 1586 : if (i == ARRAY_SIZE(sddl_attr_types)) {
2533 0 : comp_error(comp, "unknown attribute class");
2534 0 : return false;
2535 : }
2536 :
2537 : /*
2538 : * Now we are past the class and the '.', and into the
2539 : * attribute name. The attribute name can be almost
2540 : * anything, but some characters need to be escaped.
2541 : */
2542 :
2543 1586 : len = read_attr2_string(comp, &token.data.unicode);
2544 1586 : if (len == -1) {
2545 : /* read_attr2_string has set a message */
2546 0 : return false;
2547 : }
2548 1583 : ok = write_sddl_token(comp, token);
2549 1583 : if (! ok) {
2550 0 : return false;
2551 : }
2552 1583 : comp->offset += len;
2553 1583 : ok = eat_whitespace(comp, false);
2554 1583 : return ok;
2555 : }
2556 :
2557 7409 : static bool parse_literal(struct ace_condition_sddl_compiler_context *comp,
2558 : bool in_composite)
2559 : {
2560 7409 : uint8_t c = comp->sddl[comp->offset];
2561 7409 : if (!(comp->state & SDDL_FLAG_EXPECTING_LITERAL)) {
2562 0 : comp_error(comp, "did not expect to be parsing a literal now");
2563 0 : return false;
2564 : }
2565 7409 : switch(c) {
2566 15 : case '#':
2567 15 : return parse_octet_string(comp);
2568 5300 : case '"':
2569 5300 : return parse_unicode(comp);
2570 651 : case 'S':
2571 651 : return parse_sid(comp);
2572 809 : case '{':
2573 809 : if (in_composite) {
2574 : /* nested composites are not supported */
2575 0 : return false;
2576 : } else {
2577 809 : return parse_composite(comp);
2578 : }
2579 634 : default:
2580 634 : if (strchr("1234567890-+", c) != NULL) {
2581 629 : return parse_int(comp);
2582 : }
2583 : }
2584 5 : if (c > 31 && c < 127) {
2585 5 : comp_error(comp,
2586 : "unexpected byte 0x%02x '%c' parsing literal", c, c);
2587 : } else {
2588 0 : comp_error(comp, "unexpected byte 0x%02x parsing literal", c);
2589 : }
2590 2 : return false;
2591 : }
2592 :
2593 :
2594 809 : static bool parse_composite(struct ace_condition_sddl_compiler_context *comp)
2595 : {
2596 : /*
2597 : * This jumps into a different parser, expecting a comma separated
2598 : * list of literal values, which might include nested literal
2599 : * composites.
2600 : *
2601 : * To handle the nesting, we redirect the pointers that determine
2602 : * where write_sddl_token() writes.
2603 : */
2604 547 : bool ok;
2605 809 : bool first = true;
2606 809 : struct ace_condition_token token = {
2607 : .type = CONDITIONAL_ACE_TOKEN_COMPOSITE
2608 : };
2609 809 : uint32_t start = comp->offset;
2610 547 : size_t alloc_size;
2611 809 : struct ace_condition_token *old_target = comp->target;
2612 809 : uint32_t *old_target_len = comp->target_len;
2613 :
2614 809 : if (comp->sddl[start] != '{') {
2615 0 : comp_error(comp, "expected '{' for composite list");
2616 0 : return false;
2617 : }
2618 809 : if (!(comp->state & SDDL_FLAG_EXPECTING_LITERAL)) {
2619 0 : comp_error(comp, "did not expect '{' for composite list");
2620 0 : return false;
2621 : }
2622 809 : comp->offset++; /* past '{' */
2623 :
2624 : /*
2625 : * the worst case is one token for every two bytes: {1,1,1}, and we
2626 : * allocate for that (counting commas and finding '}' gets hard because
2627 : * string literals).
2628 : */
2629 809 : alloc_size = MIN((comp->length - start) / 2 + 1,
2630 : CONDITIONAL_ACE_MAX_LENGTH);
2631 :
2632 809 : token.data.composite.tokens = talloc_array(
2633 : comp->mem_ctx,
2634 : struct ace_condition_token,
2635 : alloc_size);
2636 809 : if (token.data.composite.tokens == NULL) {
2637 0 : comp_error(comp, "allocation failure");
2638 0 : return false;
2639 : }
2640 :
2641 809 : comp->target = token.data.composite.tokens;
2642 809 : comp->target_len = &token.data.composite.n_members;
2643 :
2644 : /*
2645 : * in this loop we are looking for:
2646 : *
2647 : * a) possible whitespace.
2648 : * b) a comma (or terminating '}')
2649 : * c) more possible whitespace
2650 : * d) a literal
2651 : *
2652 : * Failures use a goto to reset comp->target, just in case we ever try
2653 : * continuing after error.
2654 : */
2655 7046 : while (comp->offset < comp->length) {
2656 6426 : uint8_t c;
2657 7046 : ok = eat_whitespace(comp, false);
2658 7046 : if (! ok) {
2659 0 : goto fail;
2660 : }
2661 7046 : c = comp->sddl[comp->offset];
2662 7046 : if (c == '}') {
2663 804 : comp->offset++;
2664 804 : break;
2665 : }
2666 6242 : if (!first) {
2667 5461 : if (c != ',') {
2668 1 : comp_error(comp,
2669 : "malformed composite (expected comma)");
2670 1 : goto fail;
2671 : }
2672 5460 : comp->offset++;
2673 :
2674 5460 : ok = eat_whitespace(comp, false);
2675 5460 : if (! ok) {
2676 0 : goto fail;
2677 : }
2678 : }
2679 6241 : first = false;
2680 6241 : if (*comp->target_len >= alloc_size) {
2681 0 : comp_error(comp,
2682 : "Too many tokens in composite "
2683 : "(>= %"PRIu32" tokens)",
2684 0 : *comp->target_len);
2685 0 : goto fail;
2686 : }
2687 6241 : ok = parse_literal(comp, true);
2688 6241 : if (!ok) {
2689 4 : goto fail;
2690 : }
2691 : }
2692 804 : comp->target = old_target;
2693 804 : comp->target_len = old_target_len;
2694 804 : write_sddl_token(comp, token);
2695 804 : return true;
2696 5 : fail:
2697 5 : talloc_free(token.data.composite.tokens);
2698 5 : comp->target = old_target;
2699 5 : comp->target_len = old_target_len;
2700 5 : return false;
2701 : }
2702 :
2703 :
2704 3 : static bool parse_paren_literal(struct ace_condition_sddl_compiler_context *comp)
2705 : {
2706 3 : bool ok;
2707 3 : if (comp->sddl[comp->offset] != '(') {
2708 0 : comp_error(comp, "expected '('");
2709 0 : return false;
2710 : }
2711 3 : comp->offset++;
2712 3 : ok = parse_literal(comp, false);
2713 3 : if (!ok) {
2714 0 : return false;
2715 : }
2716 2 : if (comp->sddl[comp->offset] != ')') {
2717 0 : comp_error(comp, "expected ')'");
2718 0 : return false;
2719 : }
2720 2 : comp->offset++;
2721 2 : return true;
2722 : }
2723 :
2724 2465 : static bool parse_expression(struct ace_condition_sddl_compiler_context *comp)
2725 : {
2726 : /*
2727 : * This expects a parenthesised expression.
2728 : */
2729 1670 : bool ok;
2730 2465 : struct ace_condition_token token = {};
2731 2465 : uint32_t start = comp->offset;
2732 :
2733 2465 : if (comp->state & SDDL_FLAG_EXPECTING_PAREN_LITERAL) {
2734 : /*
2735 : * Syntactically we allow parentheses to wrap a
2736 : * literal value after a Member_of or >= op, but we
2737 : * want to remember that it just wants a single
2738 : * literal, not a general expression.
2739 : */
2740 3 : return parse_paren_literal(comp);
2741 : }
2742 :
2743 2462 : if (comp->sddl[start] != '(') {
2744 4 : comp_error(comp, "expected '('");
2745 4 : return false;
2746 : }
2747 :
2748 2458 : if (!(comp->state & SDDL_FLAG_EXPECTING_PAREN)) {
2749 1 : comp_error(comp, "did not expect '('");
2750 1 : return false;
2751 : }
2752 :
2753 2457 : token.type = CONDITIONAL_ACE_SAMBA_SDDL_PAREN;
2754 2457 : token.data.sddl_op.start = start;
2755 2457 : ok = push_sddl_token(comp, token);
2756 2457 : if (!ok) {
2757 0 : return false;
2758 : }
2759 2457 : comp->offset++; /* over the '(' */
2760 2457 : comp->state = SDDL_FLAGS_EXPR_START;
2761 2457 : DBG_INFO("%3"PRIu32": (\n", comp->offset);
2762 :
2763 2457 : comp->state |= SDDL_FLAG_NOT_EXPECTING_END_PAREN;
2764 :
2765 10361 : while (comp->offset < comp->length) {
2766 5985 : uint8_t c;
2767 8699 : ok = eat_whitespace(comp, false);
2768 8699 : if (! ok) {
2769 0 : return false;
2770 : }
2771 8699 : c = comp->sddl[comp->offset];
2772 8699 : if (c == '(') {
2773 974 : ok = parse_expression(comp);
2774 7725 : } else if (c == ')') {
2775 2422 : if (comp->state & (SDDL_FLAG_IS_BINARY_OP |
2776 : SDDL_FLAG_IS_UNARY_OP)) {
2777 : /*
2778 : * You can't have "(a ==)" or "(!)"
2779 : */
2780 3 : comp_error(comp,
2781 : "operator lacks right hand argument");
2782 3 : return false;
2783 : }
2784 2419 : if (comp->state & SDDL_FLAG_NOT_EXPECTING_END_PAREN) {
2785 : /*
2786 : * You can't have "( )"
2787 : */
2788 1 : comp_error(comp, "empty expression");
2789 1 : return false;
2790 : }
2791 791 : break;
2792 5303 : } else if (c == '@') {
2793 1587 : ok = parse_attr2(comp);
2794 3716 : } else if (strchr("!<>=&|", c)) {
2795 1393 : ok = parse_oppy_op(comp);
2796 3158 : } else if (is_attr_char1(c)) {
2797 1302 : ok = parse_word(comp);
2798 1021 : } else if (comp->state & SDDL_FLAG_EXPECTING_LITERAL) {
2799 1020 : ok = parse_literal(comp, false);
2800 : } else {
2801 1 : if (c > 31 && c < 127) {
2802 1 : comp_error(comp,
2803 : "unexpected byte 0x%02x '%c'", c, c);
2804 : } else {
2805 0 : comp_error(comp, "unexpected byte 0x%02x", c);
2806 : }
2807 0 : ok = false;
2808 : }
2809 :
2810 6276 : if (! ok) {
2811 35 : return false;
2812 : }
2813 : /*
2814 : * what did we just find? Set what we expect accordingly.
2815 : */
2816 6242 : comp->state = sddl_strings[comp->last_token_type].flags;
2817 6242 : DBG_INFO("%3"PRIu32": %s\n",
2818 : comp->offset,
2819 : sddl_strings[comp->last_token_type].name);
2820 : }
2821 2418 : ok = eat_whitespace(comp, false);
2822 2418 : if (!ok) {
2823 0 : return false;
2824 : }
2825 :
2826 2418 : if (comp->sddl[comp->offset] != ')') {
2827 0 : comp_error(comp, "expected ')' to match '(' at %"PRIu32, start);
2828 0 : return false;
2829 : }
2830 : /*
2831 : * we won't comp->offset++ until after these other error checks, so
2832 : * that their messages have consistent locations.
2833 : */
2834 2418 : ok = flush_stack_tokens(comp, CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END);
2835 2418 : if (!ok) {
2836 0 : return false;
2837 : }
2838 2418 : if (comp->stack_depth == 0) {
2839 0 : comp_error(comp, "mysterious nesting error between %"
2840 : PRIu32" and here",
2841 : start);
2842 0 : return false;
2843 : }
2844 2418 : token = comp->stack[comp->stack_depth - 1];
2845 2418 : if (token.type != CONDITIONAL_ACE_SAMBA_SDDL_PAREN) {
2846 0 : comp_error(comp, "nesting error between %"PRIu32" and here",
2847 : start);
2848 0 : return false;
2849 : }
2850 2418 : if (token.data.sddl_op.start != start) {
2851 0 : comp_error(comp, "')' should match '(' at %"PRIu32
2852 : ", not %"PRIu32,
2853 : token.data.sddl_op.start, start);
2854 0 : return false;
2855 : }
2856 2418 : comp->stack_depth--;
2857 2418 : DBG_INFO("%3"PRIu32": )\n", comp->offset);
2858 :
2859 2418 : comp->offset++; /* for the ')' */
2860 2418 : comp->last_token_type = CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END;
2861 2418 : comp->state = sddl_strings[comp->last_token_type].flags;
2862 :
2863 2418 : ok = eat_whitespace(comp, true);
2864 2418 : return ok;
2865 : }
2866 :
2867 :
2868 :
2869 1636 : static bool init_compiler_context(
2870 : TALLOC_CTX *mem_ctx,
2871 : struct ace_condition_sddl_compiler_context *comp,
2872 : const enum ace_condition_flags ace_condition_flags,
2873 : const char *sddl,
2874 : size_t max_length,
2875 : size_t max_stack)
2876 : {
2877 1636 : struct ace_condition_script *program = NULL;
2878 :
2879 1636 : comp->sddl = (const uint8_t*)sddl;
2880 1636 : comp->mem_ctx = mem_ctx;
2881 :
2882 1636 : program = talloc_zero(mem_ctx, struct ace_condition_script);
2883 1636 : if (program == NULL) {
2884 0 : return false;
2885 : }
2886 : /*
2887 : * For the moment, we allocate for the worst case up front.
2888 : */
2889 1636 : program->tokens = talloc_array(program,
2890 : struct ace_condition_token,
2891 : max_length);
2892 1636 : if (program->tokens == NULL) {
2893 0 : TALLOC_FREE(program);
2894 0 : return false;
2895 : }
2896 1636 : program->stack = talloc_array(program,
2897 : struct ace_condition_token,
2898 : max_stack + 1);
2899 1636 : if (program->stack == NULL) {
2900 0 : TALLOC_FREE(program);
2901 0 : return false;
2902 : }
2903 1636 : comp->program = program;
2904 : /* we can borrow the program stack for the operator stack */
2905 1636 : comp->stack = program->stack;
2906 1636 : comp->target = program->tokens;
2907 1636 : comp->target_len = &program->length;
2908 1636 : comp->length = strlen(sddl);
2909 1636 : comp->state = SDDL_FLAG_EXPECTING_PAREN;
2910 1636 : comp->allow_device = ace_condition_flags & ACE_CONDITION_FLAG_ALLOW_DEVICE;
2911 1636 : return true;
2912 : }
2913 :
2914 : /*
2915 : * Compile SDDL conditional ACE conditions.
2916 : *
2917 : * @param mem_ctx
2918 : * @param sddl - the string to be parsed
2919 : * @param ace_condition_flags - flags controlling compiler behaviour
2920 : * @param message - on error, a pointer to a compiler message
2921 : * @param message_offset - where the error occurred
2922 : * @param consumed_length - how much of the SDDL was used
2923 : * @return a struct ace_condition_script (or NULL).
2924 : */
2925 1491 : struct ace_condition_script * ace_conditions_compile_sddl(
2926 : TALLOC_CTX *mem_ctx,
2927 : const enum ace_condition_flags ace_condition_flags,
2928 : const char *sddl,
2929 : const char **message,
2930 : size_t *message_offset,
2931 : size_t *consumed_length)
2932 : {
2933 766 : bool ok;
2934 1491 : struct ace_condition_sddl_compiler_context comp = {};
2935 :
2936 1491 : *message = NULL;
2937 1491 : *message_offset = 0;
2938 :
2939 1491 : ok = init_compiler_context(mem_ctx,
2940 : &comp,
2941 : ace_condition_flags,
2942 : sddl,
2943 : CONDITIONAL_ACE_MAX_LENGTH,
2944 : CONDITIONAL_ACE_MAX_TOKENS);
2945 1491 : if (!ok) {
2946 0 : return NULL;
2947 : }
2948 :
2949 1491 : ok = parse_expression(&comp);
2950 1491 : if (!ok) {
2951 43 : goto error;
2952 : }
2953 1448 : if (comp.stack_depth != 0) {
2954 0 : comp_error(&comp, "incomplete expression");
2955 0 : goto error;
2956 : }
2957 1448 : if (consumed_length != NULL) {
2958 1448 : *consumed_length = comp.offset;
2959 : }
2960 1448 : *message = comp.message;
2961 1448 : *message_offset = comp.message_offset;
2962 1448 : return comp.program;
2963 43 : error:
2964 43 : *message = comp.message;
2965 43 : *message_offset = comp.message_offset;
2966 43 : TALLOC_FREE(comp.program);
2967 4 : return NULL;
2968 : }
2969 :
2970 :
2971 :
2972 105 : static bool parse_resource_attr_list(
2973 : struct ace_condition_sddl_compiler_context *comp,
2974 : char attr_type_char)
2975 : {
2976 : /*
2977 : * This is a bit like parse_composite() above, but with the following
2978 : * differences:
2979 : *
2980 : * - it doesn't want '{...}' around the list.
2981 : * - if there is just one value, it is not a composite
2982 : * - all the values must be the expected type.
2983 : * - there is no nesting.
2984 : * - SIDs are not written with SID(...) around them.
2985 : */
2986 105 : bool ok;
2987 105 : bool first = true;
2988 105 : struct ace_condition_token composite = {
2989 : .type = CONDITIONAL_ACE_TOKEN_COMPOSITE
2990 : };
2991 105 : uint32_t start = comp->offset;
2992 105 : size_t alloc_size;
2993 105 : struct ace_condition_token *old_target = comp->target;
2994 105 : uint32_t *old_target_len = comp->target_len;
2995 :
2996 105 : comp->state = SDDL_FLAG_EXPECTING_LITERAL;
2997 :
2998 : /*
2999 : * the worst case is one token for every two bytes: {1,1,1}, and we
3000 : * allocate for that (counting commas and finding '}' gets hard because
3001 : * string literals).
3002 : */
3003 105 : alloc_size = MIN((comp->length - start) / 2 + 1,
3004 : CONDITIONAL_ACE_MAX_LENGTH);
3005 :
3006 105 : composite.data.composite.tokens = talloc_array(
3007 : comp->mem_ctx,
3008 : struct ace_condition_token,
3009 : alloc_size);
3010 105 : if (composite.data.composite.tokens == NULL) {
3011 0 : comp_error(comp, "allocation failure");
3012 0 : return false;
3013 : }
3014 :
3015 105 : comp->target = composite.data.composite.tokens;
3016 105 : comp->target_len = &composite.data.composite.n_members;
3017 :
3018 : /*
3019 : * in this loop we are looking for:
3020 : *
3021 : * a) possible whitespace.
3022 : * b) a comma (or terminating ')')
3023 : * c) more possible whitespace
3024 : * d) a literal, of the right type (checked after)
3025 : *
3026 : * Failures use a goto to reset comp->target, just in case we ever try
3027 : * continuing after error.
3028 : */
3029 1399 : while (comp->offset < comp->length) {
3030 1399 : uint8_t c;
3031 1399 : ok = eat_whitespace(comp, false);
3032 1399 : if (! ok) {
3033 0 : goto fail;
3034 : }
3035 1399 : c = comp->sddl[comp->offset];
3036 1399 : if (c == ')') {
3037 0 : break;
3038 : }
3039 1294 : if (!first) {
3040 1189 : if (c != ',') {
3041 0 : comp_error(comp,
3042 : "malformed resource attribute ACE "
3043 : "(expected comma)");
3044 0 : goto fail;
3045 : }
3046 1189 : comp->offset++;
3047 :
3048 1189 : ok = eat_whitespace(comp, false);
3049 1189 : if (! ok) {
3050 0 : goto fail;
3051 : }
3052 : }
3053 1294 : first = false;
3054 1294 : if (*comp->target_len >= alloc_size) {
3055 0 : comp_error(comp,
3056 : "Too many tokens in resource attribute ACE "
3057 : "(>= %"PRIu32" tokens)",
3058 0 : *comp->target_len);
3059 0 : goto fail;
3060 : }
3061 1294 : switch(attr_type_char) {
3062 522 : case 'X':
3063 522 : ok = parse_ra_octet_string(comp);
3064 522 : break;
3065 295 : case 'S':
3066 295 : ok = parse_unicode(comp);
3067 295 : break;
3068 349 : case 'U':
3069 349 : ok = parse_uint(comp);
3070 349 : break;
3071 0 : case 'B':
3072 0 : ok = parse_bool(comp);
3073 0 : break;
3074 128 : case 'I':
3075 128 : ok = parse_int(comp);
3076 128 : break;
3077 0 : case 'D':
3078 0 : ok = parse_ra_sid(comp);
3079 0 : break;
3080 0 : default:
3081 : /* it's a mystery we got this far */
3082 0 : comp_error(comp,
3083 : "unknown attribute type T%c",
3084 : attr_type_char);
3085 0 : goto fail;
3086 : }
3087 1294 : if (!ok) {
3088 0 : goto fail;
3089 : }
3090 :
3091 1294 : if (*comp->target_len == 0) {
3092 0 : goto fail;
3093 : }
3094 : }
3095 105 : comp->target = old_target;
3096 105 : comp->target_len = old_target_len;
3097 :
3098 : /*
3099 : * If we only ended up collecting one token into the composite, we
3100 : * write that instead.
3101 : */
3102 105 : if (composite.data.composite.n_members == 1) {
3103 34 : ok = write_sddl_token(comp, composite.data.composite.tokens[0]);
3104 34 : talloc_free(composite.data.composite.tokens);
3105 : } else {
3106 71 : ok = write_sddl_token(comp, composite);
3107 : }
3108 105 : if (! ok) {
3109 0 : goto fail;
3110 : }
3111 :
3112 0 : return true;
3113 0 : fail:
3114 0 : comp->target = old_target;
3115 0 : comp->target_len = old_target_len;
3116 0 : TALLOC_FREE(composite.data.composite.tokens);
3117 0 : return false;
3118 : }
3119 :
3120 :
3121 :
3122 105 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *sddl_decode_resource_attr (
3123 : TALLOC_CTX *mem_ctx,
3124 : const char *str,
3125 : size_t *length)
3126 : {
3127 : /*
3128 : * Resource attribute ACEs define claims in object SACLs. They look like
3129 : *
3130 : * "(RA; «flags» ;;;;WD;( «attribute-data» ))"
3131 : *
3132 : * attribute-data = DQUOTE 1*attr-char2 DQUOTE "," \
3133 : * ( TI-attr / TU-attr / TS-attr / TD-attr / TX-attr / TB-attr )
3134 : * TI-attr = "TI" "," attr-flags *("," int-64)
3135 : * TU-attr = "TU" "," attr-flags *("," uint-64)
3136 : * TS-attr = "TS" "," attr-flags *("," char-string)
3137 : * TD-attr = "TD" "," attr-flags *("," sid-string)
3138 : * TX-attr = "TX" "," attr-flags *("," octet-string)
3139 : * TB-attr = "TB" "," attr-flags *("," ( "0" / "1" ) )
3140 : *
3141 : * and the data types are *mostly* parsed in the SDDL way,
3142 : * though there are significant differences for octet-strings.
3143 : *
3144 : * At this point we only have the "(«attribute-data»)".
3145 : *
3146 : * What we do is set up a conditional ACE compiler to be expecting a
3147 : * literal, and ask it to parse the strings between the commas. It's a
3148 : * hack.
3149 : */
3150 105 : bool ok;
3151 105 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL;
3152 105 : struct ace_condition_sddl_compiler_context comp = {};
3153 105 : char attr_type;
3154 105 : struct ace_condition_token *tok;
3155 105 : uint32_t flags;
3156 105 : size_t len;
3157 105 : struct ace_condition_unicode attr_name = {};
3158 :
3159 105 : ok = init_compiler_context(mem_ctx,
3160 : &comp,
3161 : ACE_CONDITION_FLAG_ALLOW_DEVICE,
3162 : str,
3163 : 3,
3164 : 3);
3165 105 : if (!ok) {
3166 0 : return NULL;
3167 : }
3168 105 : if (comp.length < 6 || comp.length > CONDITIONAL_ACE_MAX_LENGTH) {
3169 0 : DBG_WARNING("invalid resource attribute: '%s'\n", str);
3170 0 : goto error;
3171 : }
3172 : /*
3173 : * Resource attribute ACEs list SIDs in a bare form "S-1-2-3", while
3174 : * conditional ACEs use a wrapper syntax "SID(S-1-2-3)". As almost
3175 : * everything is the same, we are reusing the conditional ACE parser,
3176 : * with a flag set to tell the SID parser which form to expect.
3177 : */
3178 :
3179 : /* Most examples on the web have leading whitespace */
3180 105 : ok = eat_whitespace(&comp, false);
3181 105 : if (!ok) {
3182 0 : return NULL;
3183 : }
3184 105 : if (comp.sddl[comp.offset] != '(' ||
3185 105 : comp.sddl[comp.offset + 1] != '"') {
3186 0 : DBG_WARNING("invalid resource attribute -- expected '(\"'\n");
3187 0 : goto error;
3188 : }
3189 105 : comp.offset += 2;
3190 :
3191 : /*
3192 : * Read the name. Here we are not reading a token into comp->program,
3193 : * just into a unicode blob.
3194 : */
3195 105 : len = read_attr2_string(&comp, &attr_name);
3196 :
3197 105 : if (len == -1) {
3198 0 : DBG_WARNING("invalid resource attr name: %s\n", str);
3199 0 : goto error;
3200 : }
3201 105 : comp.offset += len;
3202 :
3203 105 : ok = eat_whitespace(&comp, false);
3204 105 : if (comp.offset + 6 > comp.length) {
3205 0 : DBG_WARNING("invalid resource attribute (too short): '%s'\n",
3206 : str);
3207 0 : goto error;
3208 : }
3209 : /*
3210 : * now we have the name. Next comes '",«T[IUSDXB]»,' followed
3211 : * by the flags, which are a 32 bit number.
3212 : */
3213 105 : if (comp.sddl[comp.offset] != '"' ||
3214 105 : comp.sddl[comp.offset + 1] != ','||
3215 105 : comp.sddl[comp.offset + 2] != 'T') {
3216 0 : DBG_WARNING("expected '\",T[IUSDXB]' after attr name\n");
3217 0 : goto error;
3218 : }
3219 105 : attr_type = comp.sddl[comp.offset + 3];
3220 :
3221 105 : if (comp.sddl[comp.offset + 4] != ',') {
3222 0 : DBG_WARNING("expected ',' after attr type\n");
3223 0 : goto error;
3224 : }
3225 105 : comp.offset += 5;
3226 105 : comp.state = SDDL_FLAG_EXPECTING_LITERAL;
3227 105 : ok = parse_literal(&comp, false);
3228 105 : if (!ok ||
3229 105 : comp.program->length != 1) {
3230 0 : DBG_WARNING("invalid attr flags: %s\n", str);
3231 0 : goto error;
3232 : }
3233 :
3234 105 : tok = &comp.program->tokens[0];
3235 105 : if (tok->type != CONDITIONAL_ACE_TOKEN_INT64 ||
3236 105 : tok->data.int64.value < 0 ||
3237 0 : tok->data.int64.value > UINT32_MAX) {
3238 0 : DBG_WARNING("invalid attr flags (want 32 bit int): %s\n", str);
3239 0 : goto error;
3240 : }
3241 105 : flags = tok->data.int64.value;
3242 105 : if (flags & 0xff00) {
3243 0 : DBG_WARNING("invalid attr flags, "
3244 : "stepping on reserved 0xff00 range: %s\n",
3245 : str);
3246 0 : goto error;
3247 : }
3248 105 : if (comp.offset + 3 > comp.length) {
3249 0 : DBG_WARNING("invalid resource attribute (too short): '%s'\n",
3250 : str);
3251 0 : goto error;
3252 : }
3253 105 : if (comp.sddl[comp.offset] != ',') {
3254 0 : DBG_WARNING("invalid resource attribute ace\n");
3255 0 : goto error;
3256 : }
3257 105 : comp.offset++;
3258 :
3259 105 : ok = parse_resource_attr_list(&comp, attr_type);
3260 105 : if (!ok || comp.program->length != 2) {
3261 0 : DBG_WARNING("invalid attribute type or value: T%c, %s\n",
3262 : attr_type, str);
3263 0 : goto error;
3264 : }
3265 105 : if (comp.sddl[comp.offset] != ')') {
3266 0 : DBG_WARNING("expected trailing ')'\n");
3267 0 : goto error;
3268 : }
3269 105 : comp.offset++;
3270 105 : *length = comp.offset;
3271 :
3272 210 : ok = ace_token_to_claim_v1(mem_ctx,
3273 : attr_name.value,
3274 105 : &comp.program->tokens[1],
3275 : &claim,
3276 : flags);
3277 105 : if (!ok) {
3278 0 : goto error;
3279 : }
3280 105 : TALLOC_FREE(comp.program);
3281 105 : return claim;
3282 0 : error:
3283 0 : TALLOC_FREE(comp.program);
3284 0 : return NULL;
3285 : }
3286 :
3287 :
3288 23 : static bool write_resource_attr_from_token(struct sddl_write_context *ctx,
3289 : const struct ace_condition_token *tok)
3290 : {
3291 : /*
3292 : * this is a helper for sddl_resource_attr_from_claim(),
3293 : * recursing into composites if necessary.
3294 : */
3295 23 : bool ok;
3296 23 : char *sid = NULL;
3297 23 : size_t i;
3298 23 : const struct ace_condition_composite *c = NULL;
3299 23 : switch (tok->type) {
3300 0 : case CONDITIONAL_ACE_TOKEN_INT64:
3301 : /*
3302 : * Note that this includes uint and bool claim types,
3303 : * but we don't check the validity of the ranges (0|1
3304 : * and >=0, respectively), rather we trust the claim
3305 : * to be self-consistent in this regard. Going the
3306 : * other way, string-to-claim, we do check.
3307 : */
3308 0 : return sddl_write_int(ctx, tok);
3309 :
3310 10 : case CONDITIONAL_ACE_TOKEN_UNICODE:
3311 10 : return sddl_write_unicode(ctx, tok);
3312 :
3313 0 : case CONDITIONAL_ACE_TOKEN_SID:
3314 : /* unlike conditional ACE, SID does not have a "SID()" wrapper. */
3315 0 : sid = sddl_encode_sid(ctx->mem_ctx, &tok->data.sid.sid, NULL);
3316 0 : if (sid == NULL) {
3317 0 : return false;
3318 : }
3319 0 : return sddl_write(ctx, sid);
3320 :
3321 6 : case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
3322 6 : return sddl_write_ra_octet_string(ctx, tok);
3323 :
3324 0 : case CONDITIONAL_ACE_TOKEN_COMPOSITE:
3325 : /*
3326 : * write each token, separated by commas. If there
3327 : * were nested composites, this would flatten them,
3328 : * but that isn't really possible because the token we
3329 : * are dealing with came from a claim, which has no
3330 : * facility for nesting.
3331 : */
3332 23 : c = &tok->data.composite;
3333 23 : for(i = 0; i < c->n_members; i++) {
3334 16 : ok = write_resource_attr_from_token(ctx, &c->tokens[i]);
3335 16 : if (!ok) {
3336 0 : return false;
3337 : }
3338 16 : if (i != c->n_members - 1) {
3339 9 : ok = sddl_write(ctx, ",");
3340 9 : if (!ok) {
3341 0 : return false;
3342 : }
3343 : }
3344 : }
3345 0 : return true;
3346 0 : default:
3347 : /* We really really don't expect to get here */
3348 0 : return false;
3349 : }
3350 : }
3351 :
3352 7 : char *sddl_resource_attr_from_claim(
3353 : TALLOC_CTX *mem_ctx,
3354 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim)
3355 : {
3356 7 : char *s = NULL;
3357 7 : char attr_type;
3358 7 : bool ok;
3359 7 : struct ace_condition_token tok = {};
3360 7 : struct sddl_write_context ctx = {};
3361 7 : TALLOC_CTX *tmp_ctx = NULL;
3362 7 : char *name = NULL;
3363 7 : size_t name_len;
3364 :
3365 7 : switch(claim->value_type) {
3366 0 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
3367 0 : attr_type = 'I';
3368 0 : break;
3369 0 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
3370 0 : attr_type = 'U';
3371 0 : break;
3372 6 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
3373 6 : attr_type = 'S';
3374 6 : break;
3375 0 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
3376 0 : attr_type = 'D';
3377 0 : break;
3378 0 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
3379 0 : attr_type = 'B';
3380 0 : break;
3381 1 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
3382 1 : attr_type = 'X';
3383 1 : break;
3384 0 : default:
3385 0 : return NULL;
3386 : }
3387 :
3388 7 : tmp_ctx = talloc_new(mem_ctx);
3389 7 : if (tmp_ctx == NULL) {
3390 0 : return NULL;
3391 : }
3392 7 : ctx.mem_ctx = tmp_ctx;
3393 :
3394 7 : ok = claim_v1_to_ace_composite_unchecked(tmp_ctx, claim, &tok);
3395 7 : if (!ok) {
3396 0 : TALLOC_FREE(tmp_ctx);
3397 0 : return NULL;
3398 : }
3399 :
3400 : /* this will construct the proper string in ctx.sddl */
3401 7 : ok = write_resource_attr_from_token(&ctx, &tok);
3402 7 : if (!ok) {
3403 0 : TALLOC_FREE(tmp_ctx);
3404 0 : return NULL;
3405 : }
3406 :
3407 : /* escape the claim name */
3408 14 : ok = sddl_encode_attr_name(tmp_ctx,
3409 7 : claim->name,
3410 : &name, &name_len);
3411 :
3412 7 : if (!ok) {
3413 0 : TALLOC_FREE(tmp_ctx);
3414 0 : return NULL;
3415 : }
3416 :
3417 14 : s = talloc_asprintf(mem_ctx,
3418 : "(\"%s\",T%c,0x%x,%s)",
3419 : name,
3420 : attr_type,
3421 7 : claim->flags,
3422 : ctx.sddl);
3423 7 : TALLOC_FREE(tmp_ctx);
3424 7 : return s;
3425 : }
3426 :
3427 :
3428 40 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *parse_sddl_literal_as_claim(
3429 : TALLOC_CTX *mem_ctx,
3430 : const char *name,
3431 : const char *str)
3432 : {
3433 : /*
3434 : * For testing purposes (and possibly for client tools), we
3435 : * want to be able to create claim literals, and we might as
3436 : * well use the SDDL syntax. So we pretend to be parsing SDDL
3437 : * for one literal.
3438 : */
3439 40 : bool ok;
3440 40 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim = NULL;
3441 40 : struct ace_condition_sddl_compiler_context comp = {};
3442 :
3443 40 : ok = init_compiler_context(mem_ctx,
3444 : &comp,
3445 : ACE_CONDITION_FLAG_ALLOW_DEVICE,
3446 : str,
3447 : 2,
3448 : 2);
3449 40 : if (!ok) {
3450 0 : return NULL;
3451 : }
3452 :
3453 40 : comp.state = SDDL_FLAG_EXPECTING_LITERAL;
3454 40 : ok = parse_literal(&comp, false);
3455 :
3456 40 : if (!ok) {
3457 0 : goto error;
3458 : }
3459 40 : if (comp.program->length != 1) {
3460 0 : goto error;
3461 : }
3462 :
3463 80 : ok = ace_token_to_claim_v1(mem_ctx,
3464 : name,
3465 40 : &comp.program->tokens[0],
3466 : &claim,
3467 : 0);
3468 40 : if (!ok) {
3469 0 : goto error;
3470 : }
3471 40 : TALLOC_FREE(comp.program);
3472 40 : return claim;
3473 0 : error:
3474 0 : TALLOC_FREE(comp.program);
3475 0 : return NULL;
3476 : }
|