Coverage Report

Created: 2026-05-24 06:47

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