Line data Source code
1 : /*
2 : * Unix SMB implementation.
3 : * Utility functions for converting between claims formats.
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 "replace.h"
20 : #include "librpc/gen_ndr/ndr_security.h"
21 : #include "librpc/gen_ndr/ndr_conditional_ace.h"
22 : #include "libcli/security/claims-conversions.h"
23 : #include "lib/util/debug.h"
24 : #include "lib/util/stable_sort.h"
25 :
26 : #include "librpc/gen_ndr/conditional_ace.h"
27 : #include "librpc/gen_ndr/claims.h"
28 :
29 : /*
30 : * We support three formats for claims, all slightly different.
31 : *
32 : * 1. MS-ADTS 2.2.18.* claims sets, blobs, arrays, or whatever, which
33 : * are used in the PAC.
34 : *
35 : * 2. MS-DTYP 2.4.10.1 CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
36 : * structures, used in security tokens and resource SACL ACEs.
37 : *
38 : * 3. MS-DTYP 2.4.4.17 Conditional ACE tokens.
39 : *
40 : * The types don't map perfectly onto each other -- in particular,
41 : * Conditional ACEs don't have unsigned integer or boolean types, but
42 : * do have short integer types which the other forms don't.
43 : *
44 : * We don't support the format used by the Win32 API function
45 : * AddResourceAttributeAce(), which is called CLAIM_SECURITY_ATTRIBUTE_V1.
46 : * Nobody has ever used that function in public, and the format is not used
47 : * on the wire.
48 : */
49 :
50 :
51 4929 : static bool claim_v1_string_to_ace_string(
52 : TALLOC_CTX *mem_ctx,
53 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
54 : size_t offset,
55 : struct ace_condition_token *result)
56 : {
57 9431 : char *s = talloc_strdup(mem_ctx,
58 427 : claim->values[offset].string_value);
59 4929 : if (s == NULL) {
60 0 : return false;
61 : }
62 :
63 4929 : result->type = CONDITIONAL_ACE_TOKEN_UNICODE;
64 4929 : result->data.unicode.value = s;
65 4929 : return true;
66 : }
67 :
68 :
69 6 : static bool claim_v1_octet_string_to_ace_octet_string(
70 : TALLOC_CTX *mem_ctx,
71 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
72 : size_t offset,
73 : struct ace_condition_token *result)
74 : {
75 6 : DATA_BLOB *v = NULL;
76 6 : DATA_BLOB w = data_blob_null;
77 :
78 6 : v = claim->values[offset].octet_value;
79 :
80 6 : if (v->length > CONDITIONAL_ACE_MAX_LENGTH) {
81 0 : DBG_WARNING("claim has octet string of unexpected length %zu "
82 : "(expected range 1 - %u)\n",
83 : v->length, CONDITIONAL_ACE_MAX_LENGTH);
84 0 : return false;
85 : }
86 6 : if (v->length != 0) {
87 6 : w = data_blob_talloc(mem_ctx, v->data, v->length);
88 6 : if (w.data == NULL) {
89 0 : return false;
90 : }
91 : }
92 :
93 6 : result->type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
94 6 : result->data.bytes = w;
95 6 : return true;
96 : }
97 :
98 :
99 34 : static bool blob_string_sid_to_sid(DATA_BLOB *blob,
100 : struct dom_sid *sid)
101 : {
102 : /*
103 : * Resource ACE claim SIDs are stored as SID strings in
104 : * CLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_RELATIVE blobs. These are in
105 : * ACEs, which means we don't quite know who wrote them, and it is
106 : * unspecified whether the blob should contain a terminating NUL byte.
107 : * Therefore we accept either form, copying into a temporary buffer if
108 : * there is no '\0'. Apart from this special case, we don't accept
109 : * SIDs that are shorter than the blob.
110 : *
111 : * It doesn't seem like SDDL short SIDs ("WD") are accepted here. This
112 : * isn't SDDL.
113 : */
114 34 : bool ok;
115 34 : size_t len = blob->length;
116 34 : char buf[DOM_SID_STR_BUFLEN + 1]; /* 191 + 1 */
117 34 : const char *end = NULL;
118 34 : char *str = NULL;
119 :
120 34 : if (len < 5 || len >= DOM_SID_STR_BUFLEN) {
121 0 : return false;
122 : }
123 34 : if (blob->data[len - 1] == '\0') {
124 0 : str = (char *)blob->data;
125 0 : len--;
126 : } else {
127 34 : memcpy(buf, blob->data, len);
128 34 : buf[len] = 0;
129 34 : str = buf;
130 : }
131 :
132 34 : ok = dom_sid_parse_endp(str, sid, &end);
133 34 : if (!ok) {
134 0 : return false;
135 : }
136 :
137 34 : if (end - str != len) {
138 0 : return false;
139 : }
140 0 : return true;
141 : }
142 :
143 :
144 8 : static bool claim_v1_sid_to_ace_sid(
145 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
146 : size_t offset,
147 : struct ace_condition_token *result)
148 : {
149 : /*
150 : * In the _V1 struct, SIDs are stored as octet string blobs,
151 : * as *SID strings*.
152 : *
153 : * In the conditional ACE they are stored as struct dom_sid.
154 : *
155 : * There are no SIDs in ADTS claims, but there can be in
156 : * resource ACEs.
157 : */
158 8 : DATA_BLOB *v = NULL;
159 8 : bool ok;
160 :
161 8 : v = claim->values[offset].sid_value;
162 :
163 8 : ok = blob_string_sid_to_sid(v, &result->data.sid.sid);
164 8 : if (! ok) {
165 0 : DBG_WARNING("claim has invalid SID string of length %zu.\n",
166 : v->length);
167 0 : return false;
168 : }
169 :
170 8 : result->type = CONDITIONAL_ACE_TOKEN_SID;
171 8 : return true;
172 : }
173 :
174 :
175 13 : static bool claim_v1_int_to_ace_int(
176 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
177 : size_t offset,
178 : struct ace_condition_token *result)
179 : {
180 13 : int64_t v = *claim->values[offset].int_value;
181 13 : result->type = CONDITIONAL_ACE_TOKEN_INT64;
182 13 : result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
183 13 : result->data.int64.value = v;
184 :
185 : /*
186 : * The sign flag (and the base flag above) determines how the
187 : * ACE token will be displayed if converted to SDDL. These
188 : * values are not likely to end up as SDDL, but we might as
189 : * well get it right. A negative flag means it will be
190 : * displayed with a minus sign, and a positive flag means a
191 : * plus sign is shown. The none flag means no + or -.
192 : */
193 13 : if (v < 0) {
194 0 : result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NEGATIVE;
195 : } else {
196 13 : result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
197 : }
198 :
199 11 : return true;
200 : }
201 :
202 :
203 26 : static bool claim_v1_unsigned_int_to_ace_int(
204 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
205 : size_t offset,
206 : struct ace_condition_token *result)
207 : {
208 26 : uint64_t v = *claim->values[offset].uint_value;
209 26 : if (v > INT64_MAX) {
210 : /*
211 : * The unsigned value can't be represented in a
212 : * conditional ACE type.
213 : *
214 : * XXX or can it? does the positive flag make it
215 : * unsigned?
216 : */
217 0 : return false;
218 : }
219 26 : result->type = CONDITIONAL_ACE_TOKEN_INT64;
220 26 : result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
221 26 : result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_POSITIVE;
222 26 : result->data.int64.value = v;
223 26 : return true;
224 : }
225 :
226 :
227 54 : static bool claim_v1_bool_to_ace_int(
228 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
229 : size_t offset,
230 : struct ace_condition_token *result)
231 : {
232 54 : uint64_t v = *claim->values[offset].uint_value;
233 54 : result->type = CONDITIONAL_ACE_TOKEN_INT64;
234 54 : result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
235 54 : result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
236 54 : result->data.int64.value = v ? 1 : 0;
237 54 : return true;
238 : }
239 :
240 :
241 5036 : static bool claim_v1_offset_to_ace_token(
242 : TALLOC_CTX *mem_ctx,
243 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
244 : size_t offset,
245 : struct ace_condition_token *result)
246 : {
247 : /*
248 : * A claim structure has an array of claims of a certain type,
249 : * and this converts a single one into a conditional ACE token.
250 : *
251 : * For example, if offset is 3, claim->values[3] will be
252 : * turned into *result.
253 : *
254 : * conditional ace token will have flags to indicate that it
255 : * comes from a claim attribute, and whether or not that
256 : * attribute should be compared case-sensitively (only
257 : * affecting unicode strings).
258 : *
259 : * The CLAIM_SECURITY_ATTRIBUTE_CASE_SENSITIVE (from the
260 : * claim_flags enum in security.idl) is used for both.
261 : */
262 5036 : uint8_t f = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
263 5036 : result->flags = f | CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR;
264 :
265 5036 : if (claim->values[offset].int_value == NULL) {
266 0 : return false;
267 : }
268 5036 : switch (claim->value_type) {
269 11 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
270 13 : return claim_v1_int_to_ace_int(claim, offset, result);
271 25 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
272 26 : return claim_v1_unsigned_int_to_ace_int(claim, offset, result);
273 427 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
274 4929 : return claim_v1_string_to_ace_string(mem_ctx, claim, offset,
275 : result);
276 8 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
277 8 : return claim_v1_sid_to_ace_sid(claim, offset, result);
278 54 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
279 54 : return claim_v1_bool_to_ace_int(claim, offset, result);
280 6 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
281 6 : return claim_v1_octet_string_to_ace_octet_string(mem_ctx,
282 : claim,
283 : offset,
284 : result);
285 0 : default:
286 0 : return false;
287 : }
288 : }
289 :
290 :
291 : static bool claim_v1_copy(
292 : TALLOC_CTX *mem_ctx,
293 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *dest,
294 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *src);
295 :
296 :
297 :
298 203 : bool claim_v1_to_ace_composite_unchecked(
299 : TALLOC_CTX *mem_ctx,
300 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
301 : struct ace_condition_token *result)
302 : {
303 : /*
304 : * This converts a claim object into a conditional ACE
305 : * composite without checking whether it is a valid and sorted
306 : * claim. It is called in two places:
307 : *
308 : * 1. claim_v1_to_ace_token() below (which does do those
309 : * checks, and is the function you want).
310 : *
311 : * 2. sddl_resource_attr_from_claim() in which a resource
312 : * attribute claim needs to pass through a conditional ACE
313 : * composite structure on its way to becoming SDDL. In that
314 : * case we don't want to check validity.
315 : */
316 92 : size_t i;
317 203 : struct ace_condition_token *tokens = NULL;
318 92 : bool ok;
319 :
320 203 : tokens = talloc_array(mem_ctx,
321 : struct ace_condition_token,
322 : claim->value_count);
323 203 : if (tokens == NULL) {
324 0 : return false;
325 : }
326 :
327 4937 : for (i = 0; i < claim->value_count; i++) {
328 9228 : ok = claim_v1_offset_to_ace_token(tokens,
329 : claim,
330 : i,
331 4734 : &tokens[i]);
332 4734 : if (! ok) {
333 0 : TALLOC_FREE(tokens);
334 0 : return false;
335 : }
336 : }
337 :
338 203 : result->type = CONDITIONAL_ACE_TOKEN_COMPOSITE;
339 203 : result->data.composite.tokens = tokens;
340 203 : result->data.composite.n_members = claim->value_count;
341 203 : result->flags = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
342 203 : return true;
343 : }
344 :
345 :
346 512 : bool claim_v1_to_ace_token(TALLOC_CTX *mem_ctx,
347 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
348 : struct ace_condition_token *result)
349 : {
350 512 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim_copy = NULL;
351 512 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *sorted_claim = NULL;
352 117 : NTSTATUS status;
353 117 : bool ok;
354 512 : bool case_sensitive = claim->flags & \
355 : CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
356 :
357 512 : if (claim->value_count < 1 ||
358 389 : claim->value_count >= CONDITIONAL_ACE_MAX_TOKENS) {
359 10 : DBG_WARNING("rejecting claim with %"PRIu32" tokens\n",
360 : claim->value_count);
361 10 : return false;
362 : }
363 : /*
364 : * if there is one, we return a single thing of that type; if
365 : * there are many, we return a composite.
366 : */
367 :
368 502 : if (claim->value_count == 1) {
369 302 : return claim_v1_offset_to_ace_token(mem_ctx,
370 : claim,
371 : 0,
372 : result);
373 : }
374 :
375 200 : if (claim->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED) {
376 : /*
377 : * We can avoid making a sorted copy.
378 : *
379 : * This is normal case for wire claims, where the
380 : * sorting and duplicate checking happens earlier in
381 : * token_claims_to_claims_v1().
382 : */
383 111 : sorted_claim = claim;
384 : } else {
385 : /*
386 : * This is presumably a resource attribute ACE, which
387 : * is stored in the ACE as struct
388 : * CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, and we don't
389 : * really want to mutate that copy -- even if there
390 : * aren't currently realistic pathways that read an
391 : * ACE, trigger this, and write it back (outside of
392 : * tests).
393 : */
394 62 : claim_copy = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
395 62 : if (claim_copy == NULL) {
396 0 : return false;
397 : }
398 :
399 62 : ok = claim_v1_copy(claim_copy, claim_copy, claim);
400 62 : if (!ok) {
401 0 : TALLOC_FREE(claim_copy);
402 0 : return false;
403 : }
404 :
405 62 : status = claim_v1_check_and_sort(claim_copy, claim_copy,
406 : case_sensitive);
407 62 : if (!NT_STATUS_IS_OK(status)) {
408 4 : DBG_WARNING("resource attribute claim sort failed with %s\n",
409 : nt_errstr(status));
410 4 : TALLOC_FREE(claim_copy);
411 4 : return false;
412 : }
413 0 : sorted_claim = claim_copy;
414 : }
415 196 : ok = claim_v1_to_ace_composite_unchecked(mem_ctx, sorted_claim, result);
416 196 : if (! ok) {
417 0 : TALLOC_FREE(claim_copy);
418 0 : return false;
419 : }
420 :
421 : /*
422 : * The multiple values will get turned into a composite
423 : * literal in the conditional ACE. Each element of the
424 : * composite will have flags set by
425 : * claim_v1_offset_to_ace_token(), but they also need to be
426 : * set here (at least the _FROM_ATTR flag) or the child values
427 : * will not be reached.
428 : */
429 196 : result->flags |= (
430 : CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR |
431 : CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED);
432 :
433 196 : return true;
434 : }
435 :
436 :
437 :
438 478 : static bool ace_int_to_claim_v1_int(TALLOC_CTX *mem_ctx,
439 : const struct ace_condition_token *tok,
440 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
441 : size_t offset)
442 : {
443 956 : int64_t *v = talloc(mem_ctx, int64_t);
444 478 : if (v == NULL) {
445 0 : return false;
446 : }
447 478 : *v = tok->data.int64.value;
448 478 : claim->values[offset].int_value = v;
449 478 : return true;
450 : }
451 :
452 :
453 356 : static bool ace_string_to_claim_v1_string(TALLOC_CTX *mem_ctx,
454 : const struct ace_condition_token *tok,
455 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
456 : size_t offset)
457 : {
458 712 : const char *s = talloc_strdup(mem_ctx,
459 356 : tok->data.unicode.value);
460 356 : if (s == NULL) {
461 0 : return false;
462 : }
463 356 : claim->values[offset].string_value = s;
464 356 : return true;
465 :
466 : }
467 :
468 :
469 11 : static bool ace_sid_to_claim_v1_sid(TALLOC_CTX *mem_ctx,
470 : const struct ace_condition_token *tok,
471 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
472 : size_t offset)
473 : {
474 : /* claim_v1 sid is an "S-1-*" string data blob, not struct dom_sid. */
475 11 : char *s = NULL;
476 :
477 11 : DATA_BLOB *blob = NULL;
478 11 : blob = talloc(mem_ctx, DATA_BLOB);
479 11 : if (blob == NULL) {
480 0 : return false;
481 : }
482 11 : s = dom_sid_string(blob, &tok->data.sid.sid);
483 11 : if (s == NULL) {
484 0 : TALLOC_FREE(blob);
485 0 : return false;
486 : }
487 11 : *blob = data_blob_string_const(s);
488 11 : claim->values[offset].sid_value = blob;
489 11 : return true;
490 : }
491 :
492 522 : static bool ace_octet_string_to_claim_v1_octet_string(
493 : TALLOC_CTX *mem_ctx,
494 : const struct ace_condition_token *tok,
495 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
496 : size_t offset)
497 : {
498 522 : DATA_BLOB *v = talloc(mem_ctx, DATA_BLOB);
499 522 : if (v == NULL) {
500 0 : return false;
501 : }
502 :
503 522 : *v = data_blob_talloc(v,
504 : tok->data.bytes.data,
505 : tok->data.bytes.length);
506 522 : if (v->data == NULL) {
507 0 : return false;
508 : }
509 :
510 522 : claim->values[offset].octet_value = v;
511 522 : return true;
512 : }
513 :
514 :
515 :
516 1367 : static bool ace_token_to_claim_v1_offset(TALLOC_CTX *mem_ctx,
517 : const struct ace_condition_token *tok,
518 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
519 : size_t offset)
520 : {
521 : /*
522 : * A claim structure has an array of claims of a certain type,
523 : * and this converts a single one into a conditional ACE token.
524 : *
525 : * For example, if offset is 3, claim->values[3] will be
526 : * turned into *result.
527 : */
528 1367 : if (offset >= claim->value_count) {
529 0 : return false;
530 : }
531 1367 : switch (claim->value_type) {
532 0 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
533 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
534 478 : return ace_int_to_claim_v1_int(mem_ctx, tok, claim, offset);
535 0 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
536 356 : return ace_string_to_claim_v1_string(mem_ctx, tok, claim, offset);
537 11 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
538 11 : return ace_sid_to_claim_v1_sid(mem_ctx, tok, claim, offset);
539 522 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
540 522 : return ace_octet_string_to_claim_v1_octet_string(mem_ctx,
541 : tok,
542 : claim,
543 : offset);
544 0 : default:
545 : /*bool unimplemented, because unreachable */
546 0 : return false;
547 : }
548 : }
549 :
550 :
551 145 : bool ace_token_to_claim_v1(TALLOC_CTX *mem_ctx,
552 : const char *name,
553 : const struct ace_condition_token *tok,
554 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **claim,
555 : uint32_t flags)
556 : {
557 145 : size_t i;
558 145 : bool ok;
559 145 : bool is_comp = false;
560 145 : int claim_type = -1;
561 145 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *_claim = NULL;
562 145 : uint32_t value_count;
563 :
564 145 : if (name == NULL || claim == NULL || tok == NULL) {
565 0 : return false;
566 : }
567 145 : *claim = NULL;
568 :
569 145 : if (tok->type == CONDITIONAL_ACE_TOKEN_COMPOSITE) {
570 102 : is_comp = true;
571 : /* there must be values, all of the same type */
572 102 : if (tok->data.composite.n_members == 0) {
573 0 : DBG_WARNING("Empty ACE composite list\n");
574 0 : return false;
575 : }
576 102 : if (tok->data.composite.n_members > 1) {
577 1324 : for (i = 1; i < tok->data.composite.n_members; i++) {
578 1222 : if (tok->data.composite.tokens[i].type !=
579 1222 : tok->data.composite.tokens[0].type) {
580 0 : DBG_WARNING(
581 : "ACE composite list has varying "
582 : "types (at least %u and %u)\n",
583 : tok->data.composite.tokens[i].type,
584 : tok->data.composite.tokens[0].type);
585 0 : return false;
586 : }
587 : }
588 : }
589 102 : value_count = tok->data.composite.n_members;
590 :
591 102 : switch (tok->data.composite.tokens[0].type) {
592 0 : case CONDITIONAL_ACE_TOKEN_INT8:
593 : case CONDITIONAL_ACE_TOKEN_INT16:
594 : case CONDITIONAL_ACE_TOKEN_INT32:
595 : case CONDITIONAL_ACE_TOKEN_INT64:
596 0 : claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
597 0 : break;
598 46 : case CONDITIONAL_ACE_TOKEN_UNICODE:
599 46 : claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
600 46 : break;
601 13 : case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
602 13 : claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING;
603 13 : break;
604 5 : case CONDITIONAL_ACE_TOKEN_SID:
605 5 : claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID;
606 5 : break;
607 0 : default:
608 : /* reject nested composites, no uint or bool. */
609 0 : DBG_WARNING("ACE composite list has invalid type %u\n",
610 : tok->data.composite.tokens[0].type);
611 0 : return false;
612 : }
613 : } else {
614 43 : value_count = 1;
615 43 : switch(tok->type) {
616 0 : case CONDITIONAL_ACE_TOKEN_INT8:
617 : case CONDITIONAL_ACE_TOKEN_INT16:
618 : case CONDITIONAL_ACE_TOKEN_INT32:
619 : case CONDITIONAL_ACE_TOKEN_INT64:
620 0 : claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
621 0 : break;
622 21 : case CONDITIONAL_ACE_TOKEN_UNICODE:
623 21 : claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
624 21 : break;
625 1 : case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
626 1 : claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING;
627 1 : break;
628 0 : case CONDITIONAL_ACE_TOKEN_SID:
629 0 : claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID;
630 0 : break;
631 0 : default:
632 : /*
633 : * no way of creating bool or uint values,
634 : * composite is handled above.
635 : */
636 0 : DBG_WARNING("ACE token has invalid type %u\n",
637 : tok->data.composite.tokens[0].type);
638 0 : return false;
639 : }
640 : }
641 :
642 145 : _claim = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
643 145 : if (_claim == NULL) {
644 0 : return false;
645 : }
646 :
647 145 : _claim->value_count = value_count;
648 145 : _claim->value_type = claim_type;
649 145 : _claim->flags = flags;
650 145 : _claim->name = talloc_strdup(mem_ctx, name);
651 145 : if (_claim->name == NULL) {
652 0 : TALLOC_FREE(_claim);
653 0 : return false;
654 : }
655 : /*
656 : * The values array is actually an array of pointers to
657 : * values, even when the values are ints or bools.
658 : */
659 145 : _claim->values = talloc_array(_claim, union claim_values, value_count);
660 145 : if (_claim->values == NULL) {
661 0 : TALLOC_FREE(_claim);
662 0 : return false;
663 : }
664 145 : if (! is_comp) {
665 : /* there is one value, not a list */
666 43 : ok = ace_token_to_claim_v1_offset(_claim,
667 : tok,
668 : _claim,
669 : 0);
670 43 : if (! ok) {
671 0 : TALLOC_FREE(_claim);
672 0 : return false;
673 : }
674 : } else {
675 : /* a composite list of values */
676 1426 : for (i = 0; i < value_count; i++) {
677 1324 : struct ace_condition_token *t = &tok->data.composite.tokens[i];
678 1324 : ok = ace_token_to_claim_v1_offset(mem_ctx,
679 : t,
680 : _claim,
681 : i);
682 1324 : if (! ok) {
683 0 : TALLOC_FREE(_claim);
684 0 : return false;
685 : }
686 : }
687 : }
688 :
689 :
690 145 : if (_claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) {
691 : /*
692 : * Conditional ACE tokens don't have a UINT type but
693 : * claims do. Windows tends to use UINT types in
694 : * claims when it can, so so do we.
695 : */
696 427 : bool could_be_uint = true;
697 427 : for (i = 0; i < value_count; i++) {
698 374 : if (*_claim->values[i].int_value < 0) {
699 0 : could_be_uint = false;
700 0 : break;
701 : }
702 : }
703 59 : if (could_be_uint) {
704 53 : _claim->value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64;
705 : }
706 : }
707 :
708 145 : *claim = _claim;
709 145 : return true;
710 : }
711 :
712 :
713 :
714 102 : static bool claim_v1_copy(
715 : TALLOC_CTX *mem_ctx,
716 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *dest,
717 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *src)
718 : {
719 102 : DATA_BLOB blob = {0};
720 102 : enum ndr_err_code ndr_err;
721 :
722 : /*
723 : * FIXME, could be more efficient! but copying these
724 : * structures is fiddly, and it might be worth coming up
725 : * with a better API for adding claims.
726 : */
727 :
728 102 : ndr_err = ndr_push_struct_blob(
729 : &blob, mem_ctx, src,
730 : (ndr_push_flags_fn_t)ndr_push_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
731 :
732 102 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
733 0 : return false;
734 : }
735 :
736 102 : ndr_err = ndr_pull_struct_blob(
737 : &blob, mem_ctx, dest,
738 : (ndr_pull_flags_fn_t)ndr_pull_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
739 :
740 102 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
741 0 : TALLOC_FREE(blob.data);
742 0 : return false;
743 : }
744 102 : TALLOC_FREE(blob.data);
745 0 : return true;
746 : }
747 :
748 :
749 :
750 40 : bool add_claim_to_token(TALLOC_CTX *mem_ctx,
751 : struct security_token *token,
752 : const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
753 : const char *claim_type)
754 : {
755 40 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *tmp = NULL;
756 40 : NTSTATUS status;
757 40 : uint32_t *n = NULL;
758 40 : bool ok;
759 40 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **list = NULL;
760 40 : if (strcmp(claim_type, "device") == 0) {
761 38 : n = &token->num_device_claims;
762 38 : list = &token->device_claims;
763 2 : } else if (strcmp(claim_type, "local") == 0) {
764 0 : n = &token->num_local_claims;
765 0 : list = &token->local_claims;
766 2 : } else if (strcmp(claim_type, "user") == 0) {
767 2 : n = &token->num_user_claims;
768 2 : list = &token->user_claims;
769 : } else {
770 0 : return false;
771 : }
772 40 : if ((*n) == UINT32_MAX) {
773 0 : return false;
774 : }
775 :
776 40 : tmp = talloc_realloc(mem_ctx,
777 : *list,
778 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1,
779 : (*n) + 1);
780 40 : if (tmp == NULL) {
781 0 : return false;
782 : }
783 :
784 40 : ok = claim_v1_copy(mem_ctx, &tmp[*n], claim);
785 40 : if (! ok ) {
786 0 : TALLOC_FREE(tmp);
787 0 : return false;
788 : }
789 :
790 40 : status = claim_v1_check_and_sort(tmp, &tmp[*n],
791 40 : claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE);
792 40 : if (!NT_STATUS_IS_OK(status)) {
793 2 : DBG_WARNING("resource attribute claim sort failed with %s\n",
794 : nt_errstr(status));
795 2 : TALLOC_FREE(tmp);
796 2 : return false;
797 : }
798 :
799 38 : (*n)++;
800 38 : *list = tmp;
801 38 : return true;
802 : }
803 :
804 :
805 54 : static NTSTATUS claim_v1_check_and_sort_boolean(
806 : TALLOC_CTX *mem_ctx,
807 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim)
808 : {
809 : /*
810 : * There are so few valid orders in a boolean claim that we can
811 : * enumerate them all.
812 : */
813 54 : switch (claim->value_count) {
814 4 : case 0:
815 49 : return NT_STATUS_OK;
816 44 : case 1:
817 44 : if (*claim->values[0].uint_value == 0 ||
818 23 : *claim->values[0].uint_value == 1) {
819 43 : return NT_STATUS_OK;
820 : }
821 1 : break;
822 6 : case 2:
823 6 : if (*claim->values[0].uint_value == 1) {
824 : /* switch the order. */
825 1 : *claim->values[0].uint_value = *claim->values[1].uint_value;
826 1 : *claim->values[1].uint_value = 1;
827 : }
828 6 : if (*claim->values[0].uint_value == 0 &&
829 6 : *claim->values[1].uint_value == 1) {
830 2 : return NT_STATUS_OK;
831 : }
832 4 : break;
833 0 : default:
834 : /* 3 or more must have duplicates. */
835 0 : break;
836 : }
837 5 : return NT_STATUS_INVALID_PARAMETER;
838 : }
839 :
840 :
841 : struct claim_sort_context {
842 : uint16_t value_type;
843 : bool failed;
844 : bool case_sensitive;
845 : };
846 :
847 929208 : static int claim_sort_cmp(const union claim_values *lhs,
848 : const union claim_values *rhs,
849 : struct claim_sort_context *ctx)
850 : {
851 : /*
852 : * These comparisons have to match those used in
853 : * conditional_ace.c.
854 : */
855 34458 : int cmp;
856 :
857 929208 : switch (ctx->value_type) {
858 894246 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
859 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
860 : {
861 : /*
862 : * We sort as signed integers, even for uint64,
863 : * because a) we don't actually care about the true
864 : * order, just uniqueness, and b) the conditional ACEs
865 : * only know of signed values.
866 : */
867 0 : int64_t a, b;
868 894246 : if (ctx->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) {
869 14 : a = *lhs->int_value;
870 14 : b = *rhs->int_value;
871 : } else {
872 894232 : a = (int64_t)*lhs->uint_value;
873 894232 : b = (int64_t)*rhs->uint_value;
874 : }
875 894246 : if (a < b) {
876 808504 : return -1;
877 : }
878 85742 : if (a == b) {
879 18 : return 0;
880 : }
881 85724 : return 1;
882 : }
883 34949 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
884 : {
885 34949 : const char *a = lhs->string_value;
886 34949 : const char *b = rhs->string_value;
887 34949 : if (ctx->case_sensitive) {
888 1359 : return strcmp(a, b);
889 : }
890 33590 : return strcasecmp_m(a, b);
891 : }
892 :
893 13 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
894 : {
895 : /*
896 : * The blobs in a claim are "S-1-.." strings, not struct
897 : * dom_sid as used in conditional ACEs, and to sort them the
898 : * same as ACEs we need to make temporary structs.
899 : *
900 : * We don't accept SID claims over the wire -- these
901 : * are resource attribute ACEs only.
902 : */
903 13 : struct dom_sid a, b;
904 13 : bool lhs_ok, rhs_ok;
905 :
906 13 : lhs_ok = blob_string_sid_to_sid(lhs->sid_value, &a);
907 13 : rhs_ok = blob_string_sid_to_sid(rhs->sid_value, &b);
908 13 : if (!(lhs_ok && rhs_ok)) {
909 0 : ctx->failed = true;
910 0 : return -1;
911 : }
912 13 : cmp = dom_sid_compare(&a, &b);
913 13 : return cmp;
914 : }
915 0 : case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
916 : {
917 0 : const DATA_BLOB *a = lhs->octet_value;
918 0 : const DATA_BLOB *b = rhs->octet_value;
919 0 : return data_blob_cmp(a, b);
920 : }
921 0 : default:
922 0 : ctx->failed = true;
923 0 : break;
924 : }
925 0 : return -1;
926 : }
927 :
928 :
929 861 : NTSTATUS claim_v1_check_and_sort(TALLOC_CTX *mem_ctx,
930 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
931 : bool case_sensitive)
932 : {
933 116 : bool ok;
934 116 : uint32_t i;
935 861 : struct claim_sort_context sort_ctx = {
936 : .failed = false,
937 861 : .value_type = claim->value_type,
938 : .case_sensitive = case_sensitive
939 : };
940 :
941 : /*
942 : * It could be that the values array contains a NULL pointer, in which
943 : * case we don't need to worry about what type it is.
944 : */
945 106678 : for (i = 0; i < claim->value_count; i++) {
946 105817 : if (claim->values[i].int_value == NULL) {
947 0 : return NT_STATUS_INVALID_PARAMETER;
948 : }
949 : }
950 :
951 861 : if (claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN) {
952 54 : NTSTATUS status = claim_v1_check_and_sort_boolean(mem_ctx, claim);
953 54 : if (NT_STATUS_IS_OK(status)) {
954 49 : claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
955 : }
956 54 : return status;
957 : }
958 :
959 923 : ok = stable_sort_talloc_r(mem_ctx,
960 807 : claim->values,
961 691 : claim->value_count,
962 : sizeof(union claim_values),
963 : (samba_compare_with_context_fn_t)claim_sort_cmp,
964 : &sort_ctx);
965 807 : if (!ok) {
966 0 : return NT_STATUS_NO_MEMORY;
967 : }
968 :
969 807 : if (sort_ctx.failed) {
970 : /* this failure probably means a bad SID string */
971 0 : DBG_WARNING("claim sort of %"PRIu32" members, type %"PRIu16" failed\n",
972 : claim->value_count,
973 : claim->value_type);
974 0 : return NT_STATUS_INVALID_PARAMETER;
975 : }
976 :
977 105462 : for (i = 1; i < claim->value_count; i++) {
978 109082 : int cmp = claim_sort_cmp(&claim->values[i - 1],
979 104673 : &claim->values[i],
980 : &sort_ctx);
981 104673 : if (cmp == 0) {
982 18 : DBG_WARNING("duplicate values in claim\n");
983 18 : return NT_STATUS_INVALID_PARAMETER;
984 : }
985 104655 : if (cmp > 0) {
986 0 : DBG_ERR("claim sort failed!\n");
987 0 : return NT_STATUS_INVALID_PARAMETER;
988 : }
989 : }
990 789 : if (case_sensitive) {
991 8 : claim->flags |= CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
992 : }
993 789 : claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
994 789 : return NT_STATUS_OK;
995 : }
996 :
997 :
998 304 : NTSTATUS token_claims_to_claims_v1(TALLOC_CTX *mem_ctx,
999 : const struct CLAIMS_SET *claims_set,
1000 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **out_claims,
1001 : uint32_t *out_n_claims)
1002 : {
1003 304 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claims = NULL;
1004 304 : uint32_t n_claims = 0;
1005 304 : uint32_t expected_n_claims = 0;
1006 6 : uint32_t i;
1007 6 : NTSTATUS status;
1008 :
1009 304 : if (out_claims == NULL) {
1010 0 : return NT_STATUS_INVALID_PARAMETER;
1011 : }
1012 304 : if (out_n_claims == NULL) {
1013 0 : return NT_STATUS_INVALID_PARAMETER;
1014 : }
1015 :
1016 304 : *out_claims = NULL;
1017 304 : *out_n_claims = 0;
1018 :
1019 304 : if (claims_set == NULL) {
1020 0 : return NT_STATUS_OK;
1021 : }
1022 :
1023 : /*
1024 : * The outgoing number of claims is (at most) the sum of the
1025 : * claims_counts of each claims_array.
1026 : */
1027 618 : for (i = 0; i < claims_set->claims_array_count; ++i) {
1028 314 : uint32_t count = claims_set->claims_arrays[i].claims_count;
1029 314 : expected_n_claims += count;
1030 314 : if (expected_n_claims < count) {
1031 0 : return NT_STATUS_INVALID_PARAMETER;
1032 : }
1033 : }
1034 :
1035 304 : claims = talloc_array(mem_ctx,
1036 : struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1,
1037 : expected_n_claims);
1038 304 : if (claims == NULL) {
1039 0 : return NT_STATUS_NO_MEMORY;
1040 : }
1041 :
1042 601 : for (i = 0; i < claims_set->claims_array_count; ++i) {
1043 314 : const struct CLAIMS_ARRAY *claims_array = &claims_set->claims_arrays[i];
1044 6 : uint32_t j;
1045 :
1046 314 : switch (claims_array->claims_source_type) {
1047 302 : case CLAIMS_SOURCE_TYPE_AD:
1048 : case CLAIMS_SOURCE_TYPE_CERTIFICATE:
1049 302 : break;
1050 7 : default:
1051 : /* Ignore any claims of a type we don’t recognize. */
1052 7 : continue;
1053 : }
1054 :
1055 1053 : for (j = 0; j < claims_array->claims_count; ++j) {
1056 763 : const struct CLAIM_ENTRY *claim_entry = &claims_array->claim_entries[j];
1057 763 : const char *name = NULL;
1058 763 : union claim_values *claim_values = NULL;
1059 14 : uint32_t n_values;
1060 14 : enum security_claim_value_type value_type;
1061 :
1062 763 : switch (claim_entry->type) {
1063 15 : case CLAIM_TYPE_INT64:
1064 : {
1065 15 : const struct CLAIM_INT64 *values = &claim_entry->values.claim_int64;
1066 0 : uint32_t k;
1067 15 : int64_t *claim_values_int64 = NULL;
1068 :
1069 15 : n_values = values->value_count;
1070 15 : value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
1071 :
1072 15 : claim_values = talloc_array(claims,
1073 : union claim_values,
1074 : n_values);
1075 15 : if (claim_values == NULL) {
1076 0 : talloc_free(claims);
1077 0 : return NT_STATUS_NO_MEMORY;
1078 : }
1079 15 : claim_values_int64 = talloc_array(claims,
1080 : int64_t,
1081 : n_values);
1082 15 : if (claim_values_int64 == NULL) {
1083 0 : talloc_free(claims);
1084 0 : return NT_STATUS_NO_MEMORY;
1085 : }
1086 :
1087 36 : for (k = 0; k < n_values; ++k) {
1088 21 : claim_values_int64[k] = values->values[k];
1089 21 : claim_values[k].int_value = &claim_values_int64[k];
1090 : }
1091 :
1092 15 : break;
1093 : }
1094 81 : case CLAIM_TYPE_UINT64:
1095 : case CLAIM_TYPE_BOOLEAN:
1096 : {
1097 81 : const struct CLAIM_UINT64 *values = &claim_entry->values.claim_uint64;
1098 1 : uint32_t k;
1099 81 : uint64_t *claim_values_uint64 = NULL;
1100 :
1101 81 : n_values = values->value_count;
1102 162 : value_type = (claim_entry->type == CLAIM_TYPE_UINT64)
1103 : ? CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64
1104 81 : : CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN;
1105 :
1106 81 : claim_values = talloc_array(claims,
1107 : union claim_values,
1108 : n_values);
1109 81 : if (claim_values == NULL) {
1110 0 : talloc_free(claims);
1111 0 : return NT_STATUS_NO_MEMORY;
1112 : }
1113 :
1114 81 : claim_values_uint64 = talloc_array(claims,
1115 : uint64_t,
1116 : n_values);
1117 81 : if (claim_values_uint64 == NULL) {
1118 0 : talloc_free(claims);
1119 0 : return NT_STATUS_NO_MEMORY;
1120 : }
1121 :
1122 100175 : for (k = 0; k < n_values; ++k) {
1123 100094 : claim_values_uint64[k] = values->values[k];
1124 100094 : claim_values[k].uint_value = &claim_values_uint64[k];
1125 : }
1126 :
1127 80 : break;
1128 : }
1129 663 : case CLAIM_TYPE_STRING:
1130 : {
1131 663 : const struct CLAIM_STRING *values = &claim_entry->values.claim_string;
1132 13 : uint32_t k, m;
1133 663 : bool seen_empty = false;
1134 663 : n_values = values->value_count;
1135 663 : value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
1136 :
1137 663 : claim_values = talloc_array(claims,
1138 : union claim_values,
1139 : n_values);
1140 663 : if (claim_values == NULL) {
1141 0 : talloc_free(claims);
1142 0 : return NT_STATUS_NO_MEMORY;
1143 : }
1144 :
1145 650 : m = 0;
1146 1572 : for (k = 0; k < n_values; ++k) {
1147 909 : const char *string_value = NULL;
1148 :
1149 909 : if (values->values[k] != NULL) {
1150 909 : string_value = talloc_strdup(claim_values, values->values[k]);
1151 909 : if (string_value == NULL) {
1152 0 : talloc_free(claims);
1153 0 : return NT_STATUS_NO_MEMORY;
1154 : }
1155 909 : claim_values[m].string_value = string_value;
1156 909 : m++;
1157 : } else {
1158 : /*
1159 : * We allow one NULL string
1160 : * per claim, but not two,
1161 : * because two would be a
1162 : * duplicate, and we don't
1163 : * want those (duplicates in
1164 : * actual values are checked
1165 : * later).
1166 : */
1167 0 : if (seen_empty) {
1168 0 : talloc_free(claims);
1169 0 : return NT_STATUS_INVALID_PARAMETER;
1170 : }
1171 0 : seen_empty = true;
1172 : }
1173 : }
1174 650 : n_values = m;
1175 650 : break;
1176 : }
1177 4 : default:
1178 : /*
1179 : * Other claim types are unsupported — just skip
1180 : * them.
1181 : */
1182 4 : continue;
1183 : }
1184 :
1185 759 : if (claim_entry->id != NULL) {
1186 759 : name = talloc_strdup(claims, claim_entry->id);
1187 759 : if (name == NULL) {
1188 0 : talloc_free(claims);
1189 0 : return NT_STATUS_NO_MEMORY;
1190 : }
1191 : }
1192 :
1193 759 : claims[n_claims] = (struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1) {
1194 : .name = name,
1195 : .value_type = value_type,
1196 : .flags = 0,
1197 : .value_count = n_values,
1198 : .values = claim_values,
1199 : };
1200 :
1201 759 : status = claim_v1_check_and_sort(claims, &claims[n_claims],
1202 : false);
1203 759 : if (!NT_STATUS_IS_OK(status)) {
1204 17 : talloc_free(claims);
1205 17 : DBG_WARNING("claim sort and uniqueness test failed with %s\n",
1206 : nt_errstr(status));
1207 17 : return status;
1208 : }
1209 742 : n_claims++;
1210 : }
1211 : }
1212 287 : *out_claims = claims;
1213 287 : *out_n_claims = n_claims;
1214 :
1215 287 : return NT_STATUS_OK;
1216 : }
|