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