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/>.
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"
26 #include "librpc/gen_ndr/conditional_ace.h"
27 #include "librpc/gen_ndr/claims.h"
30 * We support three formats for claims, all slightly different.
32 * 1. MS-ADTS 2.2.18.* claims sets, blobs, arrays, or whatever, which
33 * are used in the PAC.
35 * 2. MS-DTYP 2.4.10.1 CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
36 * structures, used in security tokens and resource SACL ACEs.
38 * 3. MS-DTYP 2.4.4.17 Conditional ACE tokens.
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.
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
51 static bool claim_v1_string_to_ace_string(
53 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
55 struct ace_condition_token *result)
57 char *s = talloc_strdup(mem_ctx,
58 claim->values[offset].string_value);
63 result->type = CONDITIONAL_ACE_TOKEN_UNICODE;
64 result->data.unicode.value = s;
69 static bool claim_v1_octet_string_to_ace_octet_string(
71 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
73 struct ace_condition_token *result)
76 DATA_BLOB w = data_blob_null;
78 v = claim->values[offset].octet_value;
80 if (v->length > CONDITIONAL_ACE_MAX_LENGTH) {
81 DBG_WARNING("claim has octet string of unexpected length %zu "
82 "(expected range 1 - %u)\n",
83 v->length, CONDITIONAL_ACE_MAX_LENGTH);
87 w = data_blob_talloc(mem_ctx, v->data, v->length);
93 result->type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
94 result->data.bytes = w;
99 static bool blob_string_sid_to_sid(DATA_BLOB *blob,
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.
111 * It doesn't seem like SDDL short SIDs ("WD") are accepted here. This
115 size_t len = blob->length;
116 char buf[DOM_SID_STR_BUFLEN + 1]; /* 191 + 1 */
117 const char *end = NULL;
120 if (len < 5 || len >= DOM_SID_STR_BUFLEN) {
123 if (blob->data[len - 1] == '\0') {
124 str = (char *)blob->data;
127 memcpy(buf, blob->data, len);
132 ok = dom_sid_parse_endp(str, sid, &end);
137 if (end - str != len) {
144 static bool claim_v1_sid_to_ace_sid(
145 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
147 struct ace_condition_token *result)
150 * In the _V1 struct, SIDs are stored as octet string blobs,
153 * In the conditional ACE they are stored as struct dom_sid.
155 * There are no SIDs in ADTS claims, but there can be in
161 v = claim->values[offset].sid_value;
163 ok = blob_string_sid_to_sid(v, &result->data.sid.sid);
165 DBG_WARNING("claim has invalid SID string of length %zu.\n",
170 result->type = CONDITIONAL_ACE_TOKEN_SID;
175 static bool claim_v1_int_to_ace_int(
176 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
178 struct ace_condition_token *result)
180 int64_t v = *claim->values[offset].int_value;
181 result->type = CONDITIONAL_ACE_TOKEN_INT64;
182 result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
183 result->data.int64.value = v;
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 -.
194 result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NEGATIVE;
196 result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
203 static bool claim_v1_unsigned_int_to_ace_int(
204 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
206 struct ace_condition_token *result)
208 uint64_t v = *claim->values[offset].uint_value;
211 * The unsigned value can't be represented in a
212 * conditional ACE type.
214 * XXX or can it? does the positive flag make it
219 result->type = CONDITIONAL_ACE_TOKEN_INT64;
220 result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
221 result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_POSITIVE;
222 result->data.int64.value = v;
227 static bool claim_v1_bool_to_ace_int(
228 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
230 struct ace_condition_token *result)
232 uint64_t v = *claim->values[offset].uint_value;
233 result->type = CONDITIONAL_ACE_TOKEN_INT64;
234 result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10;
235 result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
236 result->data.int64.value = v ? 1 : 0;
241 static bool claim_v1_offset_to_ace_token(
243 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
245 struct ace_condition_token *result)
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.
251 * For example, if offset is 3, claim->values[3] will be
252 * turned into *result.
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).
259 * The CLAIM_SECURITY_ATTRIBUTE_CASE_SENSITIVE (from the
260 * claim_flags enum in security.idl) is used for both.
262 uint8_t f = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
263 result->flags = f | CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR;
265 switch (claim->value_type) {
266 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
267 return claim_v1_int_to_ace_int(claim, offset, result);
268 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
269 return claim_v1_unsigned_int_to_ace_int(claim, offset, result);
270 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
271 return claim_v1_string_to_ace_string(mem_ctx, claim, offset,
273 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
274 return claim_v1_sid_to_ace_sid(claim, offset, result);
275 case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
276 return claim_v1_bool_to_ace_int(claim, offset, result);
277 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
278 return claim_v1_octet_string_to_ace_octet_string(mem_ctx,
288 static bool claim_v1_copy(
290 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *dest,
291 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *src);
295 bool claim_v1_to_ace_composite_unchecked(
297 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
298 struct ace_condition_token *result)
301 * This converts a claim object into a conditional ACE
302 * composite without checking whether it is a valid and sorted
303 * claim. It is called in two places:
305 * 1. claim_v1_to_ace_token() below (which does do those
306 * checks, and is the function you want).
308 * 2. sddl_resource_attr_from_claim() in which a resource
309 * attribute claim needs to pass through a conditional ACE
310 * composite structure on its way to becoming SDDL. In that
311 * case we don't want to check validity.
314 struct ace_condition_token *tokens = NULL;
317 tokens = talloc_array(mem_ctx,
318 struct ace_condition_token,
320 if (tokens == NULL) {
324 for (i = 0; i < claim->value_count; i++) {
325 ok = claim_v1_offset_to_ace_token(tokens,
335 result->type = CONDITIONAL_ACE_TOKEN_COMPOSITE;
336 result->data.composite.tokens = tokens;
337 result->data.composite.n_members = claim->value_count;
338 result->flags = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
343 bool claim_v1_to_ace_token(TALLOC_CTX *mem_ctx,
344 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
345 struct ace_condition_token *result)
347 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim_copy = NULL;
348 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *sorted_claim = NULL;
351 bool case_sensitive = claim->flags & \
352 CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
354 if (claim->value_count < 1 ||
355 claim->value_count >= CONDITIONAL_ACE_MAX_TOKENS) {
356 DBG_WARNING("rejecting claim with %"PRIu32" tokens\n",
361 * if there is one, we return a single thing of that type; if
362 * there are many, we return a composite.
365 if (claim->value_count == 1) {
366 return claim_v1_offset_to_ace_token(mem_ctx,
372 if (claim->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED) {
374 * We can avoid making a sorted copy.
376 * This is normal case for wire claims, where the
377 * sorting and duplicate checking happens earlier in
378 * token_claims_to_claims_v1().
380 sorted_claim = claim;
383 * This is presumably a resource attribute ACE, which
384 * is stored in the ACE as struct
385 * CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, and we don't
386 * really want to mutate that copy -- even if there
387 * aren't currently realistic pathways that read an
388 * ACE, trigger this, and write it back (outside of
391 claim_copy = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
392 if (claim_copy == NULL) {
396 ok = claim_v1_copy(claim_copy, claim_copy, claim);
398 TALLOC_FREE(claim_copy);
402 status = claim_v1_check_and_sort(claim_copy, claim_copy,
404 if (!NT_STATUS_IS_OK(status)) {
405 DBG_WARNING("resource attribute claim sort failed with %s\n",
407 TALLOC_FREE(claim_copy);
410 sorted_claim = claim_copy;
412 ok = claim_v1_to_ace_composite_unchecked(mem_ctx, sorted_claim, result);
414 TALLOC_FREE(claim_copy);
419 * The multiple values will get turned into a composite
420 * literal in the conditional ACE. Each element of the
421 * composite will have flags set by
422 * claim_v1_offset_to_ace_token(), but they also need to be
423 * set here (at least the _FROM_ATTR flag) or the child values
424 * will not be reached.
427 CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR |
428 CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED);
435 static bool ace_int_to_claim_v1_int(TALLOC_CTX *mem_ctx,
436 const struct ace_condition_token *tok,
437 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
440 int64_t *v = talloc(mem_ctx, int64_t);
444 *v = tok->data.int64.value;
445 claim->values[offset].int_value = v;
450 static bool ace_string_to_claim_v1_string(TALLOC_CTX *mem_ctx,
451 const struct ace_condition_token *tok,
452 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
455 const char *s = talloc_strdup(mem_ctx,
456 tok->data.unicode.value);
460 claim->values[offset].string_value = s;
466 static bool ace_sid_to_claim_v1_sid(TALLOC_CTX *mem_ctx,
467 const struct ace_condition_token *tok,
468 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
471 /* claim_v1 sid is an "S-1-*" string data blob, not struct dom_sid. */
474 DATA_BLOB *blob = NULL;
475 blob = talloc(mem_ctx, DATA_BLOB);
479 s = dom_sid_string(blob, &tok->data.sid.sid);
484 *blob = data_blob_string_const(s);
485 claim->values[offset].sid_value = blob;
489 static bool ace_octet_string_to_claim_v1_octet_string(
491 const struct ace_condition_token *tok,
492 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
495 DATA_BLOB *v = talloc(mem_ctx, DATA_BLOB);
500 *v = data_blob_talloc(v,
501 tok->data.bytes.data,
502 tok->data.bytes.length);
503 if (v->data == NULL) {
507 claim->values[offset].octet_value = v;
513 static bool ace_token_to_claim_v1_offset(TALLOC_CTX *mem_ctx,
514 const struct ace_condition_token *tok,
515 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
519 * A claim structure has an array of claims of a certain type,
520 * and this converts a single one into a conditional ACE token.
522 * For example, if offset is 3, claim->values[3] will be
523 * turned into *result.
525 if (offset >= claim->value_count) {
528 switch (claim->value_type) {
529 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
530 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
531 return ace_int_to_claim_v1_int(mem_ctx, tok, claim, offset);
532 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
533 return ace_string_to_claim_v1_string(mem_ctx, tok, claim, offset);
534 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
535 return ace_sid_to_claim_v1_sid(mem_ctx, tok, claim, offset);
536 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
537 return ace_octet_string_to_claim_v1_octet_string(mem_ctx,
542 /*bool unimplemented, because unreachable */
548 bool ace_token_to_claim_v1(TALLOC_CTX *mem_ctx,
550 const struct ace_condition_token *tok,
551 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **claim,
556 bool is_comp = false;
558 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *_claim = NULL;
559 uint32_t value_count;
561 if (name == NULL || claim == NULL || tok == NULL) {
566 if (tok->type == CONDITIONAL_ACE_TOKEN_COMPOSITE) {
568 /* there must be values, all of the same type */
569 if (tok->data.composite.n_members == 0) {
570 DBG_WARNING("Empty ACE composite list\n");
573 if (tok->data.composite.n_members > 1) {
574 for (i = 1; i < tok->data.composite.n_members; i++) {
575 if (tok->data.composite.tokens[i].type !=
576 tok->data.composite.tokens[0].type) {
578 "ACE composite list has varying "
579 "types (at least %u and %u)\n",
580 tok->data.composite.tokens[i].type,
581 tok->data.composite.tokens[0].type);
586 value_count = tok->data.composite.n_members;
588 switch (tok->data.composite.tokens[0].type) {
589 case CONDITIONAL_ACE_TOKEN_INT8:
590 case CONDITIONAL_ACE_TOKEN_INT16:
591 case CONDITIONAL_ACE_TOKEN_INT32:
592 case CONDITIONAL_ACE_TOKEN_INT64:
593 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
595 case CONDITIONAL_ACE_TOKEN_UNICODE:
596 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
598 case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
599 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING;
601 case CONDITIONAL_ACE_TOKEN_SID:
602 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID;
605 /* reject nested composites, no uint or bool. */
606 DBG_WARNING("ACE composite list has invalid type %u\n",
607 tok->data.composite.tokens[0].type);
613 case CONDITIONAL_ACE_TOKEN_INT8:
614 case CONDITIONAL_ACE_TOKEN_INT16:
615 case CONDITIONAL_ACE_TOKEN_INT32:
616 case CONDITIONAL_ACE_TOKEN_INT64:
617 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
619 case CONDITIONAL_ACE_TOKEN_UNICODE:
620 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
622 case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
623 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING;
625 case CONDITIONAL_ACE_TOKEN_SID:
626 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID;
630 * no way of creating bool or uint values,
631 * composite is handled above.
633 DBG_WARNING("ACE token has invalid type %u\n",
634 tok->data.composite.tokens[0].type);
639 _claim = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
640 if (_claim == NULL) {
644 _claim->value_count = value_count;
645 _claim->value_type = claim_type;
646 _claim->flags = flags;
647 _claim->name = talloc_strdup(mem_ctx, name);
648 if (_claim->name == NULL) {
653 * The values array is actually an array of pointers to
654 * values, even when the values are ints or bools.
656 _claim->values = talloc_array(_claim, union claim_values, value_count);
657 if (_claim->values == NULL) {
662 /* there is one value, not a list */
663 ok = ace_token_to_claim_v1_offset(_claim,
672 /* a composite list of values */
673 for (i = 0; i < value_count; i++) {
674 struct ace_condition_token *t = &tok->data.composite.tokens[i];
675 ok = ace_token_to_claim_v1_offset(mem_ctx,
687 if (_claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) {
689 * Conditional ACE tokens don't have a UINT type but
690 * claims do. Windows tends to use UINT types in
691 * claims when it can, so so do we.
693 bool could_be_uint = true;
694 for (i = 0; i < value_count; i++) {
695 if (*_claim->values[i].int_value < 0) {
696 could_be_uint = false;
701 _claim->value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64;
711 static bool claim_v1_copy(
713 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *dest,
714 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *src)
716 DATA_BLOB blob = {0};
717 enum ndr_err_code ndr_err;
720 * FIXME, could be more efficient! but copying these
721 * structures is fiddly, and it might be worth coming up
722 * with a better API for adding claims.
725 ndr_err = ndr_push_struct_blob(
727 (ndr_push_flags_fn_t)ndr_push_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
729 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
733 ndr_err = ndr_pull_struct_blob(
734 &blob, mem_ctx, dest,
735 (ndr_pull_flags_fn_t)ndr_pull_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
737 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
738 TALLOC_FREE(blob.data);
741 TALLOC_FREE(blob.data);
747 bool add_claim_to_token(TALLOC_CTX *mem_ctx,
748 struct security_token *token,
749 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
750 const char *claim_type)
752 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *tmp = NULL;
756 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **list = NULL;
757 if (strcmp(claim_type, "device") == 0) {
758 n = &token->num_device_claims;
759 list = &token->device_claims;
760 } else if (strcmp(claim_type, "local") == 0) {
761 n = &token->num_local_claims;
762 list = &token->local_claims;
763 } else if (strcmp(claim_type, "user") == 0) {
764 n = &token->num_user_claims;
765 list = &token->user_claims;
769 if ((*n) == UINT32_MAX) {
773 tmp = talloc_realloc(mem_ctx,
775 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1,
781 ok = claim_v1_copy(mem_ctx, &tmp[*n], claim);
787 status = claim_v1_check_and_sort(tmp, &tmp[*n],
788 claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE);
789 if (!NT_STATUS_IS_OK(status)) {
790 DBG_WARNING("resource attribute claim sort failed with %s\n",
802 static NTSTATUS claim_v1_check_and_sort_boolean(
804 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim)
807 * There are so few valid orders in a boolean claim that we can
808 * enumerate them all.
810 switch (claim->value_count) {
814 if (*claim->values[0].uint_value == 0 ||
815 *claim->values[0].uint_value == 1) {
820 if (*claim->values[0].uint_value == 1) {
821 /* switch the order. */
822 *claim->values[0].uint_value = *claim->values[1].uint_value;
823 *claim->values[1].uint_value = 1;
825 if (*claim->values[0].uint_value == 0 &&
826 *claim->values[1].uint_value == 1) {
831 /* 3 or more must have duplicates. */
834 return NT_STATUS_INVALID_PARAMETER;
838 struct claim_sort_context {
844 static int claim_sort_cmp(const union claim_values *lhs,
845 const union claim_values *rhs,
846 struct claim_sort_context *ctx)
849 * These comparisons have to match those used in
854 switch (ctx->value_type) {
855 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
856 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
859 * We sort as signed integers, even for uint64,
860 * because a) we don't actually care about the true
861 * order, just uniqueness, and b) the conditional ACEs
862 * only know of signed values.
865 if (ctx->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) {
869 a = (int64_t)*lhs->uint_value;
870 b = (int64_t)*rhs->uint_value;
880 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
882 const char *a = lhs->string_value;
883 const char *b = rhs->string_value;
884 if (ctx->case_sensitive) {
887 return strcasecmp_m(a, b);
890 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
893 * The blobs in a claim are "S-1-.." strings, not struct
894 * dom_sid as used in conditional ACEs, and to sort them the
895 * same as ACEs we need to make temporary structs.
897 * We don't accept SID claims over the wire -- these
898 * are resource attribute ACEs only.
903 lhs_ok = blob_string_sid_to_sid(lhs->sid_value, &a);
904 rhs_ok = blob_string_sid_to_sid(rhs->sid_value, &b);
905 if (!(lhs_ok && rhs_ok)) {
909 cmp = dom_sid_compare(&a, &b);
912 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
914 const DATA_BLOB *a = lhs->octet_value;
915 const DATA_BLOB *b = rhs->octet_value;
916 return data_blob_cmp(a, b);
926 NTSTATUS claim_v1_check_and_sort(TALLOC_CTX *mem_ctx,
927 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
932 struct claim_sort_context sort_ctx = {
934 .value_type = claim->value_type,
935 .case_sensitive = case_sensitive
938 if (claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN) {
939 NTSTATUS status = claim_v1_check_and_sort_boolean(mem_ctx, claim);
940 if (NT_STATUS_IS_OK(status)) {
941 claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
946 ok = stable_sort_talloc_r(mem_ctx,
949 sizeof(union claim_values),
950 (samba_compare_with_context_fn_t)claim_sort_cmp,
953 return NT_STATUS_NO_MEMORY;
956 if (sort_ctx.failed) {
957 /* this failure probably means a bad SID string */
958 DBG_WARNING("claim sort of %"PRIu32" members, type %"PRIu16" failed\n",
961 return NT_STATUS_INVALID_PARAMETER;
964 for (i = 1; i < claim->value_count; i++) {
965 int cmp = claim_sort_cmp(&claim->values[i - 1],
969 DBG_WARNING("duplicate values in claim\n");
970 return NT_STATUS_INVALID_PARAMETER;
973 DBG_ERR("claim sort failed!\n");
974 return NT_STATUS_INVALID_PARAMETER;
977 if (case_sensitive) {
978 claim->flags |= CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
980 claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
985 NTSTATUS token_claims_to_claims_v1(TALLOC_CTX *mem_ctx,
986 const struct CLAIMS_SET *claims_set,
987 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **out_claims,
988 uint32_t *out_n_claims)
990 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claims = NULL;
991 uint32_t n_claims = 0;
992 uint32_t expected_n_claims = 0;
996 if (out_claims == NULL) {
997 return NT_STATUS_INVALID_PARAMETER;
999 if (out_n_claims == NULL) {
1000 return NT_STATUS_INVALID_PARAMETER;
1006 if (claims_set == NULL) {
1007 return NT_STATUS_OK;
1011 * The outgoing number of claims is (at most) the sum of the
1012 * claims_counts of each claims_array.
1014 for (i = 0; i < claims_set->claims_array_count; ++i) {
1015 uint32_t count = claims_set->claims_arrays[i].claims_count;
1016 expected_n_claims += count;
1017 if (expected_n_claims < count) {
1018 return NT_STATUS_INVALID_PARAMETER;
1022 claims = talloc_array(mem_ctx,
1023 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1,
1025 if (claims == NULL) {
1026 return NT_STATUS_NO_MEMORY;
1029 for (i = 0; i < claims_set->claims_array_count; ++i) {
1030 const struct CLAIMS_ARRAY *claims_array = &claims_set->claims_arrays[i];
1033 switch (claims_array->claims_source_type) {
1034 case CLAIMS_SOURCE_TYPE_AD:
1035 case CLAIMS_SOURCE_TYPE_CERTIFICATE:
1038 /* Ignore any claims of a type we don’t recognize. */
1042 for (j = 0; j < claims_array->claims_count; ++j) {
1043 const struct CLAIM_ENTRY *claim_entry = &claims_array->claim_entries[j];
1044 const char *name = NULL;
1045 union claim_values *claim_values = NULL;
1047 enum security_claim_value_type value_type;
1049 switch (claim_entry->type) {
1050 case CLAIM_TYPE_INT64:
1052 const struct CLAIM_INT64 *values = &claim_entry->values.claim_int64;
1054 int64_t *claim_values_int64 = NULL;
1056 n_values = values->value_count;
1057 value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
1059 claim_values = talloc_array(claims,
1062 if (claim_values == NULL) {
1063 talloc_free(claims);
1064 return NT_STATUS_NO_MEMORY;
1066 claim_values_int64 = talloc_array(claims,
1069 if (claim_values_int64 == NULL) {
1070 talloc_free(claims);
1071 return NT_STATUS_NO_MEMORY;
1074 for (k = 0; k < n_values; ++k) {
1075 claim_values_int64[k] = values->values[k];
1076 claim_values[k].int_value = &claim_values_int64[k];
1081 case CLAIM_TYPE_UINT64:
1082 case CLAIM_TYPE_BOOLEAN:
1084 const struct CLAIM_UINT64 *values = &claim_entry->values.claim_uint64;
1086 uint64_t *claim_values_uint64 = NULL;
1088 n_values = values->value_count;
1089 value_type = (claim_entry->type == CLAIM_TYPE_UINT64)
1090 ? CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64
1091 : CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN;
1093 claim_values = talloc_array(claims,
1096 if (claim_values == NULL) {
1097 talloc_free(claims);
1098 return NT_STATUS_NO_MEMORY;
1101 claim_values_uint64 = talloc_array(claims,
1104 if (claim_values_uint64 == NULL) {
1105 talloc_free(claims);
1106 return NT_STATUS_NO_MEMORY;
1109 for (k = 0; k < n_values; ++k) {
1110 claim_values_uint64[k] = values->values[k];
1111 claim_values[k].uint_value = &claim_values_uint64[k];
1116 case CLAIM_TYPE_STRING:
1118 const struct CLAIM_STRING *values = &claim_entry->values.claim_string;
1120 bool seen_empty = false;
1121 n_values = values->value_count;
1122 value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
1124 claim_values = talloc_array(claims,
1127 if (claim_values == NULL) {
1128 talloc_free(claims);
1129 return NT_STATUS_NO_MEMORY;
1133 for (k = 0; k < n_values; ++k) {
1134 const char *string_value = NULL;
1136 if (values->values[k] != NULL) {
1137 string_value = talloc_strdup(claim_values, values->values[k]);
1138 if (string_value == NULL) {
1139 talloc_free(claims);
1140 return NT_STATUS_NO_MEMORY;
1142 claim_values[m].string_value = string_value;
1146 * We allow one NULL string
1147 * per claim, but not two,
1148 * because two would be a
1149 * duplicate, and we don't
1150 * want those (duplicates in
1151 * actual values are checked
1155 talloc_free(claims);
1156 return NT_STATUS_INVALID_PARAMETER;
1166 * Other claim types are unsupported — just skip
1172 if (claim_entry->id != NULL) {
1173 name = talloc_strdup(claims, claim_entry->id);
1175 talloc_free(claims);
1176 return NT_STATUS_NO_MEMORY;
1180 claims[n_claims] = (struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1) {
1182 .value_type = value_type,
1184 .value_count = n_values,
1185 .values = claim_values,
1188 status = claim_v1_check_and_sort(claims, &claims[n_claims],
1190 if (!NT_STATUS_IS_OK(status)) {
1191 talloc_free(claims);
1192 DBG_WARNING("claim sort and uniqueness test failed with %s\n",
1199 *out_claims = claims;
1200 *out_n_claims = n_claims;
1202 return NT_STATUS_OK;