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