smbd: Save 3 lines
[Samba.git] / libcli / security / claims-conversions.c
blobccf1375fc8f990361934aa05262c1342fa0c3022
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"
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
47 * on the wire.
51 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)
57 char *s = talloc_strdup(mem_ctx,
58 claim->values[offset].string_value);
59 if (s == NULL) {
60 return false;
63 result->type = CONDITIONAL_ACE_TOKEN_UNICODE;
64 result->data.unicode.value = s;
65 return true;
69 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)
75 DATA_BLOB *v = NULL;
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);
84 return false;
86 if (v->length != 0) {
87 w = data_blob_talloc(mem_ctx, v->data, v->length);
88 if (w.data == NULL) {
89 return false;
93 result->type = CONDITIONAL_ACE_TOKEN_OCTET_STRING;
94 result->data.bytes = w;
95 return true;
99 static bool blob_string_sid_to_sid(DATA_BLOB *blob,
100 struct dom_sid *sid)
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
112 * isn't SDDL.
114 bool ok;
115 size_t len = blob->length;
116 char buf[DOM_SID_STR_BUFLEN + 1]; /* 191 + 1 */
117 const char *end = NULL;
118 char *str = NULL;
120 if (len < 5 || len >= DOM_SID_STR_BUFLEN) {
121 return false;
123 if (blob->data[len - 1] == '\0') {
124 str = (char *)blob->data;
125 len--;
126 } else {
127 memcpy(buf, blob->data, len);
128 buf[len] = 0;
129 str = buf;
132 ok = dom_sid_parse_endp(str, sid, &end);
133 if (!ok) {
134 return false;
137 if (end - str != len) {
138 return false;
140 return true;
144 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)
150 * In the _V1 struct, SIDs are stored as octet string blobs,
151 * as *SID strings*.
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
156 * resource ACEs.
158 DATA_BLOB *v = NULL;
159 bool ok;
161 v = claim->values[offset].sid_value;
163 ok = blob_string_sid_to_sid(v, &result->data.sid.sid);
164 if (! ok) {
165 DBG_WARNING("claim has invalid SID string of length %zu.\n",
166 v->length);
167 return false;
170 result->type = CONDITIONAL_ACE_TOKEN_SID;
171 return true;
175 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)
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 -.
193 if (v < 0) {
194 result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NEGATIVE;
195 } else {
196 result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE;
199 return true;
203 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)
208 uint64_t v = *claim->values[offset].uint_value;
209 if (v > INT64_MAX) {
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
215 * unsigned?
217 return false;
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;
223 return true;
227 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)
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;
237 return true;
241 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)
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 if (claim->values[offset].int_value == NULL) {
266 return false;
268 switch (claim->value_type) {
269 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
270 return claim_v1_int_to_ace_int(claim, offset, result);
271 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
272 return claim_v1_unsigned_int_to_ace_int(claim, offset, result);
273 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
274 return claim_v1_string_to_ace_string(mem_ctx, claim, offset,
275 result);
276 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
277 return claim_v1_sid_to_ace_sid(claim, offset, result);
278 case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
279 return claim_v1_bool_to_ace_int(claim, offset, result);
280 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
281 return claim_v1_octet_string_to_ace_octet_string(mem_ctx,
282 claim,
283 offset,
284 result);
285 default:
286 return false;
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);
298 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)
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:
308 * 1. claim_v1_to_ace_token() below (which does do those
309 * checks, and is the function you want).
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.
316 size_t i;
317 struct ace_condition_token *tokens = NULL;
318 bool ok;
320 tokens = talloc_array(mem_ctx,
321 struct ace_condition_token,
322 claim->value_count);
323 if (tokens == NULL) {
324 return false;
327 for (i = 0; i < claim->value_count; i++) {
328 ok = claim_v1_offset_to_ace_token(tokens,
329 claim,
331 &tokens[i]);
332 if (! ok) {
333 TALLOC_FREE(tokens);
334 return false;
338 result->type = CONDITIONAL_ACE_TOKEN_COMPOSITE;
339 result->data.composite.tokens = tokens;
340 result->data.composite.n_members = claim->value_count;
341 result->flags = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
342 return true;
346 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)
350 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim_copy = NULL;
351 const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *sorted_claim = NULL;
352 NTSTATUS status;
353 bool ok;
354 bool case_sensitive = claim->flags & \
355 CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
357 if (claim->value_count < 1 ||
358 claim->value_count >= CONDITIONAL_ACE_MAX_TOKENS) {
359 DBG_WARNING("rejecting claim with %"PRIu32" tokens\n",
360 claim->value_count);
361 return false;
364 * if there is one, we return a single thing of that type; if
365 * there are many, we return a composite.
368 if (claim->value_count == 1) {
369 return claim_v1_offset_to_ace_token(mem_ctx,
370 claim,
372 result);
375 if (claim->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED) {
377 * We can avoid making a sorted copy.
379 * This is normal case for wire claims, where the
380 * sorting and duplicate checking happens earlier in
381 * token_claims_to_claims_v1().
383 sorted_claim = claim;
384 } else {
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).
394 claim_copy = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
395 if (claim_copy == NULL) {
396 return false;
399 ok = claim_v1_copy(claim_copy, claim_copy, claim);
400 if (!ok) {
401 TALLOC_FREE(claim_copy);
402 return false;
405 status = claim_v1_check_and_sort(claim_copy, claim_copy,
406 case_sensitive);
407 if (!NT_STATUS_IS_OK(status)) {
408 DBG_WARNING("resource attribute claim sort failed with %s\n",
409 nt_errstr(status));
410 TALLOC_FREE(claim_copy);
411 return false;
413 sorted_claim = claim_copy;
415 ok = claim_v1_to_ace_composite_unchecked(mem_ctx, sorted_claim, result);
416 if (! ok) {
417 TALLOC_FREE(claim_copy);
418 return false;
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.
429 result->flags |= (
430 CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR |
431 CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED);
433 return true;
438 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)
443 int64_t *v = talloc(mem_ctx, int64_t);
444 if (v == NULL) {
445 return false;
447 *v = tok->data.int64.value;
448 claim->values[offset].int_value = v;
449 return true;
453 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)
458 const char *s = talloc_strdup(mem_ctx,
459 tok->data.unicode.value);
460 if (s == NULL) {
461 return false;
463 claim->values[offset].string_value = s;
464 return true;
469 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)
474 /* claim_v1 sid is an "S-1-*" string data blob, not struct dom_sid. */
475 char *s = NULL;
477 DATA_BLOB *blob = NULL;
478 blob = talloc(mem_ctx, DATA_BLOB);
479 if (blob == NULL) {
480 return false;
482 s = dom_sid_string(blob, &tok->data.sid.sid);
483 if (s == NULL) {
484 TALLOC_FREE(blob);
485 return false;
487 *blob = data_blob_string_const(s);
488 claim->values[offset].sid_value = blob;
489 return true;
492 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)
498 DATA_BLOB *v = talloc(mem_ctx, DATA_BLOB);
499 if (v == NULL) {
500 return false;
503 *v = data_blob_talloc(v,
504 tok->data.bytes.data,
505 tok->data.bytes.length);
506 if (v->data == NULL) {
507 return false;
510 claim->values[offset].octet_value = v;
511 return true;
516 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)
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.
525 * For example, if offset is 3, claim->values[3] will be
526 * turned into *result.
528 if (offset >= claim->value_count) {
529 return false;
531 switch (claim->value_type) {
532 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
533 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
534 return ace_int_to_claim_v1_int(mem_ctx, tok, claim, offset);
535 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
536 return ace_string_to_claim_v1_string(mem_ctx, tok, claim, offset);
537 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
538 return ace_sid_to_claim_v1_sid(mem_ctx, tok, claim, offset);
539 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
540 return ace_octet_string_to_claim_v1_octet_string(mem_ctx,
541 tok,
542 claim,
543 offset);
544 default:
545 /*bool unimplemented, because unreachable */
546 return false;
551 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)
557 size_t i;
558 bool ok;
559 bool is_comp = false;
560 int claim_type = -1;
561 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *_claim = NULL;
562 uint32_t value_count;
564 if (name == NULL || claim == NULL || tok == NULL) {
565 return false;
567 *claim = NULL;
569 if (tok->type == CONDITIONAL_ACE_TOKEN_COMPOSITE) {
570 is_comp = true;
571 /* there must be values, all of the same type */
572 if (tok->data.composite.n_members == 0) {
573 DBG_WARNING("Empty ACE composite list\n");
574 return false;
576 if (tok->data.composite.n_members > 1) {
577 for (i = 1; i < tok->data.composite.n_members; i++) {
578 if (tok->data.composite.tokens[i].type !=
579 tok->data.composite.tokens[0].type) {
580 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 return false;
589 value_count = tok->data.composite.n_members;
591 switch (tok->data.composite.tokens[0].type) {
592 case CONDITIONAL_ACE_TOKEN_INT8:
593 case CONDITIONAL_ACE_TOKEN_INT16:
594 case CONDITIONAL_ACE_TOKEN_INT32:
595 case CONDITIONAL_ACE_TOKEN_INT64:
596 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
597 break;
598 case CONDITIONAL_ACE_TOKEN_UNICODE:
599 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
600 break;
601 case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
602 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING;
603 break;
604 case CONDITIONAL_ACE_TOKEN_SID:
605 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID;
606 break;
607 default:
608 /* reject nested composites, no uint or bool. */
609 DBG_WARNING("ACE composite list has invalid type %u\n",
610 tok->data.composite.tokens[0].type);
611 return false;
613 } else {
614 value_count = 1;
615 switch(tok->type) {
616 case CONDITIONAL_ACE_TOKEN_INT8:
617 case CONDITIONAL_ACE_TOKEN_INT16:
618 case CONDITIONAL_ACE_TOKEN_INT32:
619 case CONDITIONAL_ACE_TOKEN_INT64:
620 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
621 break;
622 case CONDITIONAL_ACE_TOKEN_UNICODE:
623 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
624 break;
625 case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
626 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING;
627 break;
628 case CONDITIONAL_ACE_TOKEN_SID:
629 claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID;
630 break;
631 default:
633 * no way of creating bool or uint values,
634 * composite is handled above.
636 DBG_WARNING("ACE token has invalid type %u\n",
637 tok->data.composite.tokens[0].type);
638 return false;
642 _claim = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
643 if (_claim == NULL) {
644 return false;
647 _claim->value_count = value_count;
648 _claim->value_type = claim_type;
649 _claim->flags = flags;
650 _claim->name = talloc_strdup(mem_ctx, name);
651 if (_claim->name == NULL) {
652 TALLOC_FREE(_claim);
653 return false;
656 * The values array is actually an array of pointers to
657 * values, even when the values are ints or bools.
659 _claim->values = talloc_array(_claim, union claim_values, value_count);
660 if (_claim->values == NULL) {
661 TALLOC_FREE(_claim);
662 return false;
664 if (! is_comp) {
665 /* there is one value, not a list */
666 ok = ace_token_to_claim_v1_offset(_claim,
667 tok,
668 _claim,
670 if (! ok) {
671 TALLOC_FREE(_claim);
672 return false;
674 } else {
675 /* a composite list of values */
676 for (i = 0; i < value_count; i++) {
677 struct ace_condition_token *t = &tok->data.composite.tokens[i];
678 ok = ace_token_to_claim_v1_offset(mem_ctx,
680 _claim,
682 if (! ok) {
683 TALLOC_FREE(_claim);
684 return false;
690 if (_claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) {
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.
696 bool could_be_uint = true;
697 for (i = 0; i < value_count; i++) {
698 if (*_claim->values[i].int_value < 0) {
699 could_be_uint = false;
700 break;
703 if (could_be_uint) {
704 _claim->value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64;
708 *claim = _claim;
709 return true;
714 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)
719 DATA_BLOB blob = {0};
720 enum ndr_err_code ndr_err;
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.
728 ndr_err = ndr_push_struct_blob(
729 &blob, mem_ctx, src,
730 (ndr_push_flags_fn_t)ndr_push_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
732 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
733 return false;
736 ndr_err = ndr_pull_struct_blob(
737 &blob, mem_ctx, dest,
738 (ndr_pull_flags_fn_t)ndr_pull_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1);
740 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
741 TALLOC_FREE(blob.data);
742 return false;
744 TALLOC_FREE(blob.data);
745 return true;
750 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)
755 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *tmp = NULL;
756 NTSTATUS status;
757 uint32_t *n = NULL;
758 bool ok;
759 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **list = NULL;
760 if (strcmp(claim_type, "device") == 0) {
761 n = &token->num_device_claims;
762 list = &token->device_claims;
763 } else if (strcmp(claim_type, "local") == 0) {
764 n = &token->num_local_claims;
765 list = &token->local_claims;
766 } else if (strcmp(claim_type, "user") == 0) {
767 n = &token->num_user_claims;
768 list = &token->user_claims;
769 } else {
770 return false;
772 if ((*n) == UINT32_MAX) {
773 return false;
776 tmp = talloc_realloc(mem_ctx,
777 *list,
778 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1,
779 (*n) + 1);
780 if (tmp == NULL) {
781 return false;
784 ok = claim_v1_copy(mem_ctx, &tmp[*n], claim);
785 if (! ok ) {
786 TALLOC_FREE(tmp);
787 return false;
790 status = claim_v1_check_and_sort(tmp, &tmp[*n],
791 claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE);
792 if (!NT_STATUS_IS_OK(status)) {
793 DBG_WARNING("resource attribute claim sort failed with %s\n",
794 nt_errstr(status));
795 TALLOC_FREE(tmp);
796 return false;
799 (*n)++;
800 *list = tmp;
801 return true;
805 static NTSTATUS claim_v1_check_and_sort_boolean(
806 TALLOC_CTX *mem_ctx,
807 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim)
810 * There are so few valid orders in a boolean claim that we can
811 * enumerate them all.
813 switch (claim->value_count) {
814 case 0:
815 return NT_STATUS_OK;
816 case 1:
817 if (*claim->values[0].uint_value == 0 ||
818 *claim->values[0].uint_value == 1) {
819 return NT_STATUS_OK;
821 break;
822 case 2:
823 if (*claim->values[0].uint_value == 1) {
824 /* switch the order. */
825 *claim->values[0].uint_value = *claim->values[1].uint_value;
826 *claim->values[1].uint_value = 1;
828 if (*claim->values[0].uint_value == 0 &&
829 *claim->values[1].uint_value == 1) {
830 return NT_STATUS_OK;
832 break;
833 default:
834 /* 3 or more must have duplicates. */
835 break;
837 return NT_STATUS_INVALID_PARAMETER;
841 struct claim_sort_context {
842 uint16_t value_type;
843 bool failed;
844 bool case_sensitive;
847 static int claim_sort_cmp(const union claim_values *lhs,
848 const union claim_values *rhs,
849 struct claim_sort_context *ctx)
852 * These comparisons have to match those used in
853 * conditional_ace.c.
855 int cmp;
857 switch (ctx->value_type) {
858 case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
859 case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
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.
867 int64_t a, b;
868 if (ctx->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) {
869 a = *lhs->int_value;
870 b = *rhs->int_value;
871 } else {
872 a = (int64_t)*lhs->uint_value;
873 b = (int64_t)*rhs->uint_value;
875 if (a < b) {
876 return -1;
878 if (a == b) {
879 return 0;
881 return 1;
883 case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
885 const char *a = lhs->string_value;
886 const char *b = rhs->string_value;
887 if (ctx->case_sensitive) {
888 return strcmp(a, b);
890 return strcasecmp_m(a, b);
893 case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
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.
900 * We don't accept SID claims over the wire -- these
901 * are resource attribute ACEs only.
903 struct dom_sid a, b;
904 bool lhs_ok, rhs_ok;
906 lhs_ok = blob_string_sid_to_sid(lhs->sid_value, &a);
907 rhs_ok = blob_string_sid_to_sid(rhs->sid_value, &b);
908 if (!(lhs_ok && rhs_ok)) {
909 ctx->failed = true;
910 return -1;
912 cmp = dom_sid_compare(&a, &b);
913 return cmp;
915 case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
917 const DATA_BLOB *a = lhs->octet_value;
918 const DATA_BLOB *b = rhs->octet_value;
919 return data_blob_cmp(a, b);
921 default:
922 ctx->failed = true;
923 break;
925 return -1;
929 NTSTATUS claim_v1_check_and_sort(TALLOC_CTX *mem_ctx,
930 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
931 bool case_sensitive)
933 bool ok;
934 uint32_t i;
935 struct claim_sort_context sort_ctx = {
936 .failed = false,
937 .value_type = claim->value_type,
938 .case_sensitive = case_sensitive
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.
945 for (i = 0; i < claim->value_count; i++) {
946 if (claim->values[i].int_value == NULL) {
947 return NT_STATUS_INVALID_PARAMETER;
951 if (claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN) {
952 NTSTATUS status = claim_v1_check_and_sort_boolean(mem_ctx, claim);
953 if (NT_STATUS_IS_OK(status)) {
954 claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
956 return status;
959 ok = stable_sort_talloc_r(mem_ctx,
960 claim->values,
961 claim->value_count,
962 sizeof(union claim_values),
963 (samba_compare_with_context_fn_t)claim_sort_cmp,
964 &sort_ctx);
965 if (!ok) {
966 return NT_STATUS_NO_MEMORY;
969 if (sort_ctx.failed) {
970 /* this failure probably means a bad SID string */
971 DBG_WARNING("claim sort of %"PRIu32" members, type %"PRIu16" failed\n",
972 claim->value_count,
973 claim->value_type);
974 return NT_STATUS_INVALID_PARAMETER;
977 for (i = 1; i < claim->value_count; i++) {
978 int cmp = claim_sort_cmp(&claim->values[i - 1],
979 &claim->values[i],
980 &sort_ctx);
981 if (cmp == 0) {
982 DBG_WARNING("duplicate values in claim\n");
983 return NT_STATUS_INVALID_PARAMETER;
985 if (cmp > 0) {
986 DBG_ERR("claim sort failed!\n");
987 return NT_STATUS_INVALID_PARAMETER;
990 if (case_sensitive) {
991 claim->flags |= CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
993 claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;
994 return NT_STATUS_OK;
998 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)
1003 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claims = NULL;
1004 uint32_t n_claims = 0;
1005 uint32_t expected_n_claims = 0;
1006 uint32_t i;
1007 NTSTATUS status;
1009 if (out_claims == NULL) {
1010 return NT_STATUS_INVALID_PARAMETER;
1012 if (out_n_claims == NULL) {
1013 return NT_STATUS_INVALID_PARAMETER;
1016 *out_claims = NULL;
1017 *out_n_claims = 0;
1019 if (claims_set == NULL) {
1020 return NT_STATUS_OK;
1024 * The outgoing number of claims is (at most) the sum of the
1025 * claims_counts of each claims_array.
1027 for (i = 0; i < claims_set->claims_array_count; ++i) {
1028 uint32_t count = claims_set->claims_arrays[i].claims_count;
1029 expected_n_claims += count;
1030 if (expected_n_claims < count) {
1031 return NT_STATUS_INVALID_PARAMETER;
1035 claims = talloc_array(mem_ctx,
1036 struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1,
1037 expected_n_claims);
1038 if (claims == NULL) {
1039 return NT_STATUS_NO_MEMORY;
1042 for (i = 0; i < claims_set->claims_array_count; ++i) {
1043 const struct CLAIMS_ARRAY *claims_array = &claims_set->claims_arrays[i];
1044 uint32_t j;
1046 switch (claims_array->claims_source_type) {
1047 case CLAIMS_SOURCE_TYPE_AD:
1048 case CLAIMS_SOURCE_TYPE_CERTIFICATE:
1049 break;
1050 default:
1051 /* Ignore any claims of a type we don’t recognize. */
1052 continue;
1055 for (j = 0; j < claims_array->claims_count; ++j) {
1056 const struct CLAIM_ENTRY *claim_entry = &claims_array->claim_entries[j];
1057 const char *name = NULL;
1058 union claim_values *claim_values = NULL;
1059 uint32_t n_values;
1060 enum security_claim_value_type value_type;
1062 switch (claim_entry->type) {
1063 case CLAIM_TYPE_INT64:
1065 const struct CLAIM_INT64 *values = &claim_entry->values.claim_int64;
1066 uint32_t k;
1067 int64_t *claim_values_int64 = NULL;
1069 n_values = values->value_count;
1070 value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64;
1072 claim_values = talloc_array(claims,
1073 union claim_values,
1074 n_values);
1075 if (claim_values == NULL) {
1076 talloc_free(claims);
1077 return NT_STATUS_NO_MEMORY;
1079 claim_values_int64 = talloc_array(claims,
1080 int64_t,
1081 n_values);
1082 if (claim_values_int64 == NULL) {
1083 talloc_free(claims);
1084 return NT_STATUS_NO_MEMORY;
1087 for (k = 0; k < n_values; ++k) {
1088 claim_values_int64[k] = values->values[k];
1089 claim_values[k].int_value = &claim_values_int64[k];
1092 break;
1094 case CLAIM_TYPE_UINT64:
1095 case CLAIM_TYPE_BOOLEAN:
1097 const struct CLAIM_UINT64 *values = &claim_entry->values.claim_uint64;
1098 uint32_t k;
1099 uint64_t *claim_values_uint64 = NULL;
1101 n_values = values->value_count;
1102 value_type = (claim_entry->type == CLAIM_TYPE_UINT64)
1103 ? CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64
1104 : CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN;
1106 claim_values = talloc_array(claims,
1107 union claim_values,
1108 n_values);
1109 if (claim_values == NULL) {
1110 talloc_free(claims);
1111 return NT_STATUS_NO_MEMORY;
1114 claim_values_uint64 = talloc_array(claims,
1115 uint64_t,
1116 n_values);
1117 if (claim_values_uint64 == NULL) {
1118 talloc_free(claims);
1119 return NT_STATUS_NO_MEMORY;
1122 for (k = 0; k < n_values; ++k) {
1123 claim_values_uint64[k] = values->values[k];
1124 claim_values[k].uint_value = &claim_values_uint64[k];
1127 break;
1129 case CLAIM_TYPE_STRING:
1131 const struct CLAIM_STRING *values = &claim_entry->values.claim_string;
1132 uint32_t k, m;
1133 bool seen_empty = false;
1134 n_values = values->value_count;
1135 value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING;
1137 claim_values = talloc_array(claims,
1138 union claim_values,
1139 n_values);
1140 if (claim_values == NULL) {
1141 talloc_free(claims);
1142 return NT_STATUS_NO_MEMORY;
1145 m = 0;
1146 for (k = 0; k < n_values; ++k) {
1147 const char *string_value = NULL;
1149 if (values->values[k] != NULL) {
1150 string_value = talloc_strdup(claim_values, values->values[k]);
1151 if (string_value == NULL) {
1152 talloc_free(claims);
1153 return NT_STATUS_NO_MEMORY;
1155 claim_values[m].string_value = string_value;
1156 m++;
1157 } else {
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).
1167 if (seen_empty) {
1168 talloc_free(claims);
1169 return NT_STATUS_INVALID_PARAMETER;
1171 seen_empty = true;
1174 n_values = m;
1175 break;
1177 default:
1179 * Other claim types are unsupported — just skip
1180 * them.
1182 continue;
1185 if (claim_entry->id != NULL) {
1186 name = talloc_strdup(claims, claim_entry->id);
1187 if (name == NULL) {
1188 talloc_free(claims);
1189 return NT_STATUS_NO_MEMORY;
1193 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,
1201 status = claim_v1_check_and_sort(claims, &claims[n_claims],
1202 false);
1203 if (!NT_STATUS_IS_OK(status)) {
1204 talloc_free(claims);
1205 DBG_WARNING("claim sort and uniqueness test failed with %s\n",
1206 nt_errstr(status));
1207 return status;
1209 n_claims++;
1212 *out_claims = claims;
1213 *out_n_claims = n_claims;
1215 return NT_STATUS_OK;