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