ctdb-scripts: Improve update and listing code
[samba4-gss.git] / libcli / security / claims-conversions.c
blob6983cb36f062a793e0c06212e62f977b1233c0bc
1 /*
2 * Unix SMB implementation.
3 * Utility functions for converting between claims formats.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
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 #include "libcli/security/dom_sid.h"
27 #include "librpc/gen_ndr/conditional_ace.h"
28 #include "librpc/gen_ndr/claims.h"
31 * We support three formats for claims, all slightly different.
33 * 1. MS-ADTS 2.2.18.* claims sets, blobs, arrays, or whatever, which
34 * are used in the PAC.
36 * 2. MS-DTYP 2.4.10.1 CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
37 * structures, used in security tokens and resource SACL ACEs.
39 * 3. MS-DTYP 2.4.4.17 Conditional ACE tokens.
41 * The types don't map perfectly onto each other -- in particular,
42 * Conditional ACEs don't have unsigned integer or boolean types, but
43 * do have short integer types which the other forms don't.
45 * We don't support the format used by the Win32 API function
46 * AddResourceAttributeAce(), which is called CLAIM_SECURITY_ATTRIBUTE_V1.
47 * Nobody has ever used that function in public, and the format is not used
48 * on the wire.
52 static bool claim_v1_string_to_ace_string(
53 TALLOC_CTX *mem_ctx,
54 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
55 size_t offset,
56 struct ace_condition_token *result)
58 char *s = talloc_strdup(mem_ctx,
59 claim->values[offset].string_value);
60 if (s == NULL) {
61 return false;
64 result->type = CONDITIONAL_ACE_TOKEN_UNICODE;
65 result->data.unicode.value = s;
66 return true;
70 static bool claim_v1_octet_string_to_ace_octet_string(
71 TALLOC_CTX *mem_ctx,
72 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
73 size_t offset,
74 struct ace_condition_token *result)
76 DATA_BLOB *v = NULL;
77 DATA_BLOB w = data_blob_null;
79 v = claim->values[offset].octet_value;
81 if (v->length > CONDITIONAL_ACE_MAX_LENGTH) {
82 DBG_WARNING("claim has octet string of unexpected length %zu "
83 "(expected range 1 - %u)\n",
84 v->length, CONDITIONAL_ACE_MAX_LENGTH);
85 return false;
87 if (v->length != 0) {
88 w = data_blob_talloc(mem_ctx, v->data, v->length);
89 if (w.data == NULL) {
90 return false;
94 result->type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
95 result->data.bytes = w;
96 return true;
100 static bool blob_string_sid_to_sid(DATA_BLOB *blob,
101 struct dom_sid *sid)
104 * Resource ACE claim SIDs are stored as SID strings in
105 * CLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_RELATIVE blobs. These are in
106 * ACEs, which means we don't quite know who wrote them, and it is
107 * unspecified whether the blob should contain a terminating NUL byte.
108 * Therefore we accept either form, copying into a temporary buffer if
109 * there is no '\0'. Apart from this special case, we don't accept
110 * SIDs that are shorter than the blob.
112 * It doesn't seem like SDDL short SIDs ("WD") are accepted here. This
113 * isn't SDDL.
115 bool ok;
116 size_t len = blob->length;
117 char buf[DOM_SID_STR_BUFLEN + 1]; /* 191 + 1 */
118 const char *end = NULL;
119 char *str = NULL;
121 if (len < 5 || len >= DOM_SID_STR_BUFLEN) {
122 return false;
124 if (blob->data[len - 1] == '\0') {
125 str = (char *)blob->data;
126 len--;
127 } else {
128 memcpy(buf, blob->data, len);
129 buf[len] = 0;
130 str = buf;
133 ok = dom_sid_parse_endp(str, sid, &end);
134 if (!ok) {
135 return false;
138 if (str + len != end) {
139 return false;
141 return true;
145 static bool claim_v1_sid_to_ace_sid(
146 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
147 size_t offset,
148 struct ace_condition_token *result)
151 * In the _V1 struct, SIDs are stored as octet string blobs,
152 * as *SID strings*.
154 * In the conditional ACE they are stored as struct dom_sid.
156 * There are no SIDs in ADTS claims, but there can be in
157 * resource ACEs.
159 DATA_BLOB *v = NULL;
160 bool ok;
162 v = claim->values[offset].sid_value;
164 ok = blob_string_sid_to_sid(v, &result->data.sid.sid);
165 if (! ok) {
166 DBG_WARNING("claim has invalid SID string of length %zu.\n",
167 v->length);
168 return false;
171 result->type = CONDITIONAL_ACE_TOKEN_SID;
172 return true;
176 static bool claim_v1_int_to_ace_int(
177 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
178 size_t offset,
179 struct ace_condition_token *result)
181 int64_t v = *claim->values[offset].int_value;
182 result->type = CONDITIONAL_ACE_TOKEN_INT64;
183 result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
184 result->data.int64.value = v;
187 * The sign flag (and the base flag above) determines how the
188 * ACE token will be displayed if converted to SDDL. These
189 * values are not likely to end up as SDDL, but we might as
190 * well get it right. A negative flag means it will be
191 * displayed with a minus sign, and a positive flag means a
192 * plus sign is shown. The none flag means no + or -.
194 if (v < 0) {
195 result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NEGATIVE;
196 } else {
197 result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
200 return true;
204 static bool claim_v1_unsigned_int_to_ace_int(
205 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
206 size_t offset,
207 struct ace_condition_token *result)
209 uint64_t v = *claim->values[offset].uint_value;
210 if (v > INT64_MAX) {
212 * The unsigned value can't be represented in a
213 * conditional ACE type.
215 * XXX or can it? does the positive flag make it
216 * unsigned?
218 return false;
220 result->type = CONDITIONAL_ACE_TOKEN_INT64;
221 result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
222 result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_POSITIVE;
223 result->data.int64.value = v;
224 return true;
228 static bool claim_v1_bool_to_ace_int(
229 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
230 size_t offset,
231 struct ace_condition_token *result)
233 uint64_t v = *claim->values[offset].uint_value;
234 result->type = CONDITIONAL_ACE_TOKEN_INT64;
235 result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
236 result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
237 result->data.int64.value = v ? 1 : 0;
238 return true;
242 static bool claim_v1_offset_to_ace_token(
243 TALLOC_CTX *mem_ctx,
244 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
245 size_t offset,
246 struct ace_condition_token *result)
249 * A claim structure has an array of claims of a certain type,
250 * and this converts a single one into a conditional ACE token.
252 * For example, if offset is 3, claim->values[3] will be
253 * turned into *result.
255 * conditional ace token will have flags to indicate that it
256 * comes from a claim attribute, and whether or not that
257 * attribute should be compared case-sensitively (only
258 * affecting unicode strings).
260 * The CLAIM_SECURITY_ATTRIBUTE_CASE_SENSITIVE (from the
261 * claim_flags enum in security.idl) is used for both.
263 uint8_t f = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
264 result->flags = f | CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR;
266 if (claim->values[offset].int_value == NULL) {
267 return false;
269 switch (claim->value_type) {
270 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
271 return claim_v1_int_to_ace_int(claim, offset, result);
272 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
273 return claim_v1_unsigned_int_to_ace_int(claim, offset, result);
274 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
275 return claim_v1_string_to_ace_string(mem_ctx, claim, offset,
276 result);
277 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
278 return claim_v1_sid_to_ace_sid(claim, offset, result);
279 case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
280 return claim_v1_bool_to_ace_int(claim, offset, result);
281 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
282 return claim_v1_octet_string_to_ace_octet_string(mem_ctx,
283 claim,
284 offset,
285 result);
286 default:
287 return false;
292 static bool claim_v1_copy(
293 TALLOC_CTX *mem_ctx,
294 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *dest,
295 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *src);
299 bool claim_v1_to_ace_composite_unchecked(
300 TALLOC_CTX *mem_ctx,
301 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
302 struct ace_condition_token *result)
305 * This converts a claim object into a conditional ACE
306 * composite without checking whether it is a valid and sorted
307 * claim. It is called in two places:
309 * 1. claim_v1_to_ace_token() below (which does do those
310 * checks, and is the function you want).
312 * 2. sddl_resource_attr_from_claim() in which a resource
313 * attribute claim needs to pass through a conditional ACE
314 * composite structure on its way to becoming SDDL. In that
315 * case we don't want to check validity.
317 size_t i;
318 struct ace_condition_token *tokens = NULL;
319 bool ok;
321 tokens = talloc_array(mem_ctx,
322 struct ace_condition_token,
323 claim->value_count);
324 if (tokens == NULL) {
325 return false;
328 for (i = 0; i < claim->value_count; i++) {
329 ok = claim_v1_offset_to_ace_token(tokens,
330 claim,
332 &tokens[i]);
333 if (! ok) {
334 TALLOC_FREE(tokens);
335 return false;
339 result->type = CONDITIONAL_ACE_TOKEN_COMPOSITE;
340 result->data.composite.tokens = tokens;
341 result->data.composite.n_members = claim->value_count;
342 result->flags = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
343 return true;
347 bool claim_v1_to_ace_token(TALLOC_CTX *mem_ctx,
348 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
349 struct ace_condition_token *result)
351 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim_copy = NULL;
352 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *sorted_claim = NULL;
353 NTSTATUS status;
354 bool ok;
355 bool case_sensitive = claim->flags & \
356 CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
358 if (claim->value_count < 1 ||
359 claim->value_count >= CONDITIONAL_ACE_MAX_TOKENS) {
360 DBG_WARNING("rejecting claim with %"PRIu32" tokens\n",
361 claim->value_count);
362 return false;
365 * if there is one, we return a single thing of that type; if
366 * there are many, we return a composite.
369 if (claim->value_count == 1) {
370 return claim_v1_offset_to_ace_token(mem_ctx,
371 claim,
373 result);
376 if (claim->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED) {
378 * We can avoid making a sorted copy.
380 * This is normal case for wire claims, where the
381 * sorting and duplicate checking happens earlier in
382 * token_claims_to_claims_v1().
384 sorted_claim = claim;
385 } else {
387 * This is presumably a resource attribute ACE, which
388 * is stored in the ACE as struct
389 * CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, and we don't
390 * really want to mutate that copy -- even if there
391 * aren't currently realistic pathways that read an
392 * ACE, trigger this, and write it back (outside of
393 * tests).
395 claim_copy = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
396 if (claim_copy == NULL) {
397 return false;
400 ok = claim_v1_copy(claim_copy, claim_copy, claim);
401 if (!ok) {
402 TALLOC_FREE(claim_copy);
403 return false;
406 status = claim_v1_check_and_sort(claim_copy, claim_copy,
407 case_sensitive);
408 if (!NT_STATUS_IS_OK(status)) {
409 DBG_WARNING("resource attribute claim sort failed with %s\n",
410 nt_errstr(status));
411 TALLOC_FREE(claim_copy);
412 return false;
414 sorted_claim = claim_copy;
416 ok = claim_v1_to_ace_composite_unchecked(mem_ctx, sorted_claim, result);
417 if (! ok) {
418 TALLOC_FREE(claim_copy);
419 return false;
423 * The multiple values will get turned into a composite
424 * literal in the conditional ACE. Each element of the
425 * composite will have flags set by
426 * claim_v1_offset_to_ace_token(), but they also need to be
427 * set here (at least the _FROM_ATTR flag) or the child values
428 * will not be reached.
430 result->flags |= (
431 CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR |
432 CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED);
434 return true;
439 static bool ace_int_to_claim_v1_int(TALLOC_CTX *mem_ctx,
440 const struct ace_condition_token *tok,
441 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
442 size_t offset)
444 int64_t *v = talloc(mem_ctx, int64_t);
445 if (v == NULL) {
446 return false;
448 *v = tok->data.int64.value;
449 claim->values[offset].int_value = v;
450 return true;
454 static bool ace_string_to_claim_v1_string(TALLOC_CTX *mem_ctx,
455 const struct ace_condition_token *tok,
456 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
457 size_t offset)
459 const char *s = talloc_strdup(mem_ctx,
460 tok->data.unicode.value);
461 if (s == NULL) {
462 return false;
464 claim->values[offset].string_value = s;
465 return true;
470 static bool ace_sid_to_claim_v1_sid(TALLOC_CTX *mem_ctx,
471 const struct ace_condition_token *tok,
472 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
473 size_t offset)
475 /* claim_v1 sid is an "S-1-*" string data blob, not struct dom_sid. */
476 char *s = NULL;
478 DATA_BLOB *blob = NULL;
479 blob = talloc(mem_ctx, DATA_BLOB);
480 if (blob == NULL) {
481 return false;
483 s = dom_sid_string(blob, &tok->data.sid.sid);
484 if (s == NULL) {
485 TALLOC_FREE(blob);
486 return false;
488 *blob = data_blob_string_const(s);
489 claim->values[offset].sid_value = blob;
490 return true;
493 static bool ace_octet_string_to_claim_v1_octet_string(
494 TALLOC_CTX *mem_ctx,
495 const struct ace_condition_token *tok,
496 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
497 size_t offset)
499 DATA_BLOB *v = talloc(mem_ctx, DATA_BLOB);
500 if (v == NULL) {
501 return false;
504 *v = data_blob_talloc(v,
505 tok->data.bytes.data,
506 tok->data.bytes.length);
507 if (v->data == NULL) {
508 return false;
511 claim->values[offset].octet_value = v;
512 return true;
517 static bool ace_token_to_claim_v1_offset(TALLOC_CTX *mem_ctx,
518 const struct ace_condition_token *tok,
519 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
520 size_t offset)
523 * A claim structure has an array of claims of a certain type,
524 * and this converts a single one into a conditional ACE token.
526 * For example, if offset is 3, claim->values[3] will be
527 * turned into *result.
529 if (offset >= claim->value_count) {
530 return false;
532 switch (claim->value_type) {
533 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
534 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
535 return ace_int_to_claim_v1_int(mem_ctx, tok, claim, offset);
536 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
537 return ace_string_to_claim_v1_string(mem_ctx, tok, claim, offset);
538 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
539 return ace_sid_to_claim_v1_sid(mem_ctx, tok, claim, offset);
540 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
541 return ace_octet_string_to_claim_v1_octet_string(mem_ctx,
542 tok,
543 claim,
544 offset);
545 default:
546 /*bool unimplemented, because unreachable */
547 return false;
552 bool ace_token_to_claim_v1(TALLOC_CTX *mem_ctx,
553 const char *name,
554 const struct ace_condition_token *tok,
555 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **claim,
556 uint32_t flags)
558 size_t i;
559 bool ok;
560 bool is_comp = false;
561 int claim_type = -1;
562 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *_claim = NULL;
563 uint32_t value_count;
565 if (name == NULL || claim == NULL || tok == NULL) {
566 return false;
568 *claim = NULL;
570 if (tok->type == CONDITIONAL_ACE_TOKEN_COMPOSITE) {
571 is_comp = true;
572 /* there must be values, all of the same type */
573 if (tok->data.composite.n_members == 0) {
574 DBG_WARNING("Empty ACE composite list\n");
575 return false;
577 if (tok->data.composite.n_members > 1) {
578 for (i = 1; i < tok->data.composite.n_members; i++) {
579 if (tok->data.composite.tokens[i].type !=
580 tok->data.composite.tokens[0].type) {
581 DBG_WARNING(
582 "ACE composite list has varying "
583 "types (at least %u and %u)\n",
584 tok->data.composite.tokens[i].type,
585 tok->data.composite.tokens[0].type);
586 return false;
590 value_count = tok->data.composite.n_members;
592 switch (tok->data.composite.tokens[0].type) {
593 case CONDITIONAL_ACE_TOKEN_INT8:
594 case CONDITIONAL_ACE_TOKEN_INT16:
595 case CONDITIONAL_ACE_TOKEN_INT32:
596 case CONDITIONAL_ACE_TOKEN_INT64:
597 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
598 break;
599 case CONDITIONAL_ACE_TOKEN_UNICODE:
600 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
601 break;
602 case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
603 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING;
604 break;
605 case CONDITIONAL_ACE_TOKEN_SID:
606 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID;
607 break;
608 default:
609 /* reject nested composites, no uint or bool. */
610 DBG_WARNING("ACE composite list has invalid type %u\n",
611 tok->data.composite.tokens[0].type);
612 return false;
614 } else {
615 value_count = 1;
616 switch(tok->type) {
617 case CONDITIONAL_ACE_TOKEN_INT8:
618 case CONDITIONAL_ACE_TOKEN_INT16:
619 case CONDITIONAL_ACE_TOKEN_INT32:
620 case CONDITIONAL_ACE_TOKEN_INT64:
621 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
622 break;
623 case CONDITIONAL_ACE_TOKEN_UNICODE:
624 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
625 break;
626 case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
627 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING;
628 break;
629 case CONDITIONAL_ACE_TOKEN_SID:
630 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID;
631 break;
632 default:
634 * no way of creating bool or uint values,
635 * composite is handled above.
637 DBG_WARNING("ACE token has invalid type %u\n",
638 tok->data.composite.tokens[0].type);
639 return false;
643 _claim = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
644 if (_claim == NULL) {
645 return false;
648 _claim->value_count = value_count;
649 _claim->value_type = claim_type;
650 _claim->flags = flags;
651 _claim->name = talloc_strdup(mem_ctx, name);
652 if (_claim->name == NULL) {
653 TALLOC_FREE(_claim);
654 return false;
657 * The values array is actually an array of pointers to
658 * values, even when the values are ints or bools.
660 _claim->values = talloc_array(_claim, union claim_values, value_count);
661 if (_claim->values == NULL) {
662 TALLOC_FREE(_claim);
663 return false;
665 if (! is_comp) {
666 /* there is one value, not a list */
667 ok = ace_token_to_claim_v1_offset(_claim,
668 tok,
669 _claim,
671 if (! ok) {
672 TALLOC_FREE(_claim);
673 return false;
675 } else {
676 /* a composite list of values */
677 for (i = 0; i < value_count; i++) {
678 struct ace_condition_token *t = &tok->data.composite.tokens[i];
679 ok = ace_token_to_claim_v1_offset(mem_ctx,
681 _claim,
683 if (! ok) {
684 TALLOC_FREE(_claim);
685 return false;
691 if (_claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) {
693 * Conditional ACE tokens don't have a UINT type but
694 * claims do. Windows tends to use UINT types in
695 * claims when it can, so so do we.
697 bool could_be_uint = true;
698 for (i = 0; i < value_count; i++) {
699 if (*_claim->values[i].int_value < 0) {
700 could_be_uint = false;
701 break;
704 if (could_be_uint) {
705 _claim->value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64;
709 *claim = _claim;
710 return true;
715 static bool claim_v1_copy(
716 TALLOC_CTX *mem_ctx,
717 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *dest,
718 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *src)
720 DATA_BLOB blob = {0};
721 enum ndr_err_code ndr_err;
724 * FIXME, could be more efficient! but copying these
725 * structures is fiddly, and it might be worth coming up
726 * with a better API for adding claims.
729 ndr_err = ndr_push_struct_blob(
730 &blob, mem_ctx, src,
731 (ndr_push_flags_fn_t)ndr_push_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
733 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
734 return false;
737 ndr_err = ndr_pull_struct_blob(
738 &blob, mem_ctx, dest,
739 (ndr_pull_flags_fn_t)ndr_pull_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
741 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
742 TALLOC_FREE(blob.data);
743 return false;
745 TALLOC_FREE(blob.data);
746 return true;
751 bool add_claim_to_token(TALLOC_CTX *mem_ctx,
752 struct security_token *token,
753 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
754 const char *claim_type)
756 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *tmp = NULL;
757 NTSTATUS status;
758 uint32_t *n = NULL;
759 bool ok;
760 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **list = NULL;
761 if (strcmp(claim_type, "device") == 0) {
762 n = &token->num_device_claims;
763 list = &token->device_claims;
764 } else if (strcmp(claim_type, "local") == 0) {
765 n = &token->num_local_claims;
766 list = &token->local_claims;
767 } else if (strcmp(claim_type, "user") == 0) {
768 n = &token->num_user_claims;
769 list = &token->user_claims;
770 } else {
771 return false;
773 if ((*n) == UINT32_MAX) {
774 return false;
777 tmp = talloc_realloc(mem_ctx,
778 *list,
779 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1,
780 (*n) + 1);
781 if (tmp == NULL) {
782 return false;
785 ok = claim_v1_copy(mem_ctx, &tmp[*n], claim);
786 if (! ok ) {
787 TALLOC_FREE(tmp);
788 return false;
791 status = claim_v1_check_and_sort(tmp, &tmp[*n],
792 claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE);
793 if (!NT_STATUS_IS_OK(status)) {
794 DBG_WARNING("resource attribute claim sort failed with %s\n",
795 nt_errstr(status));
796 TALLOC_FREE(tmp);
797 return false;
800 (*n)++;
801 *list = tmp;
802 return true;
806 static NTSTATUS claim_v1_check_and_sort_boolean(
807 TALLOC_CTX *mem_ctx,
808 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim)
811 * There are so few valid orders in a boolean claim that we can
812 * enumerate them all.
814 switch (claim->value_count) {
815 case 0:
816 return NT_STATUS_OK;
817 case 1:
818 if (*claim->values[0].uint_value == 0 ||
819 *claim->values[0].uint_value == 1) {
820 return NT_STATUS_OK;
822 break;
823 case 2:
824 if (*claim->values[0].uint_value == 1) {
825 /* switch the order. */
826 *claim->values[0].uint_value = *claim->values[1].uint_value;
827 *claim->values[1].uint_value = 1;
829 if (*claim->values[0].uint_value == 0 &&
830 *claim->values[1].uint_value == 1) {
831 return NT_STATUS_OK;
833 break;
834 default:
835 /* 3 or more must have duplicates. */
836 break;
838 return NT_STATUS_INVALID_PARAMETER;
842 struct claim_sort_context {
843 uint16_t value_type;
844 bool failed;
845 bool case_sensitive;
848 static int claim_sort_cmp(const union claim_values *lhs,
849 const union claim_values *rhs,
850 struct claim_sort_context *ctx)
853 * These comparisons have to match those used in
854 * conditional_ace.c.
856 int cmp;
858 switch (ctx->value_type) {
859 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
860 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
863 * We sort as signed integers, even for uint64,
864 * because a) we don't actually care about the true
865 * order, just uniqueness, and b) the conditional ACEs
866 * only know of signed values.
868 int64_t a, b;
869 if (ctx->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) {
870 a = *lhs->int_value;
871 b = *rhs->int_value;
872 } else {
873 a = (int64_t)*lhs->uint_value;
874 b = (int64_t)*rhs->uint_value;
876 if (a < b) {
877 return -1;
879 if (a == b) {
880 return 0;
882 return 1;
884 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
886 const char *a = lhs->string_value;
887 const char *b = rhs->string_value;
888 if (ctx->case_sensitive) {
889 return strcmp(a, b);
891 return strcasecmp_m(a, b);
894 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
897 * The blobs in a claim are "S-1-.." strings, not struct
898 * dom_sid as used in conditional ACEs, and to sort them the
899 * same as ACEs we need to make temporary structs.
901 * We don't accept SID claims over the wire -- these
902 * are resource attribute ACEs only.
904 struct dom_sid a, b;
905 bool lhs_ok, rhs_ok;
907 lhs_ok = blob_string_sid_to_sid(lhs->sid_value, &a);
908 rhs_ok = blob_string_sid_to_sid(rhs->sid_value, &b);
909 if (!(lhs_ok && rhs_ok)) {
910 ctx->failed = true;
911 return -1;
913 cmp = dom_sid_compare(&a, &b);
914 return cmp;
916 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
918 const DATA_BLOB *a = lhs->octet_value;
919 const DATA_BLOB *b = rhs->octet_value;
920 return data_blob_cmp(a, b);
922 default:
923 ctx->failed = true;
924 break;
926 return -1;
930 NTSTATUS claim_v1_check_and_sort(TALLOC_CTX *mem_ctx,
931 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
932 bool case_sensitive)
934 bool ok;
935 uint32_t i;
936 struct claim_sort_context sort_ctx = {
937 .failed = false,
938 .value_type = claim->value_type,
939 .case_sensitive = case_sensitive
943 * It could be that the values array contains a NULL pointer, in which
944 * case we don't need to worry about what type it is.
946 for (i = 0; i < claim->value_count; i++) {
947 if (claim->values[i].int_value == NULL) {
948 return NT_STATUS_INVALID_PARAMETER;
952 if (claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN) {
953 NTSTATUS status = claim_v1_check_and_sort_boolean(mem_ctx, claim);
954 if (NT_STATUS_IS_OK(status)) {
955 claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
957 return status;
960 ok = stable_sort_talloc_r(mem_ctx,
961 claim->values,
962 claim->value_count,
963 sizeof(union claim_values),
964 (samba_compare_with_context_fn_t)claim_sort_cmp,
965 &sort_ctx);
966 if (!ok) {
967 return NT_STATUS_NO_MEMORY;
970 if (sort_ctx.failed) {
971 /* this failure probably means a bad SID string */
972 DBG_WARNING("claim sort of %"PRIu32" members, type %"PRIu16" failed\n",
973 claim->value_count,
974 claim->value_type);
975 return NT_STATUS_INVALID_PARAMETER;
978 for (i = 1; i < claim->value_count; i++) {
979 int cmp = claim_sort_cmp(&claim->values[i - 1],
980 &claim->values[i],
981 &sort_ctx);
982 if (cmp == 0) {
983 DBG_WARNING("duplicate values in claim\n");
984 return NT_STATUS_INVALID_PARAMETER;
986 if (cmp > 0) {
987 DBG_ERR("claim sort failed!\n");
988 return NT_STATUS_INVALID_PARAMETER;
991 if (case_sensitive) {
992 claim->flags |= CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
994 claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
995 return NT_STATUS_OK;
999 NTSTATUS token_claims_to_claims_v1(TALLOC_CTX *mem_ctx,
1000 const struct CLAIMS_SET *claims_set,
1001 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **out_claims,
1002 uint32_t *out_n_claims)
1004 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claims = NULL;
1005 uint32_t n_claims = 0;
1006 uint32_t expected_n_claims = 0;
1007 uint32_t i;
1008 NTSTATUS status;
1010 if (out_claims == NULL) {
1011 return NT_STATUS_INVALID_PARAMETER;
1013 if (out_n_claims == NULL) {
1014 return NT_STATUS_INVALID_PARAMETER;
1017 *out_claims = NULL;
1018 *out_n_claims = 0;
1020 if (claims_set == NULL) {
1021 return NT_STATUS_OK;
1025 * The outgoing number of claims is (at most) the sum of the
1026 * claims_counts of each claims_array.
1028 for (i = 0; i < claims_set->claims_array_count; ++i) {
1029 uint32_t count = claims_set->claims_arrays[i].claims_count;
1030 expected_n_claims += count;
1031 if (expected_n_claims < count) {
1032 return NT_STATUS_INVALID_PARAMETER;
1036 claims = talloc_array(mem_ctx,
1037 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1,
1038 expected_n_claims);
1039 if (claims == NULL) {
1040 return NT_STATUS_NO_MEMORY;
1043 for (i = 0; i < claims_set->claims_array_count; ++i) {
1044 const struct CLAIMS_ARRAY *claims_array = &claims_set->claims_arrays[i];
1045 uint32_t j;
1047 switch (claims_array->claims_source_type) {
1048 case CLAIMS_SOURCE_TYPE_AD:
1049 case CLAIMS_SOURCE_TYPE_CERTIFICATE:
1050 break;
1051 default:
1052 /* Ignore any claims of a type we don’t recognize. */
1053 continue;
1056 for (j = 0; j < claims_array->claims_count; ++j) {
1057 const struct CLAIM_ENTRY *claim_entry = &claims_array->claim_entries[j];
1058 const char *name = NULL;
1059 union claim_values *claim_values = NULL;
1060 uint32_t n_values;
1061 enum security_claim_value_type value_type;
1063 switch (claim_entry->type) {
1064 case CLAIM_TYPE_INT64:
1066 const struct CLAIM_INT64 *values = &claim_entry->values.claim_int64;
1067 uint32_t k;
1068 int64_t *claim_values_int64 = NULL;
1070 n_values = values->value_count;
1071 value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
1073 claim_values = talloc_array(claims,
1074 union claim_values,
1075 n_values);
1076 if (claim_values == NULL) {
1077 talloc_free(claims);
1078 return NT_STATUS_NO_MEMORY;
1080 claim_values_int64 = talloc_array(claims,
1081 int64_t,
1082 n_values);
1083 if (claim_values_int64 == NULL) {
1084 talloc_free(claims);
1085 return NT_STATUS_NO_MEMORY;
1088 for (k = 0; k < n_values; ++k) {
1089 claim_values_int64[k] = values->values[k];
1090 claim_values[k].int_value = &claim_values_int64[k];
1093 break;
1095 case CLAIM_TYPE_UINT64:
1096 case CLAIM_TYPE_BOOLEAN:
1098 const struct CLAIM_UINT64 *values = &claim_entry->values.claim_uint64;
1099 uint32_t k;
1100 uint64_t *claim_values_uint64 = NULL;
1102 n_values = values->value_count;
1103 value_type = (claim_entry->type == CLAIM_TYPE_UINT64)
1104 ? CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64
1105 : CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN;
1107 claim_values = talloc_array(claims,
1108 union claim_values,
1109 n_values);
1110 if (claim_values == NULL) {
1111 talloc_free(claims);
1112 return NT_STATUS_NO_MEMORY;
1115 claim_values_uint64 = talloc_array(claims,
1116 uint64_t,
1117 n_values);
1118 if (claim_values_uint64 == NULL) {
1119 talloc_free(claims);
1120 return NT_STATUS_NO_MEMORY;
1123 for (k = 0; k < n_values; ++k) {
1124 claim_values_uint64[k] = values->values[k];
1125 claim_values[k].uint_value = &claim_values_uint64[k];
1128 break;
1130 case CLAIM_TYPE_STRING:
1132 const struct CLAIM_STRING *values = &claim_entry->values.claim_string;
1133 uint32_t k, m;
1134 bool seen_empty = false;
1135 n_values = values->value_count;
1136 value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
1138 claim_values = talloc_array(claims,
1139 union claim_values,
1140 n_values);
1141 if (claim_values == NULL) {
1142 talloc_free(claims);
1143 return NT_STATUS_NO_MEMORY;
1146 m = 0;
1147 for (k = 0; k < n_values; ++k) {
1148 const char *string_value = NULL;
1150 if (values->values[k] != NULL) {
1151 string_value = talloc_strdup(claim_values, values->values[k]);
1152 if (string_value == NULL) {
1153 talloc_free(claims);
1154 return NT_STATUS_NO_MEMORY;
1156 claim_values[m].string_value = string_value;
1157 m++;
1158 } else {
1160 * We allow one NULL string
1161 * per claim, but not two,
1162 * because two would be a
1163 * duplicate, and we don't
1164 * want those (duplicates in
1165 * actual values are checked
1166 * later).
1168 if (seen_empty) {
1169 talloc_free(claims);
1170 return NT_STATUS_INVALID_PARAMETER;
1172 seen_empty = true;
1175 n_values = m;
1176 break;
1178 default:
1180 * Other claim types are unsupported — just skip
1181 * them.
1183 continue;
1186 if (claim_entry->id != NULL) {
1187 name = talloc_strdup(claims, claim_entry->id);
1188 if (name == NULL) {
1189 talloc_free(claims);
1190 return NT_STATUS_NO_MEMORY;
1194 claims[n_claims] = (struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1) {
1195 .name = name,
1196 .value_type = value_type,
1197 .flags = 0,
1198 .value_count = n_values,
1199 .values = claim_values,
1202 status = claim_v1_check_and_sort(claims, &claims[n_claims],
1203 false);
1204 if (!NT_STATUS_IS_OK(status)) {
1205 talloc_free(claims);
1206 DBG_WARNING("claim sort and uniqueness test failed with %s\n",
1207 nt_errstr(status));
1208 return status;
1210 n_claims++;
1213 *out_claims = claims;
1214 *out_n_claims = n_claims;
1216 return NT_STATUS_OK;