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"
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
52 static bool claim_v1_string_to_ace_string(
54 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
,
56 struct ace_condition_token
*result
)
58 char *s
= talloc_strdup(mem_ctx
,
59 claim
->values
[offset
].string_value
);
64 result
->type
= CONDITIONAL_ACE_TOKEN_UNICODE
;
65 result
->data
.unicode
.value
= s
;
70 static bool claim_v1_octet_string_to_ace_octet_string(
72 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
,
74 struct ace_condition_token
*result
)
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
);
88 w
= data_blob_talloc(mem_ctx
, v
->data
, v
->length
);
94 result
->type
= CONDITIONAL_ACE_TOKEN_OCTET_STRING
;
95 result
->data
.bytes
= w
;
100 static bool blob_string_sid_to_sid(DATA_BLOB
*blob
,
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
116 size_t len
= blob
->length
;
117 char buf
[DOM_SID_STR_BUFLEN
+ 1]; /* 191 + 1 */
118 const char *end
= NULL
;
121 if (len
< 5 || len
>= DOM_SID_STR_BUFLEN
) {
124 if (blob
->data
[len
- 1] == '\0') {
125 str
= (char *)blob
->data
;
128 memcpy(buf
, blob
->data
, len
);
133 ok
= dom_sid_parse_endp(str
, sid
, &end
);
138 if (str
+ len
!= end
) {
145 static bool claim_v1_sid_to_ace_sid(
146 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
,
148 struct ace_condition_token
*result
)
151 * In the _V1 struct, SIDs are stored as octet string blobs,
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
162 v
= claim
->values
[offset
].sid_value
;
164 ok
= blob_string_sid_to_sid(v
, &result
->data
.sid
.sid
);
166 DBG_WARNING("claim has invalid SID string of length %zu.\n",
171 result
->type
= CONDITIONAL_ACE_TOKEN_SID
;
176 static bool claim_v1_int_to_ace_int(
177 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
,
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 -.
195 result
->data
.int64
.sign
= CONDITIONAL_ACE_INT_SIGN_NEGATIVE
;
197 result
->data
.int64
.sign
= CONDITIONAL_ACE_INT_SIGN_NONE
;
204 static bool claim_v1_unsigned_int_to_ace_int(
205 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
,
207 struct ace_condition_token
*result
)
209 uint64_t v
= *claim
->values
[offset
].uint_value
;
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
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
;
228 static bool claim_v1_bool_to_ace_int(
229 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
,
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;
242 static bool claim_v1_offset_to_ace_token(
244 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
,
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
) {
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
,
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
,
292 static bool claim_v1_copy(
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(
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.
318 struct ace_condition_token
*tokens
= NULL
;
321 tokens
= talloc_array(mem_ctx
,
322 struct ace_condition_token
,
324 if (tokens
== NULL
) {
328 for (i
= 0; i
< claim
->value_count
; i
++) {
329 ok
= claim_v1_offset_to_ace_token(tokens
,
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
;
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
;
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",
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
,
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
;
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
395 claim_copy
= talloc(mem_ctx
, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
);
396 if (claim_copy
== NULL
) {
400 ok
= claim_v1_copy(claim_copy
, claim_copy
, claim
);
402 TALLOC_FREE(claim_copy
);
406 status
= claim_v1_check_and_sort(claim_copy
, claim_copy
,
408 if (!NT_STATUS_IS_OK(status
)) {
409 DBG_WARNING("resource attribute claim sort failed with %s\n",
411 TALLOC_FREE(claim_copy
);
414 sorted_claim
= claim_copy
;
416 ok
= claim_v1_to_ace_composite_unchecked(mem_ctx
, sorted_claim
, result
);
418 TALLOC_FREE(claim_copy
);
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.
431 CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR
|
432 CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED
);
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
,
444 int64_t *v
= talloc(mem_ctx
, int64_t);
448 *v
= tok
->data
.int64
.value
;
449 claim
->values
[offset
].int_value
= v
;
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
,
459 const char *s
= talloc_strdup(mem_ctx
,
460 tok
->data
.unicode
.value
);
464 claim
->values
[offset
].string_value
= s
;
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
,
475 /* claim_v1 sid is an "S-1-*" string data blob, not struct dom_sid. */
478 DATA_BLOB
*blob
= NULL
;
479 blob
= talloc(mem_ctx
, DATA_BLOB
);
483 s
= dom_sid_string(blob
, &tok
->data
.sid
.sid
);
488 *blob
= data_blob_string_const(s
);
489 claim
->values
[offset
].sid_value
= blob
;
493 static bool ace_octet_string_to_claim_v1_octet_string(
495 const struct ace_condition_token
*tok
,
496 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
,
499 DATA_BLOB
*v
= talloc(mem_ctx
, DATA_BLOB
);
504 *v
= data_blob_talloc(v
,
505 tok
->data
.bytes
.data
,
506 tok
->data
.bytes
.length
);
507 if (v
->data
== NULL
) {
511 claim
->values
[offset
].octet_value
= v
;
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
,
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
) {
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
,
546 /*bool unimplemented, because unreachable */
552 bool ace_token_to_claim_v1(TALLOC_CTX
*mem_ctx
,
554 const struct ace_condition_token
*tok
,
555 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
**claim
,
560 bool is_comp
= false;
562 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*_claim
= NULL
;
563 uint32_t value_count
;
565 if (name
== NULL
|| claim
== NULL
|| tok
== NULL
) {
570 if (tok
->type
== CONDITIONAL_ACE_TOKEN_COMPOSITE
) {
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");
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
) {
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
);
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
;
599 case CONDITIONAL_ACE_TOKEN_UNICODE
:
600 claim_type
= CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING
;
602 case CONDITIONAL_ACE_TOKEN_OCTET_STRING
:
603 claim_type
= CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING
;
605 case CONDITIONAL_ACE_TOKEN_SID
:
606 claim_type
= CLAIM_SECURITY_ATTRIBUTE_TYPE_SID
;
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
);
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
;
623 case CONDITIONAL_ACE_TOKEN_UNICODE
:
624 claim_type
= CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING
;
626 case CONDITIONAL_ACE_TOKEN_OCTET_STRING
:
627 claim_type
= CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING
;
629 case CONDITIONAL_ACE_TOKEN_SID
:
630 claim_type
= CLAIM_SECURITY_ATTRIBUTE_TYPE_SID
;
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
);
643 _claim
= talloc(mem_ctx
, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
);
644 if (_claim
== NULL
) {
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
) {
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
) {
666 /* there is one value, not a list */
667 ok
= ace_token_to_claim_v1_offset(_claim
,
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
,
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;
705 _claim
->value_type
= CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64
;
715 static bool claim_v1_copy(
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(
731 (ndr_push_flags_fn_t
)ndr_push_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
);
733 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
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
);
745 TALLOC_FREE(blob
.data
);
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
;
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
;
773 if ((*n
) == UINT32_MAX
) {
777 tmp
= talloc_realloc(mem_ctx
,
779 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
,
785 ok
= claim_v1_copy(mem_ctx
, &tmp
[*n
], claim
);
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",
806 static NTSTATUS
claim_v1_check_and_sort_boolean(
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
) {
818 if (*claim
->values
[0].uint_value
== 0 ||
819 *claim
->values
[0].uint_value
== 1) {
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) {
835 /* 3 or more must have duplicates. */
838 return NT_STATUS_INVALID_PARAMETER
;
842 struct claim_sort_context
{
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
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.
869 if (ctx
->value_type
== CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64
) {
873 a
= (int64_t)*lhs
->uint_value
;
874 b
= (int64_t)*rhs
->uint_value
;
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
) {
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.
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
)) {
913 cmp
= dom_sid_compare(&a
, &b
);
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
);
930 NTSTATUS
claim_v1_check_and_sort(TALLOC_CTX
*mem_ctx
,
931 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
*claim
,
936 struct claim_sort_context sort_ctx
= {
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
;
960 ok
= stable_sort_talloc_r(mem_ctx
,
963 sizeof(union claim_values
),
964 (samba_compare_with_context_fn_t
)claim_sort_cmp
,
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",
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],
983 DBG_WARNING("duplicate values in claim\n");
984 return NT_STATUS_INVALID_PARAMETER
;
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
;
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;
1010 if (out_claims
== NULL
) {
1011 return NT_STATUS_INVALID_PARAMETER
;
1013 if (out_n_claims
== NULL
) {
1014 return NT_STATUS_INVALID_PARAMETER
;
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
,
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
];
1047 switch (claims_array
->claims_source_type
) {
1048 case CLAIMS_SOURCE_TYPE_AD
:
1049 case CLAIMS_SOURCE_TYPE_CERTIFICATE
:
1052 /* Ignore any claims of a type we don’t recognize. */
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
;
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
;
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
,
1076 if (claim_values
== NULL
) {
1077 talloc_free(claims
);
1078 return NT_STATUS_NO_MEMORY
;
1080 claim_values_int64
= talloc_array(claims
,
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
];
1095 case CLAIM_TYPE_UINT64
:
1096 case CLAIM_TYPE_BOOLEAN
:
1098 const struct CLAIM_UINT64
*values
= &claim_entry
->values
.claim_uint64
;
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
,
1110 if (claim_values
== NULL
) {
1111 talloc_free(claims
);
1112 return NT_STATUS_NO_MEMORY
;
1115 claim_values_uint64
= talloc_array(claims
,
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
];
1130 case CLAIM_TYPE_STRING
:
1132 const struct CLAIM_STRING
*values
= &claim_entry
->values
.claim_string
;
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
,
1141 if (claim_values
== NULL
) {
1142 talloc_free(claims
);
1143 return NT_STATUS_NO_MEMORY
;
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
;
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
1169 talloc_free(claims
);
1170 return NT_STATUS_INVALID_PARAMETER
;
1180 * Other claim types are unsupported — just skip
1186 if (claim_entry
->id
!= NULL
) {
1187 name
= talloc_strdup(claims
, claim_entry
->id
);
1189 talloc_free(claims
);
1190 return NT_STATUS_NO_MEMORY
;
1194 claims
[n_claims
] = (struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
) {
1196 .value_type
= value_type
,
1198 .value_count
= n_values
,
1199 .values
= claim_values
,
1202 status
= claim_v1_check_and_sort(claims
, &claims
[n_claims
],
1204 if (!NT_STATUS_IS_OK(status
)) {
1205 talloc_free(claims
);
1206 DBG_WARNING("claim sort and uniqueness test failed with %s\n",
1213 *out_claims
= claims
;
1214 *out_n_claims
= n_claims
;
1216 return NT_STATUS_OK
;