/src/samba/libcli/security/claims-conversions.c
Line | Count | Source |
1 | | /* |
2 | | * Unix SMB implementation. |
3 | | * Utility functions for converting between claims formats. |
4 | | * |
5 | | * This program is free software; you can redistribute it and/or modify |
6 | | * it under the terms of the GNU General Public License as published by |
7 | | * the Free Software Foundation; either version 3 of the License, or |
8 | | * (at your option) any later version. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | * GNU General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public License |
16 | | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include "replace.h" |
20 | | #include "librpc/gen_ndr/ndr_security.h" |
21 | | #include "librpc/gen_ndr/ndr_conditional_ace.h" |
22 | | #include "libcli/security/claims-conversions.h" |
23 | | #include "lib/util/debug.h" |
24 | | #include "lib/util/stable_sort.h" |
25 | | #include "libcli/security/dom_sid.h" |
26 | | |
27 | | #include "librpc/gen_ndr/conditional_ace.h" |
28 | | #include "librpc/gen_ndr/claims.h" |
29 | | |
30 | | /* |
31 | | * We support three formats for claims, all slightly different. |
32 | | * |
33 | | * 1. MS-ADTS 2.2.18.* claims sets, blobs, arrays, or whatever, which |
34 | | * are used in the PAC. |
35 | | * |
36 | | * 2. MS-DTYP 2.4.10.1 CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 |
37 | | * structures, used in security tokens and resource SACL ACEs. |
38 | | * |
39 | | * 3. MS-DTYP 2.4.4.17 Conditional ACE tokens. |
40 | | * |
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. |
44 | | * |
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. |
49 | | */ |
50 | | |
51 | | |
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) |
57 | 23.9k | { |
58 | 23.9k | char *s = talloc_strdup(mem_ctx, |
59 | 23.9k | claim->values[offset].string_value); |
60 | 23.9k | if (s == NULL) { |
61 | 0 | return false; |
62 | 0 | } |
63 | | |
64 | 23.9k | result->type = CONDITIONAL_ACE_TOKEN_UNICODE; |
65 | 23.9k | result->data.unicode.value = s; |
66 | 23.9k | return true; |
67 | 23.9k | } |
68 | | |
69 | | |
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) |
75 | 13.1k | { |
76 | 13.1k | DATA_BLOB *v = NULL; |
77 | 13.1k | DATA_BLOB w = data_blob_null; |
78 | | |
79 | 13.1k | v = claim->values[offset].octet_value; |
80 | | |
81 | 13.1k | if (v->length > CONDITIONAL_ACE_MAX_LENGTH) { |
82 | 0 | DBG_WARNING("claim has octet string of unexpected length %zu " |
83 | 0 | "(expected range 1 - %u)\n", |
84 | 0 | v->length, CONDITIONAL_ACE_MAX_LENGTH); |
85 | 0 | return false; |
86 | 0 | } |
87 | 13.1k | if (v->length != 0) { |
88 | 9.47k | w = data_blob_talloc(mem_ctx, v->data, v->length); |
89 | 9.47k | if (w.data == NULL) { |
90 | 0 | return false; |
91 | 0 | } |
92 | 9.47k | } |
93 | | |
94 | 13.1k | result->type = CONDITIONAL_ACE_TOKEN_OCTET_STRING; |
95 | 13.1k | result->data.bytes = w; |
96 | 13.1k | return true; |
97 | 13.1k | } |
98 | | |
99 | | |
100 | | static bool blob_string_sid_to_sid(DATA_BLOB *blob, |
101 | | struct dom_sid *sid) |
102 | 69.1k | { |
103 | | /* |
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. |
111 | | * |
112 | | * It doesn't seem like SDDL short SIDs ("WD") are accepted here. This |
113 | | * isn't SDDL. |
114 | | */ |
115 | 69.1k | bool ok; |
116 | 69.1k | size_t len = blob->length; |
117 | 69.1k | char buf[DOM_SID_STR_BUFLEN + 1]; /* 191 + 1 */ |
118 | 69.1k | const char *end = NULL; |
119 | 69.1k | char *str = NULL; |
120 | | |
121 | 69.1k | if (len < 5 || len >= DOM_SID_STR_BUFLEN) { |
122 | 18.1k | return false; |
123 | 18.1k | } |
124 | 50.9k | if (blob->data[len - 1] == '\0') { |
125 | 18.7k | str = (char *)blob->data; |
126 | 18.7k | len--; |
127 | 32.2k | } else { |
128 | 32.2k | memcpy(buf, blob->data, len); |
129 | 32.2k | buf[len] = 0; |
130 | 32.2k | str = buf; |
131 | 32.2k | } |
132 | | |
133 | 50.9k | ok = dom_sid_parse_endp(str, sid, &end); |
134 | 50.9k | if (!ok) { |
135 | 32.4k | return false; |
136 | 32.4k | } |
137 | | |
138 | 18.5k | if (str + len != end) { |
139 | 2.30k | return false; |
140 | 2.30k | } |
141 | 16.1k | return true; |
142 | 18.5k | } |
143 | | |
144 | | |
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) |
149 | 14.3k | { |
150 | | /* |
151 | | * In the _V1 struct, SIDs are stored as octet string blobs, |
152 | | * as *SID strings*. |
153 | | * |
154 | | * In the conditional ACE they are stored as struct dom_sid. |
155 | | * |
156 | | * There are no SIDs in ADTS claims, but there can be in |
157 | | * resource ACEs. |
158 | | */ |
159 | 14.3k | DATA_BLOB *v = NULL; |
160 | 14.3k | bool ok; |
161 | | |
162 | 14.3k | v = claim->values[offset].sid_value; |
163 | | |
164 | 14.3k | ok = blob_string_sid_to_sid(v, &result->data.sid.sid); |
165 | 14.3k | if (! ok) { |
166 | 3.40k | DBG_WARNING("claim has invalid SID string of length %zu.\n", |
167 | 3.40k | v->length); |
168 | 3.40k | return false; |
169 | 3.40k | } |
170 | | |
171 | 10.9k | result->type = CONDITIONAL_ACE_TOKEN_SID; |
172 | 10.9k | return true; |
173 | 14.3k | } |
174 | | |
175 | | |
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) |
180 | 24.9k | { |
181 | 24.9k | int64_t v = *claim->values[offset].int_value; |
182 | 24.9k | result->type = CONDITIONAL_ACE_TOKEN_INT64; |
183 | 24.9k | result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10; |
184 | 24.9k | result->data.int64.value = v; |
185 | | |
186 | | /* |
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 -. |
193 | | */ |
194 | 24.9k | if (v < 0) { |
195 | 3.34k | result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NEGATIVE; |
196 | 21.5k | } else { |
197 | 21.5k | result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE; |
198 | 21.5k | } |
199 | | |
200 | 24.9k | return true; |
201 | 24.9k | } |
202 | | |
203 | | |
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) |
208 | 33.5k | { |
209 | 33.5k | uint64_t v = *claim->values[offset].uint_value; |
210 | 33.5k | if (v > INT64_MAX) { |
211 | | /* |
212 | | * The unsigned value can't be represented in a |
213 | | * conditional ACE type. |
214 | | * |
215 | | * XXX or can it? does the positive flag make it |
216 | | * unsigned? |
217 | | */ |
218 | 1.70k | return false; |
219 | 1.70k | } |
220 | 31.7k | result->type = CONDITIONAL_ACE_TOKEN_INT64; |
221 | 31.7k | result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10; |
222 | 31.7k | result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_POSITIVE; |
223 | 31.7k | result->data.int64.value = v; |
224 | 31.7k | return true; |
225 | 33.5k | } |
226 | | |
227 | | |
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) |
232 | 3.20k | { |
233 | 3.20k | uint64_t v = *claim->values[offset].uint_value; |
234 | 3.20k | result->type = CONDITIONAL_ACE_TOKEN_INT64; |
235 | 3.20k | result->data.int64.base = CONDITIONAL_ACE_INT_BASE_10; |
236 | 3.20k | result->data.int64.sign = CONDITIONAL_ACE_INT_SIGN_NONE; |
237 | 3.20k | result->data.int64.value = v ? 1 : 0; |
238 | 3.20k | return true; |
239 | 3.20k | } |
240 | | |
241 | | |
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) |
247 | 115k | { |
248 | | /* |
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. |
251 | | * |
252 | | * For example, if offset is 3, claim->values[3] will be |
253 | | * turned into *result. |
254 | | * |
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). |
259 | | * |
260 | | * The CLAIM_SECURITY_ATTRIBUTE_CASE_SENSITIVE (from the |
261 | | * claim_flags enum in security.idl) is used for both. |
262 | | */ |
263 | 115k | uint8_t f = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE; |
264 | 115k | result->flags = f | CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR; |
265 | | |
266 | 115k | if (claim->values[offset].int_value == NULL) { |
267 | 1.97k | return false; |
268 | 1.97k | } |
269 | 113k | switch (claim->value_type) { |
270 | 24.9k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64: |
271 | 24.9k | return claim_v1_int_to_ace_int(claim, offset, result); |
272 | 33.5k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64: |
273 | 33.5k | return claim_v1_unsigned_int_to_ace_int(claim, offset, result); |
274 | 23.9k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING: |
275 | 23.9k | return claim_v1_string_to_ace_string(mem_ctx, claim, offset, |
276 | 23.9k | result); |
277 | 14.3k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID: |
278 | 14.3k | return claim_v1_sid_to_ace_sid(claim, offset, result); |
279 | 3.20k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN: |
280 | 3.20k | return claim_v1_bool_to_ace_int(claim, offset, result); |
281 | 13.1k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING: |
282 | 13.1k | return claim_v1_octet_string_to_ace_octet_string(mem_ctx, |
283 | 13.1k | claim, |
284 | 13.1k | offset, |
285 | 13.1k | result); |
286 | 0 | default: |
287 | 0 | return false; |
288 | 113k | } |
289 | 113k | } |
290 | | |
291 | | |
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); |
296 | | |
297 | | |
298 | | |
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) |
303 | 23.0k | { |
304 | | /* |
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: |
308 | | * |
309 | | * 1. claim_v1_to_ace_token() below (which does do those |
310 | | * checks, and is the function you want). |
311 | | * |
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. |
316 | | */ |
317 | 23.0k | size_t i; |
318 | 23.0k | struct ace_condition_token *tokens = NULL; |
319 | 23.0k | bool ok; |
320 | | |
321 | 23.0k | tokens = talloc_array(mem_ctx, |
322 | 23.0k | struct ace_condition_token, |
323 | 23.0k | claim->value_count); |
324 | 23.0k | if (tokens == NULL) { |
325 | 0 | return false; |
326 | 0 | } |
327 | | |
328 | 126k | for (i = 0; i < claim->value_count; i++) { |
329 | 108k | ok = claim_v1_offset_to_ace_token(tokens, |
330 | 108k | claim, |
331 | 108k | i, |
332 | 108k | &tokens[i]); |
333 | 108k | if (! ok) { |
334 | 5.30k | TALLOC_FREE(tokens); |
335 | 5.30k | return false; |
336 | 5.30k | } |
337 | 108k | } |
338 | | |
339 | 17.7k | result->type = CONDITIONAL_ACE_TOKEN_COMPOSITE; |
340 | 17.7k | result->data.composite.tokens = tokens; |
341 | 17.7k | result->data.composite.n_members = claim->value_count; |
342 | 17.7k | result->flags = claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE; |
343 | 17.7k | return true; |
344 | 23.0k | } |
345 | | |
346 | | |
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) |
350 | 49.2k | { |
351 | 49.2k | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim_copy = NULL; |
352 | 49.2k | const struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *sorted_claim = NULL; |
353 | 49.2k | NTSTATUS status; |
354 | 49.2k | bool ok; |
355 | 49.2k | bool case_sensitive = claim->flags & \ |
356 | 49.2k | CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE; |
357 | | |
358 | 49.2k | if (claim->value_count < 1 || |
359 | 44.7k | claim->value_count >= CONDITIONAL_ACE_MAX_TOKENS) { |
360 | 4.54k | DBG_WARNING("rejecting claim with %"PRIu32" tokens\n", |
361 | 4.54k | claim->value_count); |
362 | 4.54k | return false; |
363 | 4.54k | } |
364 | | /* |
365 | | * if there is one, we return a single thing of that type; if |
366 | | * there are many, we return a composite. |
367 | | */ |
368 | | |
369 | 44.7k | if (claim->value_count == 1) { |
370 | 6.74k | return claim_v1_offset_to_ace_token(mem_ctx, |
371 | 6.74k | claim, |
372 | 6.74k | 0, |
373 | 6.74k | result); |
374 | 6.74k | } |
375 | | |
376 | 37.9k | if (claim->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED) { |
377 | | /* |
378 | | * We can avoid making a sorted copy. |
379 | | * |
380 | | * This is normal case for wire claims, where the |
381 | | * sorting and duplicate checking happens earlier in |
382 | | * token_claims_to_claims_v1(). |
383 | | */ |
384 | 8.79k | sorted_claim = claim; |
385 | 29.1k | } else { |
386 | | /* |
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). |
394 | | */ |
395 | 29.1k | claim_copy = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1); |
396 | 29.1k | if (claim_copy == NULL) { |
397 | 0 | return false; |
398 | 0 | } |
399 | | |
400 | 29.1k | ok = claim_v1_copy(claim_copy, claim_copy, claim); |
401 | 29.1k | if (!ok) { |
402 | 0 | TALLOC_FREE(claim_copy); |
403 | 0 | return false; |
404 | 0 | } |
405 | | |
406 | 29.1k | status = claim_v1_check_and_sort(claim_copy, claim_copy, |
407 | 29.1k | case_sensitive); |
408 | 29.1k | if (!NT_STATUS_IS_OK(status)) { |
409 | 17.9k | DBG_WARNING("resource attribute claim sort failed with %s\n", |
410 | 17.9k | nt_errstr(status)); |
411 | 17.9k | TALLOC_FREE(claim_copy); |
412 | 17.9k | return false; |
413 | 17.9k | } |
414 | 11.2k | sorted_claim = claim_copy; |
415 | 11.2k | } |
416 | 20.0k | ok = claim_v1_to_ace_composite_unchecked(mem_ctx, sorted_claim, result); |
417 | 20.0k | if (! ok) { |
418 | 5.30k | TALLOC_FREE(claim_copy); |
419 | 5.30k | return false; |
420 | 5.30k | } |
421 | | |
422 | | /* |
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. |
429 | | */ |
430 | 14.6k | result->flags |= ( |
431 | 14.6k | CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR | |
432 | 14.6k | CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED); |
433 | | |
434 | 14.6k | return true; |
435 | 20.0k | } |
436 | | |
437 | | |
438 | | |
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) |
443 | 70.6k | { |
444 | 70.6k | int64_t *v = talloc(mem_ctx, int64_t); |
445 | 70.6k | if (v == NULL) { |
446 | 0 | return false; |
447 | 0 | } |
448 | 70.6k | *v = tok->data.int64.value; |
449 | 70.6k | claim->values[offset].int_value = v; |
450 | 70.6k | return true; |
451 | 70.6k | } |
452 | | |
453 | | |
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) |
458 | 6.12k | { |
459 | 6.12k | const char *s = talloc_strdup(mem_ctx, |
460 | 6.12k | tok->data.unicode.value); |
461 | 6.12k | if (s == NULL) { |
462 | 0 | return false; |
463 | 0 | } |
464 | 6.12k | claim->values[offset].string_value = s; |
465 | 6.12k | return true; |
466 | | |
467 | 6.12k | } |
468 | | |
469 | | |
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) |
474 | 16.4k | { |
475 | | /* claim_v1 sid is an "S-1-*" string data blob, not struct dom_sid. */ |
476 | 16.4k | char *s = NULL; |
477 | | |
478 | 16.4k | DATA_BLOB *blob = NULL; |
479 | 16.4k | blob = talloc(mem_ctx, DATA_BLOB); |
480 | 16.4k | if (blob == NULL) { |
481 | 0 | return false; |
482 | 0 | } |
483 | 16.4k | s = dom_sid_string(blob, &tok->data.sid.sid); |
484 | 16.4k | if (s == NULL) { |
485 | 0 | TALLOC_FREE(blob); |
486 | 0 | return false; |
487 | 0 | } |
488 | 16.4k | *blob = data_blob_string_const(s); |
489 | 16.4k | claim->values[offset].sid_value = blob; |
490 | 16.4k | return true; |
491 | 16.4k | } |
492 | | |
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) |
498 | 6.82k | { |
499 | 6.82k | DATA_BLOB *v = talloc(mem_ctx, DATA_BLOB); |
500 | 6.82k | if (v == NULL) { |
501 | 0 | return false; |
502 | 0 | } |
503 | | |
504 | 6.82k | *v = data_blob_talloc(v, |
505 | 6.82k | tok->data.bytes.data, |
506 | 6.82k | tok->data.bytes.length); |
507 | 6.82k | if (v->data == NULL) { |
508 | 0 | return false; |
509 | 0 | } |
510 | | |
511 | 6.82k | claim->values[offset].octet_value = v; |
512 | 6.82k | return true; |
513 | 6.82k | } |
514 | | |
515 | | |
516 | | |
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) |
521 | 100k | { |
522 | | /* |
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. |
525 | | * |
526 | | * For example, if offset is 3, claim->values[3] will be |
527 | | * turned into *result. |
528 | | */ |
529 | 100k | if (offset >= claim->value_count) { |
530 | 0 | return false; |
531 | 0 | } |
532 | 100k | switch (claim->value_type) { |
533 | 70.6k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64: |
534 | 70.6k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64: |
535 | 70.6k | return ace_int_to_claim_v1_int(mem_ctx, tok, claim, offset); |
536 | 6.12k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING: |
537 | 6.12k | return ace_string_to_claim_v1_string(mem_ctx, tok, claim, offset); |
538 | 16.4k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID: |
539 | 16.4k | return ace_sid_to_claim_v1_sid(mem_ctx, tok, claim, offset); |
540 | 6.82k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING: |
541 | 6.82k | return ace_octet_string_to_claim_v1_octet_string(mem_ctx, |
542 | 6.82k | tok, |
543 | 6.82k | claim, |
544 | 6.82k | offset); |
545 | 0 | default: |
546 | | /*bool unimplemented, because unreachable */ |
547 | 0 | return false; |
548 | 100k | } |
549 | 100k | } |
550 | | |
551 | | |
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) |
557 | 11.2k | { |
558 | 11.2k | size_t i; |
559 | 11.2k | bool ok; |
560 | 11.2k | bool is_comp = false; |
561 | 11.2k | int claim_type = -1; |
562 | 11.2k | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *_claim = NULL; |
563 | 11.2k | uint32_t value_count; |
564 | | |
565 | 11.2k | if (name == NULL || claim == NULL || tok == NULL) { |
566 | 0 | return false; |
567 | 0 | } |
568 | 11.2k | *claim = NULL; |
569 | | |
570 | 11.2k | if (tok->type == CONDITIONAL_ACE_TOKEN_COMPOSITE) { |
571 | 5.39k | is_comp = true; |
572 | | /* there must be values, all of the same type */ |
573 | 5.39k | if (tok->data.composite.n_members == 0) { |
574 | 11 | DBG_WARNING("Empty ACE composite list\n"); |
575 | 11 | return false; |
576 | 11 | } |
577 | 5.38k | if (tok->data.composite.n_members > 1) { |
578 | 94.1k | for (i = 1; i < tok->data.composite.n_members; i++) { |
579 | 88.7k | if (tok->data.composite.tokens[i].type != |
580 | 88.7k | tok->data.composite.tokens[0].type) { |
581 | 0 | DBG_WARNING( |
582 | 0 | "ACE composite list has varying " |
583 | 0 | "types (at least %u and %u)\n", |
584 | 0 | tok->data.composite.tokens[i].type, |
585 | 0 | tok->data.composite.tokens[0].type); |
586 | 0 | return false; |
587 | 0 | } |
588 | 88.7k | } |
589 | 5.38k | } |
590 | 5.38k | value_count = tok->data.composite.n_members; |
591 | | |
592 | 5.38k | switch (tok->data.composite.tokens[0].type) { |
593 | 0 | case CONDITIONAL_ACE_TOKEN_INT8: |
594 | 0 | case CONDITIONAL_ACE_TOKEN_INT16: |
595 | 0 | case CONDITIONAL_ACE_TOKEN_INT32: |
596 | 2.54k | case CONDITIONAL_ACE_TOKEN_INT64: |
597 | 2.54k | claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64; |
598 | 2.54k | break; |
599 | 802 | case CONDITIONAL_ACE_TOKEN_UNICODE: |
600 | 802 | claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING; |
601 | 802 | break; |
602 | 1.27k | case CONDITIONAL_ACE_TOKEN_OCTET_STRING: |
603 | 1.27k | claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING; |
604 | 1.27k | break; |
605 | 764 | case CONDITIONAL_ACE_TOKEN_SID: |
606 | 764 | claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID; |
607 | 764 | break; |
608 | 0 | default: |
609 | | /* reject nested composites, no uint or bool. */ |
610 | 0 | DBG_WARNING("ACE composite list has invalid type %u\n", |
611 | 0 | tok->data.composite.tokens[0].type); |
612 | 0 | return false; |
613 | 5.38k | } |
614 | 5.89k | } else { |
615 | 5.89k | value_count = 1; |
616 | 5.89k | switch(tok->type) { |
617 | 0 | case CONDITIONAL_ACE_TOKEN_INT8: |
618 | 0 | case CONDITIONAL_ACE_TOKEN_INT16: |
619 | 0 | case CONDITIONAL_ACE_TOKEN_INT32: |
620 | 2.47k | case CONDITIONAL_ACE_TOKEN_INT64: |
621 | 2.47k | claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64; |
622 | 2.47k | break; |
623 | 1.13k | case CONDITIONAL_ACE_TOKEN_UNICODE: |
624 | 1.13k | claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING; |
625 | 1.13k | break; |
626 | 1.02k | case CONDITIONAL_ACE_TOKEN_OCTET_STRING: |
627 | 1.02k | claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING; |
628 | 1.02k | break; |
629 | 1.25k | case CONDITIONAL_ACE_TOKEN_SID: |
630 | 1.25k | claim_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_SID; |
631 | 1.25k | break; |
632 | 0 | default: |
633 | | /* |
634 | | * no way of creating bool or uint values, |
635 | | * composite is handled above. |
636 | | */ |
637 | 0 | DBG_WARNING("ACE token has invalid type %u\n", |
638 | 0 | tok->data.composite.tokens[0].type); |
639 | 0 | return false; |
640 | 5.89k | } |
641 | 5.89k | } |
642 | | |
643 | 11.2k | _claim = talloc(mem_ctx, struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1); |
644 | 11.2k | if (_claim == NULL) { |
645 | 0 | return false; |
646 | 0 | } |
647 | | |
648 | 11.2k | _claim->value_count = value_count; |
649 | 11.2k | _claim->value_type = claim_type; |
650 | 11.2k | _claim->flags = flags; |
651 | 11.2k | _claim->name = talloc_strdup(mem_ctx, name); |
652 | 11.2k | if (_claim->name == NULL) { |
653 | 0 | TALLOC_FREE(_claim); |
654 | 0 | return false; |
655 | 0 | } |
656 | | /* |
657 | | * The values array is actually an array of pointers to |
658 | | * values, even when the values are ints or bools. |
659 | | */ |
660 | 11.2k | _claim->values = talloc_array(_claim, union claim_values, value_count); |
661 | 11.2k | if (_claim->values == NULL) { |
662 | 0 | TALLOC_FREE(_claim); |
663 | 0 | return false; |
664 | 0 | } |
665 | 11.2k | if (! is_comp) { |
666 | | /* there is one value, not a list */ |
667 | 5.89k | ok = ace_token_to_claim_v1_offset(_claim, |
668 | 5.89k | tok, |
669 | 5.89k | _claim, |
670 | 5.89k | 0); |
671 | 5.89k | if (! ok) { |
672 | 0 | TALLOC_FREE(_claim); |
673 | 0 | return false; |
674 | 0 | } |
675 | 5.89k | } else { |
676 | | /* a composite list of values */ |
677 | 99.5k | for (i = 0; i < value_count; i++) { |
678 | 94.1k | struct ace_condition_token *t = &tok->data.composite.tokens[i]; |
679 | 94.1k | ok = ace_token_to_claim_v1_offset(mem_ctx, |
680 | 94.1k | t, |
681 | 94.1k | _claim, |
682 | 94.1k | i); |
683 | 94.1k | if (! ok) { |
684 | 0 | TALLOC_FREE(_claim); |
685 | 0 | return false; |
686 | 0 | } |
687 | 94.1k | } |
688 | 5.38k | } |
689 | | |
690 | | |
691 | 11.2k | if (_claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) { |
692 | | /* |
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. |
696 | | */ |
697 | 5.01k | bool could_be_uint = true; |
698 | 66.0k | for (i = 0; i < value_count; i++) { |
699 | 63.2k | if (*_claim->values[i].int_value < 0) { |
700 | 2.21k | could_be_uint = false; |
701 | 2.21k | break; |
702 | 2.21k | } |
703 | 63.2k | } |
704 | 5.01k | if (could_be_uint) { |
705 | 2.80k | _claim->value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64; |
706 | 2.80k | } |
707 | 5.01k | } |
708 | | |
709 | 11.2k | *claim = _claim; |
710 | 11.2k | return true; |
711 | 11.2k | } |
712 | | |
713 | | |
714 | | |
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) |
719 | 29.2k | { |
720 | 29.2k | DATA_BLOB blob = {0}; |
721 | 29.2k | enum ndr_err_code ndr_err; |
722 | | |
723 | | /* |
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. |
727 | | */ |
728 | | |
729 | 29.2k | ndr_err = ndr_push_struct_blob( |
730 | 29.2k | &blob, mem_ctx, src, |
731 | 29.2k | (ndr_push_flags_fn_t)ndr_push_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1); |
732 | | |
733 | 29.2k | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
734 | 0 | return false; |
735 | 0 | } |
736 | | |
737 | 29.2k | ndr_err = ndr_pull_struct_blob( |
738 | 29.2k | &blob, mem_ctx, dest, |
739 | 29.2k | (ndr_pull_flags_fn_t)ndr_pull_CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1); |
740 | | |
741 | 29.2k | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
742 | 0 | TALLOC_FREE(blob.data); |
743 | 0 | return false; |
744 | 0 | } |
745 | 29.2k | TALLOC_FREE(blob.data); |
746 | 29.2k | return true; |
747 | 29.2k | } |
748 | | |
749 | | |
750 | | |
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) |
755 | 20 | { |
756 | 20 | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *tmp = NULL; |
757 | 20 | NTSTATUS status; |
758 | 20 | uint32_t *n = NULL; |
759 | 20 | bool ok; |
760 | 20 | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 **list = NULL; |
761 | 20 | if (strcmp(claim_type, "device") == 0) { |
762 | 8 | n = &token->num_device_claims; |
763 | 8 | list = &token->device_claims; |
764 | 12 | } else if (strcmp(claim_type, "local") == 0) { |
765 | 4 | n = &token->num_local_claims; |
766 | 4 | list = &token->local_claims; |
767 | 8 | } else if (strcmp(claim_type, "user") == 0) { |
768 | 8 | n = &token->num_user_claims; |
769 | 8 | list = &token->user_claims; |
770 | 8 | } else { |
771 | 0 | return false; |
772 | 0 | } |
773 | 20 | if ((*n) == UINT32_MAX) { |
774 | 0 | return false; |
775 | 0 | } |
776 | | |
777 | 20 | tmp = talloc_realloc(mem_ctx, |
778 | 20 | *list, |
779 | 20 | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, |
780 | 20 | (*n) + 1); |
781 | 20 | if (tmp == NULL) { |
782 | 0 | return false; |
783 | 0 | } |
784 | | |
785 | 20 | ok = claim_v1_copy(mem_ctx, &tmp[*n], claim); |
786 | 20 | if (! ok ) { |
787 | 0 | TALLOC_FREE(tmp); |
788 | 0 | return false; |
789 | 0 | } |
790 | | |
791 | 20 | status = claim_v1_check_and_sort(tmp, &tmp[*n], |
792 | 20 | claim->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE); |
793 | 20 | if (!NT_STATUS_IS_OK(status)) { |
794 | 0 | DBG_WARNING("resource attribute claim sort failed with %s\n", |
795 | 0 | nt_errstr(status)); |
796 | 0 | TALLOC_FREE(tmp); |
797 | 0 | return false; |
798 | 0 | } |
799 | | |
800 | 20 | (*n)++; |
801 | 20 | *list = tmp; |
802 | 20 | return true; |
803 | 20 | } |
804 | | |
805 | | |
806 | | static NTSTATUS claim_v1_check_and_sort_boolean( |
807 | | TALLOC_CTX *mem_ctx, |
808 | | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim) |
809 | 2.62k | { |
810 | | /* |
811 | | * There are so few valid orders in a boolean claim that we can |
812 | | * enumerate them all. |
813 | | */ |
814 | 2.62k | switch (claim->value_count) { |
815 | 0 | case 0: |
816 | 0 | return NT_STATUS_OK; |
817 | 0 | case 1: |
818 | 0 | if (*claim->values[0].uint_value == 0 || |
819 | 0 | *claim->values[0].uint_value == 1) { |
820 | 0 | return NT_STATUS_OK; |
821 | 0 | } |
822 | 0 | break; |
823 | 1.90k | case 2: |
824 | 1.90k | if (*claim->values[0].uint_value == 1) { |
825 | | /* switch the order. */ |
826 | 313 | *claim->values[0].uint_value = *claim->values[1].uint_value; |
827 | 313 | *claim->values[1].uint_value = 1; |
828 | 313 | } |
829 | 1.90k | if (*claim->values[0].uint_value == 0 && |
830 | 774 | *claim->values[1].uint_value == 1) { |
831 | 260 | return NT_STATUS_OK; |
832 | 260 | } |
833 | 1.64k | break; |
834 | 1.64k | default: |
835 | | /* 3 or more must have duplicates. */ |
836 | 724 | break; |
837 | 2.62k | } |
838 | 2.36k | return NT_STATUS_INVALID_PARAMETER; |
839 | 2.62k | } |
840 | | |
841 | | |
842 | | struct claim_sort_context { |
843 | | uint16_t value_type; |
844 | | bool failed; |
845 | | bool case_sensitive; |
846 | | }; |
847 | | |
848 | | static int claim_sort_cmp(const union claim_values *lhs, |
849 | | const union claim_values *rhs, |
850 | | struct claim_sort_context *ctx) |
851 | 508k | { |
852 | | /* |
853 | | * These comparisons have to match those used in |
854 | | * conditional_ace.c. |
855 | | */ |
856 | 508k | int cmp; |
857 | | |
858 | 508k | switch (ctx->value_type) { |
859 | 112k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64: |
860 | 211k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64: |
861 | 211k | { |
862 | | /* |
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. |
867 | | */ |
868 | 211k | int64_t a, b; |
869 | 211k | if (ctx->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64) { |
870 | 112k | a = *lhs->int_value; |
871 | 112k | b = *rhs->int_value; |
872 | 112k | } else { |
873 | 99.1k | a = (int64_t)*lhs->uint_value; |
874 | 99.1k | b = (int64_t)*rhs->uint_value; |
875 | 99.1k | } |
876 | 211k | if (a < b) { |
877 | 85.8k | return -1; |
878 | 85.8k | } |
879 | 125k | if (a == b) { |
880 | 75.4k | return 0; |
881 | 75.4k | } |
882 | 49.9k | return 1; |
883 | 125k | } |
884 | 232k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING: |
885 | 232k | { |
886 | 232k | const char *a = lhs->string_value; |
887 | 232k | const char *b = rhs->string_value; |
888 | 232k | if (ctx->case_sensitive) { |
889 | 132k | return strcmp(a, b); |
890 | 132k | } |
891 | 100k | return strcasecmp_m(a, b); |
892 | 232k | } |
893 | | |
894 | 27.4k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID: |
895 | 27.4k | { |
896 | | /* |
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. |
900 | | * |
901 | | * We don't accept SID claims over the wire -- these |
902 | | * are resource attribute ACEs only. |
903 | | */ |
904 | 27.4k | struct dom_sid a, b; |
905 | 27.4k | bool lhs_ok, rhs_ok; |
906 | | |
907 | 27.4k | lhs_ok = blob_string_sid_to_sid(lhs->sid_value, &a); |
908 | 27.4k | rhs_ok = blob_string_sid_to_sid(rhs->sid_value, &b); |
909 | 27.4k | if (!(lhs_ok && rhs_ok)) { |
910 | 25.4k | ctx->failed = true; |
911 | 25.4k | return -1; |
912 | 25.4k | } |
913 | 1.94k | cmp = dom_sid_compare(&a, &b); |
914 | 1.94k | return cmp; |
915 | 27.4k | } |
916 | 36.4k | case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING: |
917 | 36.4k | { |
918 | 36.4k | const DATA_BLOB *a = lhs->octet_value; |
919 | 36.4k | const DATA_BLOB *b = rhs->octet_value; |
920 | 36.4k | return data_blob_cmp(a, b); |
921 | 27.4k | } |
922 | 0 | default: |
923 | 0 | ctx->failed = true; |
924 | 0 | break; |
925 | 508k | } |
926 | 0 | return -1; |
927 | 508k | } |
928 | | |
929 | | |
930 | | NTSTATUS claim_v1_check_and_sort(TALLOC_CTX *mem_ctx, |
931 | | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim, |
932 | | bool case_sensitive) |
933 | 29.2k | { |
934 | 29.2k | bool ok; |
935 | 29.2k | uint32_t i; |
936 | 29.2k | struct claim_sort_context sort_ctx = { |
937 | 29.2k | .failed = false, |
938 | 29.2k | .value_type = claim->value_type, |
939 | 29.2k | .case_sensitive = case_sensitive |
940 | 29.2k | }; |
941 | | |
942 | | /* |
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. |
945 | | */ |
946 | 216k | for (i = 0; i < claim->value_count; i++) { |
947 | 190k | if (claim->values[i].int_value == NULL) { |
948 | 3.08k | return NT_STATUS_INVALID_PARAMETER; |
949 | 3.08k | } |
950 | 190k | } |
951 | | |
952 | 26.1k | if (claim->value_type == CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN) { |
953 | 2.62k | NTSTATUS status = claim_v1_check_and_sort_boolean(mem_ctx, claim); |
954 | 2.62k | if (NT_STATUS_IS_OK(status)) { |
955 | 260 | claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED; |
956 | 260 | } |
957 | 2.62k | return status; |
958 | 2.62k | } |
959 | | |
960 | 23.4k | ok = stable_sort_talloc_r(mem_ctx, |
961 | 23.4k | claim->values, |
962 | 23.4k | claim->value_count, |
963 | 23.4k | sizeof(union claim_values), |
964 | 23.4k | (samba_compare_with_context_fn_t)claim_sort_cmp, |
965 | 23.4k | &sort_ctx); |
966 | 23.4k | if (!ok) { |
967 | 0 | return NT_STATUS_NO_MEMORY; |
968 | 0 | } |
969 | | |
970 | 23.4k | if (sort_ctx.failed) { |
971 | | /* this failure probably means a bad SID string */ |
972 | 7.53k | DBG_WARNING("claim sort of %"PRIu32" members, type %"PRIu16" failed\n", |
973 | 7.53k | claim->value_count, |
974 | 7.53k | claim->value_type); |
975 | 7.53k | return NT_STATUS_INVALID_PARAMETER; |
976 | 7.53k | } |
977 | | |
978 | 39.6k | for (i = 1; i < claim->value_count; i++) { |
979 | 28.6k | int cmp = claim_sort_cmp(&claim->values[i - 1], |
980 | 28.6k | &claim->values[i], |
981 | 28.6k | &sort_ctx); |
982 | 28.6k | if (cmp == 0) { |
983 | 4.98k | DBG_WARNING("duplicate values in claim\n"); |
984 | 4.98k | return NT_STATUS_INVALID_PARAMETER; |
985 | 4.98k | } |
986 | 23.6k | if (cmp > 0) { |
987 | 0 | DBG_ERR("claim sort failed!\n"); |
988 | 0 | return NT_STATUS_INVALID_PARAMETER; |
989 | 0 | } |
990 | 23.6k | } |
991 | 10.9k | if (case_sensitive) { |
992 | 4.42k | claim->flags |= CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE; |
993 | 4.42k | } |
994 | 10.9k | claim->flags |= CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED; |
995 | 10.9k | return NT_STATUS_OK; |
996 | 15.9k | } |
997 | | |
998 | | |
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) |
1003 | 0 | { |
1004 | 0 | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claims = NULL; |
1005 | 0 | uint32_t n_claims = 0; |
1006 | 0 | uint32_t expected_n_claims = 0; |
1007 | 0 | uint32_t i; |
1008 | 0 | NTSTATUS status; |
1009 | |
|
1010 | 0 | if (out_claims == NULL) { |
1011 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1012 | 0 | } |
1013 | 0 | if (out_n_claims == NULL) { |
1014 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1015 | 0 | } |
1016 | | |
1017 | 0 | *out_claims = NULL; |
1018 | 0 | *out_n_claims = 0; |
1019 | |
|
1020 | 0 | if (claims_set == NULL) { |
1021 | 0 | return NT_STATUS_OK; |
1022 | 0 | } |
1023 | | |
1024 | | /* |
1025 | | * The outgoing number of claims is (at most) the sum of the |
1026 | | * claims_counts of each claims_array. |
1027 | | */ |
1028 | 0 | for (i = 0; i < claims_set->claims_array_count; ++i) { |
1029 | 0 | uint32_t count = claims_set->claims_arrays[i].claims_count; |
1030 | 0 | expected_n_claims += count; |
1031 | 0 | if (expected_n_claims < count) { |
1032 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1033 | 0 | } |
1034 | 0 | } |
1035 | | |
1036 | 0 | claims = talloc_array(mem_ctx, |
1037 | 0 | struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, |
1038 | 0 | expected_n_claims); |
1039 | 0 | if (claims == NULL) { |
1040 | 0 | return NT_STATUS_NO_MEMORY; |
1041 | 0 | } |
1042 | | |
1043 | 0 | for (i = 0; i < claims_set->claims_array_count; ++i) { |
1044 | 0 | const struct CLAIMS_ARRAY *claims_array = &claims_set->claims_arrays[i]; |
1045 | 0 | uint32_t j; |
1046 | |
|
1047 | 0 | switch (claims_array->claims_source_type) { |
1048 | 0 | case CLAIMS_SOURCE_TYPE_AD: |
1049 | 0 | case CLAIMS_SOURCE_TYPE_CERTIFICATE: |
1050 | 0 | break; |
1051 | 0 | default: |
1052 | | /* Ignore any claims of a type we don’t recognize. */ |
1053 | 0 | continue; |
1054 | 0 | } |
1055 | | |
1056 | 0 | for (j = 0; j < claims_array->claims_count; ++j) { |
1057 | 0 | const struct CLAIM_ENTRY *claim_entry = &claims_array->claim_entries[j]; |
1058 | 0 | const char *name = NULL; |
1059 | 0 | union claim_values *claim_values = NULL; |
1060 | 0 | uint32_t n_values; |
1061 | 0 | enum security_claim_value_type value_type; |
1062 | |
|
1063 | 0 | switch (claim_entry->type) { |
1064 | 0 | case CLAIM_TYPE_INT64: |
1065 | 0 | { |
1066 | 0 | const struct CLAIM_INT64 *values = &claim_entry->values.claim_int64; |
1067 | 0 | uint32_t k; |
1068 | 0 | int64_t *claim_values_int64 = NULL; |
1069 | |
|
1070 | 0 | n_values = values->value_count; |
1071 | 0 | value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64; |
1072 | |
|
1073 | 0 | claim_values = talloc_array(claims, |
1074 | 0 | union claim_values, |
1075 | 0 | n_values); |
1076 | 0 | if (claim_values == NULL) { |
1077 | 0 | talloc_free(claims); |
1078 | 0 | return NT_STATUS_NO_MEMORY; |
1079 | 0 | } |
1080 | 0 | claim_values_int64 = talloc_array(claims, |
1081 | 0 | int64_t, |
1082 | 0 | n_values); |
1083 | 0 | if (claim_values_int64 == NULL) { |
1084 | 0 | talloc_free(claims); |
1085 | 0 | return NT_STATUS_NO_MEMORY; |
1086 | 0 | } |
1087 | | |
1088 | 0 | for (k = 0; k < n_values; ++k) { |
1089 | 0 | claim_values_int64[k] = values->values[k]; |
1090 | 0 | claim_values[k].int_value = &claim_values_int64[k]; |
1091 | 0 | } |
1092 | |
|
1093 | 0 | break; |
1094 | 0 | } |
1095 | 0 | case CLAIM_TYPE_UINT64: |
1096 | 0 | case CLAIM_TYPE_BOOLEAN: |
1097 | 0 | { |
1098 | 0 | const struct CLAIM_UINT64 *values = &claim_entry->values.claim_uint64; |
1099 | 0 | uint32_t k; |
1100 | 0 | uint64_t *claim_values_uint64 = NULL; |
1101 | |
|
1102 | 0 | n_values = values->value_count; |
1103 | 0 | value_type = (claim_entry->type == CLAIM_TYPE_UINT64) |
1104 | 0 | ? CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64 |
1105 | 0 | : CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN; |
1106 | |
|
1107 | 0 | claim_values = talloc_array(claims, |
1108 | 0 | union claim_values, |
1109 | 0 | n_values); |
1110 | 0 | if (claim_values == NULL) { |
1111 | 0 | talloc_free(claims); |
1112 | 0 | return NT_STATUS_NO_MEMORY; |
1113 | 0 | } |
1114 | | |
1115 | 0 | claim_values_uint64 = talloc_array(claims, |
1116 | 0 | uint64_t, |
1117 | 0 | n_values); |
1118 | 0 | if (claim_values_uint64 == NULL) { |
1119 | 0 | talloc_free(claims); |
1120 | 0 | return NT_STATUS_NO_MEMORY; |
1121 | 0 | } |
1122 | | |
1123 | 0 | for (k = 0; k < n_values; ++k) { |
1124 | 0 | claim_values_uint64[k] = values->values[k]; |
1125 | 0 | claim_values[k].uint_value = &claim_values_uint64[k]; |
1126 | 0 | } |
1127 | |
|
1128 | 0 | break; |
1129 | 0 | } |
1130 | 0 | case CLAIM_TYPE_STRING: |
1131 | 0 | { |
1132 | 0 | const struct CLAIM_STRING *values = &claim_entry->values.claim_string; |
1133 | 0 | uint32_t k, m; |
1134 | 0 | bool seen_empty = false; |
1135 | 0 | n_values = values->value_count; |
1136 | 0 | value_type = CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING; |
1137 | |
|
1138 | 0 | claim_values = talloc_array(claims, |
1139 | 0 | union claim_values, |
1140 | 0 | n_values); |
1141 | 0 | if (claim_values == NULL) { |
1142 | 0 | talloc_free(claims); |
1143 | 0 | return NT_STATUS_NO_MEMORY; |
1144 | 0 | } |
1145 | | |
1146 | 0 | m = 0; |
1147 | 0 | for (k = 0; k < n_values; ++k) { |
1148 | 0 | const char *string_value = NULL; |
1149 | |
|
1150 | 0 | if (values->values[k] != NULL) { |
1151 | 0 | string_value = talloc_strdup(claim_values, values->values[k]); |
1152 | 0 | if (string_value == NULL) { |
1153 | 0 | talloc_free(claims); |
1154 | 0 | return NT_STATUS_NO_MEMORY; |
1155 | 0 | } |
1156 | 0 | claim_values[m].string_value = string_value; |
1157 | 0 | m++; |
1158 | 0 | } else { |
1159 | | /* |
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). |
1167 | | */ |
1168 | 0 | if (seen_empty) { |
1169 | 0 | talloc_free(claims); |
1170 | 0 | return NT_STATUS_INVALID_PARAMETER; |
1171 | 0 | } |
1172 | 0 | seen_empty = true; |
1173 | 0 | } |
1174 | 0 | } |
1175 | 0 | n_values = m; |
1176 | 0 | break; |
1177 | 0 | } |
1178 | 0 | default: |
1179 | | /* |
1180 | | * Other claim types are unsupported — just skip |
1181 | | * them. |
1182 | | */ |
1183 | 0 | continue; |
1184 | 0 | } |
1185 | | |
1186 | 0 | if (claim_entry->id != NULL) { |
1187 | 0 | name = talloc_strdup(claims, claim_entry->id); |
1188 | 0 | if (name == NULL) { |
1189 | 0 | talloc_free(claims); |
1190 | 0 | return NT_STATUS_NO_MEMORY; |
1191 | 0 | } |
1192 | 0 | } |
1193 | | |
1194 | 0 | claims[n_claims] = (struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1) { |
1195 | 0 | .name = name, |
1196 | 0 | .value_type = value_type, |
1197 | 0 | .flags = 0, |
1198 | 0 | .value_count = n_values, |
1199 | 0 | .values = claim_values, |
1200 | 0 | }; |
1201 | |
|
1202 | 0 | status = claim_v1_check_and_sort(claims, &claims[n_claims], |
1203 | 0 | false); |
1204 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1205 | 0 | talloc_free(claims); |
1206 | 0 | DBG_WARNING("claim sort and uniqueness test failed with %s\n", |
1207 | 0 | nt_errstr(status)); |
1208 | 0 | return status; |
1209 | 0 | } |
1210 | 0 | n_claims++; |
1211 | 0 | } |
1212 | 0 | } |
1213 | 0 | *out_claims = claims; |
1214 | 0 | *out_n_claims = n_claims; |
1215 | |
|
1216 | 0 | return NT_STATUS_OK; |
1217 | 0 | } |