Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Samba Active Directory claims utility functions
4 :
5 : Copyright (C) Catalyst.Net Ltd 2023
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "lib/replace/replace.h"
22 : #include "lib/util/debug.h"
23 : #include "lib/util/samba_util.h"
24 : #include "source4/kdc/ad_claims.h"
25 : #include "source4/kdc/authn_policy_util.h"
26 : #include "ldb_module.h"
27 : #include "dsdb/samdb/samdb.h"
28 : #include "dsdb/samdb/ldb_modules/util.h"
29 : #include "librpc/gen_ndr/claims.h"
30 : #include "librpc/gen_ndr/ndr_claims.h"
31 : #include "librpc/gen_ndr/ndr_krb5pac.h"
32 : #include "lib/util/binsearch.h"
33 : #include "auth/session.h"
34 :
35 : #undef strcasecmp
36 :
37 31125 : bool ad_claims_are_issued(struct ldb_context *samdb)
38 : {
39 : /*
40 : * Claims aren’t issued by Samba unless the DC is at
41 : * FL2012. This is to match Windows, which will offer
42 : * this feature as soon as the DC is upgraded.
43 : */
44 31125 : const int functional_level = dsdb_dc_functional_level(samdb);
45 31125 : return functional_level >= DS_DOMAIN_FUNCTION_2012;
46 : }
47 :
48 5809 : static int acl_attr_cmp_fn(const char *a, const char * const *b)
49 : {
50 5809 : return ldb_attr_cmp(a, *b);
51 : }
52 :
53 : /*
54 : * Add a single attribute to a list of attributes if it is not already
55 : * present. The list is maintained in case-insensitive sorted order.
56 : */
57 3654 : static int add_attr_unique(TALLOC_CTX *mem_ctx,
58 : const char **attrs,
59 : unsigned *ad_claim_attrs_count,
60 : const char *attr)
61 : {
62 3654 : const unsigned count = *ad_claim_attrs_count;
63 3654 : const char * const *exact = NULL;
64 3654 : const char * const *next = NULL;
65 :
66 9463 : BINARY_ARRAY_SEARCH_GTE(attrs,
67 : count,
68 : attr,
69 : acl_attr_cmp_fn,
70 : exact,
71 : next);
72 3654 : if (exact != NULL) {
73 : /* The attribute is already present; there's nothing to do. */
74 2279 : return LDB_SUCCESS;
75 : }
76 :
77 : /* Make sure we don't overflow the array. */
78 1375 : SMB_ASSERT(count < talloc_array_length(attrs));
79 1375 : *ad_claim_attrs_count = count + 1;
80 :
81 1375 : if (next == NULL) {
82 : /* Just add the new element on the end. */
83 956 : attrs[count] = attr;
84 : } else {
85 : /* Shift all following elements over to make room. */
86 419 : size_t next_idx = next - attrs;
87 419 : size_t bytes_to_move = (count - next_idx) * sizeof (attrs[0]);
88 419 : memmove(&attrs[next_idx + 1],
89 419 : &attrs[next_idx],
90 : bytes_to_move);
91 :
92 419 : attrs[next_idx] = attr;
93 : }
94 :
95 1375 : return LDB_SUCCESS;
96 : }
97 :
98 : /*
99 : * Return true if a data_blob, interpreted as a string, is equal to another
100 : * string. This is more efficient than strcmp(), particularly when comparing
101 : * against a string constant. This assumes the data_blob's length does not
102 : * include the zero-terminator.
103 : */
104 10587 : static inline bool data_blob_equals_str(const DATA_BLOB val, const char *str)
105 : {
106 10587 : size_t len = strlen(str);
107 10587 : if (val.length != len) {
108 4329 : return false;
109 : }
110 :
111 6258 : return memcmp(val.data, str, len) == 0;
112 : }
113 :
114 227 : static int fill_claim_int64(TALLOC_CTX *mem_ctx,
115 : struct ldb_context *ldb,
116 : const struct ldb_message_element *principal_attribute,
117 : const struct ldb_val name,
118 : struct CLAIM_INT64 *claim)
119 : {
120 0 : uint32_t i;
121 :
122 227 : claim->value_count = 0;
123 227 : claim->values = talloc_array(mem_ctx,
124 : int64_t,
125 : principal_attribute->num_values);
126 227 : if (claim->values == NULL) {
127 0 : return ldb_oom(ldb);
128 : }
129 :
130 550 : for (i = 0; i < principal_attribute->num_values; ++i) {
131 323 : const struct ldb_val *value = &principal_attribute->values[i];
132 323 : int ret = ldb_val_as_int64(value, &claim->values[i]);
133 323 : if (ret) {
134 0 : char buf[1024];
135 0 : const char *reason = NULL;
136 0 : int err = strerror_r(ret, buf, sizeof(buf));
137 0 : if (err == 0) {
138 0 : reason = buf;
139 : } else {
140 0 : reason = "Unknown error";
141 : }
142 0 : DBG_WARNING("Failed to interpret value %s as INT64 "
143 : "while creating claim %s for attribute %s (%s); "
144 : "skipping value\n",
145 : (value->data != NULL) ? (const char *)value->data : "<unknown>",
146 : name.data, principal_attribute->name,
147 : reason);
148 0 : continue;
149 : }
150 :
151 323 : ++claim->value_count;
152 : }
153 :
154 : /* Shrink the array to fit. */
155 227 : claim->values = talloc_realloc(mem_ctx,
156 : claim->values,
157 : int64_t,
158 : claim->value_count);
159 227 : if (claim->value_count && claim->values == NULL) {
160 0 : return ldb_oom(ldb);
161 : }
162 :
163 227 : return LDB_SUCCESS;
164 : }
165 :
166 0 : static int fill_claim_uint64(TALLOC_CTX *mem_ctx,
167 : struct ldb_context *ldb,
168 : const struct ldb_message_element *principal_attribute,
169 : const struct ldb_val name,
170 : struct CLAIM_UINT64 *claim)
171 : {
172 0 : uint32_t i;
173 :
174 0 : claim->value_count = 0;
175 0 : claim->values = talloc_array(mem_ctx,
176 : uint64_t,
177 : principal_attribute->num_values);
178 0 : if (claim->values == NULL) {
179 0 : return ldb_oom(ldb);
180 : }
181 :
182 0 : for (i = 0; i < principal_attribute->num_values; ++i) {
183 0 : const struct ldb_val *value = &principal_attribute->values[i];
184 0 : int ret = ldb_val_as_uint64(value, &claim->values[i]);
185 0 : if (ret) {
186 0 : char buf[1024];
187 0 : const char *reason = NULL;
188 0 : int err = strerror_r(ret, buf, sizeof(buf));
189 0 : if (err == 0) {
190 0 : reason = buf;
191 : } else {
192 0 : reason = "Unknown error";
193 : }
194 0 : DBG_WARNING("Failed to interpret value %s as UINT64 "
195 : "while creating claim %s for attribute %s (%s); "
196 : "skipping value\n",
197 : (value->data != NULL) ? (const char *)value->data : "<unknown>",
198 : name.data, principal_attribute->name,
199 : reason);
200 0 : continue;
201 : }
202 :
203 0 : ++claim->value_count;
204 : }
205 :
206 : /* Shrink the array to fit. */
207 0 : claim->values = talloc_realloc(mem_ctx,
208 : claim->values,
209 : uint64_t,
210 : claim->value_count);
211 0 : if (claim->value_count && claim->values == NULL) {
212 0 : return ldb_oom(ldb);
213 : }
214 :
215 0 : return LDB_SUCCESS;
216 : }
217 :
218 96 : static int fill_claim_uint64_oid_syntax(TALLOC_CTX *mem_ctx,
219 : struct ldb_context *ldb,
220 : const struct dsdb_schema *schema,
221 : const struct ldb_message_element *principal_attribute,
222 : const struct ldb_val name,
223 : struct CLAIM_UINT64 *claim)
224 : {
225 0 : uint32_t i;
226 :
227 96 : claim->value_count = 0;
228 96 : claim->values = talloc_array(mem_ctx,
229 : uint64_t,
230 : principal_attribute->num_values);
231 96 : if (claim->values == NULL) {
232 0 : return ldb_oom(ldb);
233 : }
234 :
235 517 : for (i = 0; i < principal_attribute->num_values; ++i) {
236 421 : const struct dsdb_class *class_val = NULL;
237 :
238 : /*
239 : * OID values for objectClass
240 : * are presented in reverse
241 : * order.
242 : */
243 421 : const struct ldb_val *display_name = &principal_attribute->values[
244 421 : principal_attribute->num_values - 1 - i];
245 :
246 421 : class_val = dsdb_class_by_lDAPDisplayName_ldb_val(schema, display_name);
247 421 : if (class_val == NULL) {
248 0 : DBG_WARNING("Failed to look up OID for value %s "
249 : "while creating claim %s for attribute %s; "
250 : "skipping value\n",
251 : (display_name->data != NULL) ? (const char *)display_name->data : "<unknown>",
252 : name.data, principal_attribute->name);
253 0 : continue;
254 : }
255 :
256 421 : claim->values[i] = class_val->governsID_id;
257 421 : ++claim->value_count;
258 : }
259 :
260 : /* Shrink the array to fit. */
261 96 : claim->values = talloc_realloc(mem_ctx,
262 : claim->values,
263 : uint64_t,
264 : claim->value_count);
265 96 : if (claim->value_count && claim->values == NULL) {
266 0 : return ldb_oom(ldb);
267 : }
268 :
269 96 : return LDB_SUCCESS;
270 : }
271 :
272 29 : static int fill_claim_boolean(TALLOC_CTX *mem_ctx,
273 : struct ldb_context *ldb,
274 : const struct ldb_message_element *principal_attribute,
275 : const struct ldb_val name,
276 : struct CLAIM_UINT64 *claim)
277 : {
278 0 : uint32_t i;
279 :
280 29 : claim->value_count = 0;
281 29 : claim->values = talloc_array(mem_ctx,
282 : uint64_t,
283 : principal_attribute->num_values);
284 29 : if (claim->values == NULL) {
285 0 : return ldb_oom(ldb);
286 : }
287 :
288 58 : for (i = 0; i < principal_attribute->num_values; ++i) {
289 29 : const struct ldb_val *value = &principal_attribute->values[i];
290 29 : bool val = false;
291 29 : int ret = ldb_val_as_bool(value, &val);
292 29 : if (ret) {
293 0 : char buf[1024];
294 0 : const char *reason = NULL;
295 0 : int err = strerror_r(ret, buf, sizeof(buf));
296 0 : if (err == 0) {
297 0 : reason = buf;
298 : } else {
299 0 : reason = "Unknown error";
300 : }
301 0 : DBG_WARNING("Failed to interpret value %s as BOOL "
302 : "while creating claim %s for attribute %s (%s); "
303 : "skipping value\n",
304 : (value->data != NULL) ? (const char *)value->data : "<unknown>",
305 : name.data, principal_attribute->name,
306 : reason);
307 0 : continue;
308 : }
309 :
310 29 : claim->values[i] = val;
311 29 : ++claim->value_count;
312 : }
313 :
314 : /* Shrink the array to fit. */
315 29 : claim->values = talloc_realloc(mem_ctx,
316 : claim->values,
317 : uint64_t,
318 : claim->value_count);
319 29 : if (claim->value_count && claim->values == NULL) {
320 0 : return ldb_oom(ldb);
321 : }
322 :
323 29 : return LDB_SUCCESS;
324 : }
325 :
326 683 : static int fill_claim_string(TALLOC_CTX *mem_ctx,
327 : struct ldb_context *ldb,
328 : const struct ldb_message_element *principal_attribute,
329 : struct CLAIM_STRING *claim)
330 : {
331 0 : uint32_t i;
332 :
333 683 : claim->value_count = 0;
334 683 : claim->values = talloc_array(mem_ctx,
335 : const char *,
336 : principal_attribute->num_values);
337 683 : if (claim->values == NULL) {
338 0 : return ldb_oom(ldb);
339 : }
340 :
341 1513 : for (i = 0; i < principal_attribute->num_values; ++i) {
342 830 : const char *val = NULL;
343 830 : const struct ldb_val *v = &principal_attribute->values[i];
344 :
345 830 : if (v == NULL || v->data == NULL) {
346 0 : continue;
347 : }
348 :
349 830 : val = talloc_strndup(claim->values,
350 830 : (const char *)v->data,
351 830 : v->length);
352 830 : if (val == NULL) {
353 0 : return ldb_oom(ldb);
354 : }
355 :
356 830 : claim->values[i] = val;
357 830 : ++claim->value_count;
358 : }
359 :
360 : /* Shrink the array to fit. */
361 683 : claim->values = talloc_realloc(mem_ctx,
362 : claim->values,
363 : const char *,
364 : claim->value_count);
365 683 : if (claim->value_count && claim->values == NULL) {
366 0 : return ldb_oom(ldb);
367 : }
368 :
369 683 : return LDB_SUCCESS;
370 : }
371 :
372 7 : static int fill_claim_string_sec_desc_syntax(TALLOC_CTX *mem_ctx,
373 : struct ldb_context *ldb,
374 : const struct ldb_message_element *principal_attribute,
375 : struct CLAIM_STRING *claim)
376 : {
377 7 : TALLOC_CTX *tmp_ctx = NULL;
378 7 : const struct dom_sid *domain_sid = NULL;
379 0 : uint32_t i;
380 :
381 7 : claim->value_count = 0;
382 7 : claim->values = talloc_array(mem_ctx,
383 : const char *,
384 : principal_attribute->num_values);
385 7 : if (claim->values == NULL) {
386 0 : return ldb_oom(ldb);
387 : }
388 :
389 7 : domain_sid = samdb_domain_sid(ldb);
390 7 : if (domain_sid == NULL) {
391 0 : return ldb_oom(ldb);
392 : }
393 :
394 7 : tmp_ctx = talloc_new(mem_ctx);
395 7 : if (tmp_ctx == NULL) {
396 0 : return ldb_oom(ldb);
397 : }
398 :
399 14 : for (i = 0; i < principal_attribute->num_values; ++i) {
400 7 : const struct ldb_val *v = &principal_attribute->values[i];
401 :
402 0 : enum ndr_err_code ndr_err;
403 7 : struct security_descriptor desc = {};
404 7 : const char *sddl = NULL;
405 :
406 7 : if (v == NULL || v->data == NULL) {
407 0 : continue;
408 : }
409 :
410 7 : ndr_err = ndr_pull_struct_blob(v,
411 : tmp_ctx,
412 : &desc,
413 : (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
414 7 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
415 0 : NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
416 0 : DBG_ERR("security_descriptor pull failed: %s\n",
417 : nt_errstr(nt_status));
418 0 : talloc_free(tmp_ctx);
419 0 : return ldb_operr(ldb);
420 : }
421 :
422 7 : sddl = sddl_encode(mem_ctx,
423 : &desc,
424 : domain_sid);
425 7 : if (sddl == NULL) {
426 0 : talloc_free(tmp_ctx);
427 0 : return ldb_oom(ldb);
428 : }
429 :
430 7 : claim->values[i] = sddl;
431 7 : ++claim->value_count;
432 : }
433 :
434 7 : talloc_free(tmp_ctx);
435 :
436 : /* Shrink the array to fit. */
437 7 : claim->values = talloc_realloc(mem_ctx,
438 : claim->values,
439 : const char *,
440 : claim->value_count);
441 7 : if (claim->value_count && claim->values == NULL) {
442 0 : return ldb_oom(ldb);
443 : }
444 :
445 7 : return LDB_SUCCESS;
446 : }
447 :
448 1042 : static int fill_claim_entry(TALLOC_CTX *mem_ctx,
449 : struct ldb_context *ldb,
450 : const struct dsdb_schema *schema,
451 : const struct ldb_message_element *principal_attribute,
452 : const struct ldb_val name,
453 : const DATA_BLOB syntax,
454 : enum CLAIM_TYPE claim_type,
455 : struct CLAIM_ENTRY *claim_entry)
456 : {
457 :
458 2084 : claim_entry->id = talloc_strndup(mem_ctx,
459 1042 : (const char *)name.data,
460 1042 : name.length);
461 1042 : if (claim_entry->id == NULL) {
462 0 : return ldb_oom(ldb);
463 : }
464 :
465 1042 : claim_entry->type = claim_type;
466 :
467 1042 : switch (claim_type) {
468 227 : case CLAIM_TYPE_INT64:
469 227 : return fill_claim_int64(mem_ctx,
470 : ldb,
471 : principal_attribute,
472 : name,
473 : &claim_entry->values.claim_int64);
474 96 : case CLAIM_TYPE_UINT64:
475 96 : if (syntax.data != NULL && data_blob_equals_str(syntax, "2.5.5.2")) {
476 96 : return fill_claim_uint64_oid_syntax(mem_ctx,
477 : ldb,
478 : schema,
479 : principal_attribute,
480 : name,
481 : &claim_entry->values.claim_uint64);
482 : } else {
483 0 : return fill_claim_uint64(mem_ctx,
484 : ldb,
485 : principal_attribute,
486 : name,
487 : &claim_entry->values.claim_uint64);
488 : }
489 29 : case CLAIM_TYPE_BOOLEAN:
490 29 : return fill_claim_boolean(mem_ctx,
491 : ldb,
492 : principal_attribute,
493 : name,
494 : &claim_entry->values.claim_boolean);
495 690 : case CLAIM_TYPE_STRING:
496 : default:
497 690 : if (syntax.data != NULL && data_blob_equals_str(syntax, "2.5.5.15")) {
498 7 : return fill_claim_string_sec_desc_syntax(mem_ctx,
499 : ldb,
500 : principal_attribute,
501 : &claim_entry->values.claim_string);
502 : } else {
503 683 : return fill_claim_string(mem_ctx,
504 : ldb,
505 : principal_attribute,
506 : &claim_entry->values.claim_string);
507 : }
508 : }
509 : }
510 :
511 : /*
512 : * Determine whether a claim applies to the most specific objectClass of the
513 : * principal.
514 : */
515 25804 : static int claim_applies_to_class(TALLOC_CTX *mem_ctx,
516 : struct ldb_context *ldb,
517 : const struct dsdb_schema *schema,
518 : const struct ldb_message *claim_msg,
519 : const uint32_t principal_class_id,
520 : bool *applies)
521 : {
522 25804 : struct ldb_message_element *applies_to_class = NULL;
523 1170 : unsigned i;
524 :
525 25804 : applies_to_class = ldb_msg_find_element(claim_msg,
526 : "msDS-ClaimTypeAppliesToClass");
527 25804 : if (applies_to_class == NULL) {
528 117 : *applies = false;
529 117 : return LDB_SUCCESS;
530 : }
531 :
532 59324 : for (i = 0; i < applies_to_class->num_values; ++i) {
533 56670 : struct ldb_dn *class_dn = NULL;
534 56670 : const struct dsdb_class *class_val = NULL;
535 56670 : const struct ldb_val *class_rdn = NULL;
536 :
537 58886 : class_dn = ldb_dn_from_ldb_val(mem_ctx,
538 : ldb,
539 56670 : &applies_to_class->values[i]);
540 56670 : if (class_dn == NULL) {
541 0 : return ldb_oom(ldb);
542 : }
543 :
544 56670 : class_rdn = ldb_dn_get_rdn_val(class_dn);
545 56670 : if (class_rdn == NULL) {
546 0 : TALLOC_FREE(class_dn);
547 0 : continue;
548 : }
549 :
550 56670 : class_val = dsdb_class_by_cn_ldb_val(schema, class_rdn);
551 56670 : TALLOC_FREE(class_dn);
552 56670 : if (class_val == NULL) {
553 0 : continue;
554 : }
555 :
556 56670 : if (class_val->governsID_id == principal_class_id) {
557 23033 : *applies = true;
558 23033 : return LDB_SUCCESS;
559 : }
560 : }
561 :
562 2654 : *applies = false;
563 2654 : return LDB_SUCCESS;
564 : }
565 :
566 : struct assigned_silo {
567 : const char *name;
568 : bool is_initialised;
569 : bool is_assigned;
570 : };
571 :
572 16230 : static struct assigned_silo new_assigned_silo(void)
573 : {
574 16230 : return (struct assigned_silo) {
575 : .name = NULL,
576 : .is_initialised = false,
577 : .is_assigned = false,
578 : };
579 : }
580 :
581 17277 : static bool silo_is_maybe_assigned(struct assigned_silo silo)
582 : {
583 16107 : return !silo.is_initialised || silo.is_assigned;
584 : }
585 :
586 17238 : static int get_assigned_silo(struct ldb_context *ldb,
587 : TALLOC_CTX *mem_ctx,
588 : const struct ldb_message *principal,
589 : struct assigned_silo *assigned_silo)
590 : {
591 17238 : TALLOC_CTX *tmp_ctx = NULL;
592 1170 : int ret;
593 :
594 17238 : const struct ldb_message *silo_msg = NULL;
595 1170 : static const char * const silo_attrs[] = {
596 : "msDS-AuthNPolicySiloEnforced",
597 : "msDS-AuthNPolicySiloMembers",
598 : "name",
599 : NULL
600 : };
601 :
602 17238 : bool is_silo_enforced = false;
603 17238 : const char *silo_name = NULL;
604 :
605 17238 : if (assigned_silo->is_initialised) {
606 0 : return LDB_SUCCESS;
607 : }
608 :
609 17238 : tmp_ctx = talloc_new(mem_ctx);
610 17238 : if (tmp_ctx == NULL) {
611 0 : return ldb_oom(ldb);
612 : }
613 :
614 17238 : if (!authn_policy_silos_and_policies_in_effect(ldb)) {
615 : /* No assigned silo. */
616 0 : assigned_silo->is_assigned = false;
617 0 : assigned_silo->is_initialised = true;
618 :
619 0 : talloc_free(tmp_ctx);
620 0 : return LDB_SUCCESS;
621 : }
622 :
623 : /* Check whether the user is assigned to an enforced silo. */
624 17238 : ret = authn_policy_get_assigned_silo(ldb,
625 : tmp_ctx,
626 : principal,
627 : silo_attrs,
628 : &silo_msg,
629 : &is_silo_enforced);
630 17238 : if (ret) {
631 0 : talloc_free(tmp_ctx);
632 0 : return ret;
633 : }
634 :
635 17238 : if (silo_msg == NULL || !is_silo_enforced) {
636 : /* No assigned silo. */
637 17216 : assigned_silo->is_assigned = false;
638 17216 : assigned_silo->is_initialised = true;
639 :
640 17216 : talloc_free(tmp_ctx);
641 17216 : return LDB_SUCCESS;
642 : }
643 :
644 : /* The user does belong to a silo, so return the name of the silo. */
645 22 : silo_name = ldb_msg_find_attr_as_string(silo_msg,
646 : "name",
647 : NULL);
648 22 : assigned_silo->name = talloc_steal(mem_ctx, silo_name);
649 22 : assigned_silo->is_assigned = true;
650 22 : assigned_silo->is_initialised = true;
651 :
652 22 : talloc_free(tmp_ctx);
653 22 : return LDB_SUCCESS;
654 : }
655 :
656 : static inline struct ldb_val talloc_steal_ldb_val(TALLOC_CTX *mem_ctx, struct ldb_val val)
657 : {
658 : val.data = talloc_steal(mem_ctx, val.data);
659 : return val;
660 : }
661 :
662 1042 : static uint32_t claim_get_value_count(const struct CLAIM_ENTRY *claim)
663 : {
664 1042 : switch (claim->type) {
665 227 : case CLAIM_TYPE_INT64:
666 227 : return claim->values.claim_int64.value_count;
667 96 : case CLAIM_TYPE_UINT64:
668 96 : return claim->values.claim_uint64.value_count;
669 690 : case CLAIM_TYPE_STRING:
670 690 : return claim->values.claim_string.value_count;
671 29 : case CLAIM_TYPE_BOOLEAN:
672 29 : return claim->values.claim_boolean.value_count;
673 : }
674 :
675 0 : smb_panic(__location__ ": unknown claim type");
676 : return 0;
677 : }
678 :
679 5083 : static bool is_schema_dn(struct ldb_dn *dn,
680 : struct ldb_dn *schema_dn)
681 : {
682 5083 : if (ldb_dn_get_comp_num(dn) != (ldb_dn_get_comp_num(schema_dn) + 1)) {
683 0 : return false;
684 : }
685 :
686 5083 : return ldb_dn_compare_base(schema_dn, dn) == 0;
687 : }
688 :
689 5083 : static bool is_valid_claim_attribute_syntax(const DATA_BLOB source_syntax,
690 : uint64_t claim_value_type)
691 : {
692 5083 : switch (claim_value_type) {
693 3518 : case CLAIM_TYPE_STRING:
694 3518 : if (data_blob_equals_str(source_syntax, "2.5.5.1")) {
695 155 : return true;
696 : }
697 3363 : if (data_blob_equals_str(source_syntax, "2.5.5.12")) {
698 2370 : return true;
699 : }
700 993 : if (data_blob_equals_str(source_syntax, "2.5.5.15")) {
701 33 : return true;
702 : }
703 960 : break;
704 244 : case CLAIM_TYPE_UINT64:
705 244 : if (data_blob_equals_str(source_syntax, "2.5.5.2")) {
706 96 : return true;
707 : }
708 148 : break;
709 761 : case CLAIM_TYPE_INT64:
710 761 : if (data_blob_equals_str(source_syntax, "2.5.5.9")) {
711 399 : return true;
712 : }
713 362 : if (data_blob_equals_str(source_syntax, "2.5.5.16")) {
714 176 : return true;
715 : }
716 186 : break;
717 560 : case CLAIM_TYPE_BOOLEAN:
718 : /* Note: MS-ADTS has a typo (2.2.5.8 instead of 2.5.5.8) */
719 560 : if (data_blob_equals_str(source_syntax, "2.5.5.8")) {
720 425 : return true;
721 : }
722 135 : break;
723 0 : default:
724 0 : break;
725 : }
726 :
727 1429 : return false;
728 : }
729 :
730 17400 : static int get_all_claims(struct ldb_context *ldb,
731 : TALLOC_CTX *mem_ctx,
732 : const struct ldb_message *principal,
733 : uint32_t principal_class_id,
734 : struct CLAIMS_SET **claims_set_out)
735 : {
736 17400 : TALLOC_CTX *tmp_ctx = NULL;
737 :
738 17400 : const struct dsdb_schema *schema = NULL;
739 :
740 17400 : struct ldb_dn *claim_config_container = NULL;
741 17400 : struct ldb_dn *claim_types_child = NULL;
742 17400 : struct ldb_dn *config_dn = ldb_get_config_basedn(ldb);
743 17400 : struct ldb_dn *schema_dn = ldb_get_schema_basedn(ldb);
744 1170 : bool ok;
745 1170 : int ret;
746 17400 : struct ldb_result *res = NULL;
747 1170 : static const char * const attrs[] = {
748 : "Enabled",
749 : "msDS-ClaimAttributeSource",
750 : "msDS-ClaimSource",
751 : "msDS-ClaimSourceType",
752 : "msDS-ClaimTypeAppliesToClass",
753 : "msDS-ClaimValueType",
754 : "name",
755 : NULL
756 : };
757 :
758 17400 : const char **ad_claim_attrs = NULL;
759 1170 : unsigned int ad_claim_attrs_count;
760 1170 : struct ad_claim_info {
761 : struct ldb_val name;
762 : DATA_BLOB syntax;
763 : const char *attribute;
764 : enum CLAIM_TYPE claim_type;
765 17400 : } *ad_claims = NULL;
766 1170 : unsigned ad_claims_count;
767 :
768 1170 : unsigned i;
769 :
770 : /* The structure which we'll use to build up the claims. */
771 17400 : struct CLAIMS_SET *claims_set = NULL;
772 :
773 17400 : struct CLAIMS_ARRAY *ad_sourced_constructed = NULL;
774 :
775 17400 : struct assigned_silo assigned_silo = new_assigned_silo();
776 :
777 17400 : *claims_set_out = NULL;
778 :
779 17400 : tmp_ctx = talloc_new(mem_ctx);
780 17400 : if (tmp_ctx == NULL) {
781 0 : return ldb_oom(ldb);
782 : }
783 :
784 17400 : claims_set = talloc_zero(tmp_ctx, struct CLAIMS_SET);
785 17400 : if (claims_set == NULL) {
786 0 : talloc_free(tmp_ctx);
787 0 : return ldb_oom(ldb);
788 : }
789 :
790 17400 : schema = dsdb_get_schema(ldb, tmp_ctx);
791 17400 : if (schema == NULL) {
792 0 : talloc_free(tmp_ctx);
793 0 : return ldb_operr(ldb);
794 : }
795 :
796 : /* Get the DN of the claims container. */
797 17400 : claim_config_container = ldb_dn_copy(tmp_ctx, config_dn);
798 17400 : if (claim_config_container == NULL) {
799 0 : talloc_free(tmp_ctx);
800 0 : return ldb_oom(ldb);
801 : }
802 :
803 17400 : claim_types_child = ldb_dn_new(tmp_ctx, ldb,
804 : "CN=Claim Types,CN=Claims Configuration,CN=Services");
805 17400 : if (claim_types_child == NULL) {
806 0 : talloc_free(tmp_ctx);
807 0 : return ldb_oom(ldb);
808 : }
809 :
810 17400 : ok = ldb_dn_add_child(claim_config_container, claim_types_child);
811 17400 : TALLOC_FREE(claim_types_child);
812 17400 : if (!ok) {
813 0 : talloc_free(tmp_ctx);
814 0 : return ldb_operr(ldb);
815 : }
816 :
817 : /* Search for the claims container's children. */
818 17400 : ret = ldb_search(ldb, tmp_ctx, &res,
819 : claim_config_container,
820 : LDB_SCOPE_ONELEVEL,
821 : attrs, NULL);
822 17400 : if (ret) {
823 162 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
824 162 : ret = LDB_SUCCESS;
825 : }
826 :
827 162 : talloc_free(tmp_ctx);
828 162 : return ret;
829 : }
830 :
831 : /*
832 : * Allocate enough space for all AD claim attributes, followed by space
833 : * for a NULL marker (so it can be passed as the attributes filter to an
834 : * LDB search).
835 : */
836 17238 : ad_claim_attrs = talloc_array(tmp_ctx,
837 : const char *,
838 : res->count + 1);
839 17238 : if (ad_claim_attrs == NULL) {
840 0 : talloc_free(tmp_ctx);
841 0 : return ldb_oom(ldb);
842 : }
843 17238 : ad_claims = talloc_array(tmp_ctx,
844 : struct ad_claim_info,
845 : res->count);
846 17238 : if (ad_claims == NULL) {
847 0 : talloc_free(tmp_ctx);
848 0 : return ldb_oom(ldb);
849 : }
850 17238 : ad_claims_count = ad_claim_attrs_count = 0;
851 :
852 : /* Loop through each child of the claims container. */
853 43042 : for (i = 0; i < res->count; ++i) {
854 25804 : bool claim_applies = false;
855 :
856 1170 : int enabled;
857 1170 : uint64_t claim_value_type;
858 :
859 25804 : const char *claim_source_type = NULL;
860 25804 : const struct ldb_val *claim_attribute_source = NULL;
861 25804 : const char *claim_source = NULL;
862 :
863 : /*
864 : * Does this claim apply to the most specific objectClass of the
865 : * principal?
866 : */
867 26974 : ret = claim_applies_to_class(tmp_ctx,
868 : ldb,
869 : schema,
870 25804 : res->msgs[i],
871 : principal_class_id,
872 : &claim_applies);
873 25804 : if (ret) {
874 0 : talloc_free(tmp_ctx);
875 0 : return ret;
876 : }
877 25804 : if (!claim_applies) {
878 : /* If the claim doesn't apply, skip it. */
879 22089 : continue;
880 : }
881 :
882 23033 : enabled = ldb_msg_find_attr_as_bool(res->msgs[i], "Enabled", 0);
883 23033 : if (!enabled) {
884 : /* If the claim isn't enabled, skip it. */
885 360 : continue;
886 : }
887 :
888 22673 : claim_value_type = ldb_msg_find_attr_as_uint64(res->msgs[i],
889 : "msDS-ClaimValueType",
890 : 0);
891 22673 : if (!claim_value_type) {
892 210 : continue;
893 : }
894 :
895 22463 : claim_source_type = ldb_msg_find_attr_as_string(res->msgs[i],
896 : "msDS-ClaimSourceType",
897 : "");
898 :
899 : /* Get the attribute used by the claim. */
900 22463 : claim_attribute_source = ldb_msg_find_ldb_val(res->msgs[i],
901 : "msDS-ClaimAttributeSource");
902 :
903 22463 : claim_source = ldb_msg_find_attr_as_string(res->msgs[i],
904 : "msDS-ClaimSource",
905 : NULL);
906 :
907 22463 : if (strcasecmp(claim_source_type, "AD") == 0) {
908 5186 : struct ldb_dn *claim_attribute_source_dn = NULL;
909 5186 : const struct ldb_val *claim_attribute_source_rdn = NULL;
910 5186 : const struct dsdb_attribute *claim_attribute_source_class = NULL;
911 :
912 0 : DATA_BLOB source_syntax;
913 5186 : const char *attribute = NULL;
914 5186 : const struct ldb_val *name = NULL;
915 :
916 5186 : if (claim_attribute_source == NULL) {
917 1532 : continue;
918 : }
919 :
920 5083 : claim_attribute_source_dn = ldb_val_as_dn(ldb,
921 : tmp_ctx,
922 : claim_attribute_source);
923 5083 : if (claim_attribute_source_dn == NULL) {
924 0 : talloc_free(tmp_ctx);
925 0 : return ldb_operr(ldb);
926 : }
927 :
928 5083 : if (!is_schema_dn(claim_attribute_source_dn, schema_dn)) {
929 : /* This DN doesn't belong to the schema. */
930 0 : continue;
931 : }
932 :
933 5083 : claim_attribute_source_rdn = ldb_dn_get_rdn_val(claim_attribute_source_dn);
934 5083 : if (claim_attribute_source_rdn == NULL) {
935 : /* No RDN, skip it. */
936 0 : continue;
937 : }
938 :
939 5083 : claim_attribute_source_class = dsdb_attribute_by_cn_ldb_val(schema,
940 : claim_attribute_source_rdn);
941 5083 : claim_attribute_source_rdn = NULL;
942 5083 : TALLOC_FREE(claim_attribute_source_dn);
943 5083 : if (claim_attribute_source_class == NULL) {
944 0 : continue;
945 : }
946 :
947 5083 : source_syntax = data_blob_string_const(claim_attribute_source_class->attributeSyntax_oid);
948 5083 : if (source_syntax.data == NULL) {
949 0 : continue;
950 : }
951 :
952 5083 : if (!is_valid_claim_attribute_syntax(source_syntax, claim_value_type)) {
953 1429 : continue;
954 : }
955 :
956 3654 : attribute = claim_attribute_source_class->lDAPDisplayName;
957 3654 : if (attribute == NULL) {
958 0 : continue;
959 : }
960 :
961 3654 : ret = add_attr_unique(tmp_ctx,
962 : ad_claim_attrs,
963 : &ad_claim_attrs_count,
964 : attribute);
965 3654 : if (ret) {
966 0 : talloc_free(tmp_ctx);
967 0 : return ret;
968 : }
969 :
970 3654 : name = ldb_msg_find_ldb_val(res->msgs[i], "name");
971 3654 : if (name == NULL) {
972 0 : name = &data_blob_null;
973 : }
974 :
975 3654 : ad_claims[ad_claims_count++] = (struct ad_claim_info) {
976 3654 : .name = *name,
977 : .syntax = source_syntax,
978 : .attribute = attribute,
979 : .claim_type = claim_value_type,
980 : };
981 17277 : } else if (silo_is_maybe_assigned(assigned_silo)
982 17277 : && strcasecmp(claim_source_type, "Constructed") == 0)
983 : {
984 17238 : const struct ldb_val *name = NULL;
985 17238 : struct CLAIM_STRING *claim = NULL;
986 17238 : struct CLAIM_ENTRY *claim_entry = NULL;
987 17238 : const char *claim_value = NULL;
988 :
989 17238 : if (claim_attribute_source != NULL) {
990 0 : continue;
991 : }
992 :
993 17238 : if (claim_source != NULL) {
994 0 : continue;
995 : }
996 :
997 17238 : name = ldb_msg_find_ldb_val(res->msgs[i], "name");
998 17238 : if (name == NULL || name->data == NULL) {
999 0 : continue;
1000 : }
1001 : /* Does the claim ID match exactly in case? */
1002 17238 : if (strcmp((const char *)name->data, "ad://ext/AuthenticationSilo") != 0) {
1003 0 : continue;
1004 : }
1005 :
1006 17238 : ret = get_assigned_silo(ldb, tmp_ctx, principal, &assigned_silo);
1007 17238 : if (ret) {
1008 0 : talloc_free(tmp_ctx);
1009 0 : return ret;
1010 : }
1011 17238 : if (!assigned_silo.is_assigned) {
1012 17216 : continue;
1013 : }
1014 :
1015 22 : if (ad_sourced_constructed == NULL) {
1016 22 : claims_set->claims_arrays = talloc_realloc(claims_set,
1017 : claims_set->claims_arrays,
1018 : struct CLAIMS_ARRAY,
1019 : claims_set->claims_array_count + 1);
1020 22 : if (claims_set->claims_arrays == NULL) {
1021 0 : talloc_free(tmp_ctx);
1022 0 : return ldb_oom(ldb);
1023 : }
1024 :
1025 22 : ad_sourced_constructed = &claims_set->claims_arrays[claims_set->claims_array_count++];
1026 22 : *ad_sourced_constructed = (struct CLAIMS_ARRAY) {
1027 : .claims_source_type = CLAIMS_SOURCE_TYPE_AD,
1028 : };
1029 : }
1030 :
1031 : /* Add the claim to the array. */
1032 22 : ad_sourced_constructed->claim_entries = talloc_realloc(
1033 : claims_set->claims_arrays,
1034 : ad_sourced_constructed->claim_entries,
1035 : struct CLAIM_ENTRY,
1036 : ad_sourced_constructed->claims_count + 1);
1037 22 : if (ad_sourced_constructed->claim_entries == NULL) {
1038 0 : talloc_free(tmp_ctx);
1039 0 : return ldb_oom(ldb);
1040 : }
1041 :
1042 22 : claim_entry = &ad_sourced_constructed->claim_entries[ad_sourced_constructed->claims_count++];
1043 :
1044 : /* Fill in the claim details and return the claim. */
1045 22 : claim_entry->id = "ad://ext/AuthenticationSilo";
1046 22 : claim_entry->type = CLAIM_TYPE_STRING;
1047 :
1048 22 : claim = &claim_entry->values.claim_string;
1049 :
1050 22 : claim->value_count = 1;
1051 22 : claim->values = talloc_array(ad_sourced_constructed->claim_entries,
1052 : const char *,
1053 : claim->value_count);
1054 22 : if (claim->values == NULL) {
1055 0 : talloc_free(tmp_ctx);
1056 0 : return ldb_oom(ldb);
1057 : }
1058 :
1059 22 : claim_value = talloc_strdup(claim->values, assigned_silo.name);
1060 22 : if (claim_value == NULL) {
1061 0 : talloc_free(tmp_ctx);
1062 0 : return ldb_oom(ldb);
1063 : }
1064 :
1065 22 : claim->values[0] = claim_value;
1066 : }
1067 : }
1068 :
1069 17238 : if (ad_claims_count) {
1070 505 : struct ldb_message *principal_msg = NULL;
1071 :
1072 : /* Shrink the arrays to remove any unused space. */
1073 505 : ad_claim_attrs = talloc_realloc(tmp_ctx,
1074 : ad_claim_attrs,
1075 : const char *,
1076 : ad_claim_attrs_count + 1);
1077 505 : if (ad_claim_attrs == NULL) {
1078 0 : talloc_free(tmp_ctx);
1079 0 : return ldb_oom(ldb);
1080 : }
1081 505 : ad_claim_attrs[ad_claim_attrs_count] = NULL;
1082 :
1083 505 : ad_claims = talloc_realloc(tmp_ctx,
1084 : ad_claims,
1085 : struct ad_claim_info,
1086 : ad_claims_count);
1087 505 : if (ad_claims == NULL) {
1088 0 : talloc_free(tmp_ctx);
1089 0 : return ldb_oom(ldb);
1090 : }
1091 :
1092 505 : ret = dsdb_search_one(ldb,
1093 : tmp_ctx,
1094 : &principal_msg,
1095 505 : principal->dn,
1096 : LDB_SCOPE_BASE,
1097 : ad_claim_attrs,
1098 : 0,
1099 : NULL);
1100 505 : if (ret != LDB_SUCCESS) {
1101 0 : const char *dn = ldb_dn_get_linearized(principal->dn);
1102 0 : DBG_ERR("Failed to find principal %s to construct claims\n",
1103 : dn != NULL ? dn : "<NULL>");
1104 0 : talloc_free(tmp_ctx);
1105 0 : return ret;
1106 : }
1107 :
1108 : /*
1109 : * Ensure that only the attrs we asked for end up in the results
1110 : * (it's fine if some are missing)
1111 : */
1112 505 : SMB_ASSERT(principal_msg->num_elements <= ad_claim_attrs_count);
1113 :
1114 4159 : for (i = 0; i < ad_claims_count; ++i) {
1115 3654 : const struct ldb_message_element *principal_attribute = NULL;
1116 3654 : struct CLAIM_ENTRY *claim_entry = NULL;
1117 3654 : uint32_t new_claims_array_count = claims_set->claims_array_count;
1118 :
1119 : /* Get the value of the claim attribute for the principal. */
1120 3654 : principal_attribute = ldb_msg_find_element(principal_msg,
1121 3654 : ad_claims[i].attribute);
1122 3654 : if (principal_attribute == NULL) {
1123 2612 : continue;
1124 : }
1125 :
1126 : /* Add the claim to the array. */
1127 :
1128 1042 : if (ad_sourced_constructed == NULL) {
1129 302 : claims_set->claims_arrays = talloc_realloc(claims_set,
1130 : claims_set->claims_arrays,
1131 : struct CLAIMS_ARRAY,
1132 : new_claims_array_count + 1);
1133 302 : if (claims_set->claims_arrays == NULL) {
1134 0 : talloc_free(tmp_ctx);
1135 0 : return ldb_oom(ldb);
1136 : }
1137 :
1138 302 : ad_sourced_constructed = &claims_set->claims_arrays[new_claims_array_count++];
1139 302 : *ad_sourced_constructed = (struct CLAIMS_ARRAY) {
1140 : .claims_source_type = CLAIMS_SOURCE_TYPE_AD,
1141 : };
1142 : }
1143 :
1144 1042 : ad_sourced_constructed->claim_entries = talloc_realloc(
1145 : claims_set->claims_arrays,
1146 : ad_sourced_constructed->claim_entries,
1147 : struct CLAIM_ENTRY,
1148 : ad_sourced_constructed->claims_count + 1);
1149 1042 : if (ad_sourced_constructed->claim_entries == NULL) {
1150 0 : talloc_free(tmp_ctx);
1151 0 : return ldb_oom(ldb);
1152 : }
1153 :
1154 1042 : claim_entry = &ad_sourced_constructed->claim_entries[
1155 1042 : ad_sourced_constructed->claims_count];
1156 :
1157 1042 : ret = fill_claim_entry(ad_sourced_constructed->claim_entries,
1158 : ldb,
1159 : schema,
1160 : principal_attribute,
1161 1042 : ad_claims[i].name,
1162 1042 : ad_claims[i].syntax,
1163 1042 : ad_claims[i].claim_type,
1164 : claim_entry);
1165 1042 : if (ret != LDB_SUCCESS) {
1166 0 : talloc_free(tmp_ctx);
1167 0 : return ret;
1168 : }
1169 :
1170 1042 : if (claim_get_value_count(claim_entry) > 0) {
1171 : /*
1172 : * If the claim contains values, add it to the
1173 : * array(s).
1174 : */
1175 1042 : ++ad_sourced_constructed->claims_count;
1176 1042 : claims_set->claims_array_count = new_claims_array_count;
1177 : }
1178 : }
1179 : }
1180 :
1181 17238 : if (claims_set->claims_array_count) {
1182 324 : *claims_set_out = talloc_steal(mem_ctx, claims_set);
1183 : }
1184 :
1185 17238 : talloc_free(tmp_ctx);
1186 17238 : return LDB_SUCCESS;
1187 : }
1188 :
1189 31125 : int get_claims_set_for_principal(struct ldb_context *ldb,
1190 : TALLOC_CTX *mem_ctx,
1191 : const struct ldb_message *principal,
1192 : struct CLAIMS_SET **claims_set_out)
1193 : {
1194 31125 : struct ldb_message_element *principal_class_el = NULL;
1195 31125 : struct dsdb_schema *schema = NULL;
1196 31125 : const struct dsdb_class *principal_class = NULL;
1197 :
1198 31125 : *claims_set_out = NULL;
1199 :
1200 31125 : if (!ad_claims_are_issued(ldb)) {
1201 13725 : return LDB_SUCCESS;
1202 : }
1203 :
1204 17400 : principal_class_el = ldb_msg_find_element(principal,
1205 : "objectClass");
1206 17400 : if (principal_class_el == NULL) {
1207 0 : return ldb_operr(ldb);
1208 : }
1209 :
1210 17400 : schema = dsdb_get_schema(ldb, mem_ctx);
1211 17400 : if (schema == NULL) {
1212 0 : return ldb_operr(ldb);
1213 : }
1214 :
1215 17400 : principal_class = dsdb_get_last_structural_class(schema, principal_class_el);
1216 17400 : if (principal_class == NULL) {
1217 0 : return ldb_operr(ldb);
1218 : }
1219 :
1220 17400 : return get_all_claims(ldb,
1221 : mem_ctx,
1222 : principal,
1223 17400 : principal_class->governsID_id,
1224 : claims_set_out);
1225 : }
|