Coverage Report

Created: 2026-01-16 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/ldb-samba/ldb_matching_rules.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   ldb database library - Extended match rules
5
6
   Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me>
7
   Copyright (C) Andrew Bartlett <abartlet@samba.org>
8
9
   This program is free software; you can redistribute it and/or modify
10
   it under the terms of the GNU General Public License as published by
11
   the Free Software Foundation; either version 3 of the License, or
12
   (at your option) any later version.
13
14
   This program is distributed in the hope that it will be useful,
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
   GNU General Public License for more details.
18
19
   You should have received a copy of the GNU General Public License
20
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
*/
22
23
#include "includes.h"
24
#include <ldb_module.h>
25
#include "dsdb/samdb/samdb.h"
26
#include "ldb_matching_rules.h"
27
#include "libcli/security/security.h"
28
#include "dsdb/common/util.h"
29
#include "librpc/gen_ndr/ndr_dnsp.h"
30
#include "lib/util/smb_strtox.h"
31
32
#undef strcasecmp
33
34
#undef DBGC_CLASS
35
0
#define DBGC_CLASS DBGC_LDB
36
37
static int ldb_eval_transitive_filter_helper(TALLOC_CTX *mem_ctx,
38
               struct ldb_context *ldb,
39
               const char *attr,
40
               const struct dsdb_dn *dn_to_match,
41
               const char *dn_oid,
42
               struct dsdb_dn *to_visit,
43
               struct dsdb_dn ***visited,
44
               unsigned int *visited_count,
45
               bool *matched)
46
0
{
47
0
  TALLOC_CTX *tmp_ctx;
48
0
  int ret, i, j;
49
0
  struct ldb_result *res;
50
0
  struct ldb_message *msg;
51
0
  struct ldb_message_element *el;
52
0
  const char *attrs[] = { attr, NULL };
53
54
0
  tmp_ctx = talloc_new(mem_ctx);
55
0
  if (tmp_ctx == NULL) {
56
0
    return LDB_ERR_OPERATIONS_ERROR;
57
0
  }
58
59
  /*
60
   * Fetch the entry to_visit
61
   *
62
   * NOTE: This is a new LDB search from the TOP of the module
63
   * stack.  This means that this search runs the whole stack
64
   * from top to bottom.
65
   *
66
   * This may seem to be in-efficient, but it is also the only
67
   * way to ensure that the ACLs for this search are applied
68
   * correctly.
69
   *
70
   * Note also that we don't have the original request
71
   * here, so we can not apply controls or timeouts here.
72
   */
73
0
  ret = dsdb_search_dn(ldb,
74
0
           tmp_ctx,
75
0
           &res,
76
0
           to_visit->dn,
77
0
           attrs,
78
0
           DSDB_MARK_REQ_UNTRUSTED);
79
0
  if (ret != LDB_SUCCESS) {
80
0
    DBG_NOTICE("search failure (%d: %s) looking for '%s' on '%s'\n",
81
0
         ret,
82
0
         ldb_strerror(ret),
83
0
         attr,
84
0
         ldb_dn_get_linearized(to_visit->dn));
85
0
    talloc_free(tmp_ctx);
86
0
    return ret;
87
0
  }
88
0
  if (res->count != 1) {
89
0
    talloc_free(tmp_ctx);
90
0
    return LDB_ERR_OPERATIONS_ERROR;
91
0
  }
92
0
  msg = res->msgs[0];
93
94
  /* Fetch the attribute to match from the entry being visited */
95
0
  el = ldb_msg_find_element(msg, attr);
96
0
  if (el == NULL) {
97
    /* This entry does not have the attribute to match */
98
0
    talloc_free(tmp_ctx);
99
0
    *matched = false;
100
0
    return LDB_SUCCESS;
101
0
  }
102
103
  /*
104
   * If the value to match is present in the attribute values of the
105
   * current entry being visited, set matched to true and return OK
106
   */
107
0
  for (i=0; i<el->num_values; i++) {
108
0
    struct dsdb_dn *dn;
109
0
    dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
110
0
    if (dn == NULL) {
111
0
      talloc_free(tmp_ctx);
112
0
      *matched = false;
113
0
      return LDB_ERR_INVALID_DN_SYNTAX;
114
0
    }
115
116
0
    if (ldb_dn_compare(dn_to_match->dn, dn->dn) == 0) {
117
0
      talloc_free(tmp_ctx);
118
0
      *matched = true;
119
0
      return LDB_SUCCESS;
120
0
    }
121
0
  }
122
123
  /*
124
   * If arrived here, the value to match is not in the values of the
125
   * entry being visited. Add the entry being visited (to_visit)
126
   * to the visited array. The array is (re)allocated in the parent
127
   * memory context.
128
   */
129
0
  if (visited == NULL) {
130
0
    return LDB_ERR_OPERATIONS_ERROR;
131
0
  } else if (*visited == NULL) {
132
0
    *visited = talloc_array(mem_ctx, struct dsdb_dn *, 1);
133
0
    if (*visited == NULL) {
134
0
      talloc_free(tmp_ctx);
135
0
      return LDB_ERR_OPERATIONS_ERROR;
136
0
    }
137
0
    (*visited)[0] = to_visit;
138
0
    (*visited_count) = 1;
139
0
  } else {
140
0
    *visited = talloc_realloc(mem_ctx, *visited, struct dsdb_dn *,
141
0
           (*visited_count) + 1);
142
0
    if (*visited == NULL) {
143
0
      talloc_free(tmp_ctx);
144
0
      return LDB_ERR_OPERATIONS_ERROR;
145
0
    }
146
0
    (*visited)[(*visited_count)] = to_visit;
147
0
    (*visited_count)++;
148
0
  }
149
150
  /*
151
   * steal to_visit into visited array context, as it has to live until
152
   * the array is freed.
153
   */
154
0
  talloc_steal(*visited, to_visit);
155
156
  /*
157
   * Iterate over the values of the attribute of the entry being
158
   * visited (to_visit) and follow them, calling this function
159
   * recursively.
160
   * If the value is in the visited array, skip it.
161
   * Otherwise, follow the link and visit it.
162
   */
163
0
  for (i=0; i<el->num_values; i++) {
164
0
    struct dsdb_dn *next_to_visit;
165
0
    bool skip = false;
166
167
0
    next_to_visit = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
168
0
    if (next_to_visit == NULL) {
169
0
      talloc_free(tmp_ctx);
170
0
      *matched = false;
171
0
      return LDB_ERR_INVALID_DN_SYNTAX;
172
0
    }
173
174
    /*
175
     * If the value is already in the visited array, skip it.
176
     * Note the last element of the array is ignored because it is
177
     * the current entry DN.
178
     */
179
0
    for (j=0; j < (*visited_count) - 1; j++) {
180
0
      struct dsdb_dn *visited_dn = (*visited)[j];
181
0
      if (ldb_dn_compare(visited_dn->dn,
182
0
             next_to_visit->dn) == 0) {
183
0
        skip = true;
184
0
        break;
185
0
      }
186
0
    }
187
0
    if (skip) {
188
0
      talloc_free(next_to_visit);
189
0
      continue;
190
0
    }
191
192
    /* If the value is not in the visited array, evaluate it */
193
0
    ret = ldb_eval_transitive_filter_helper(tmp_ctx, ldb, attr,
194
0
              dn_to_match, dn_oid,
195
0
              next_to_visit,
196
0
              visited, visited_count,
197
0
              matched);
198
0
    if (ret != LDB_SUCCESS) {
199
0
      talloc_free(tmp_ctx);
200
0
      return ret;
201
0
    }
202
0
    if (*matched) {
203
0
      talloc_free(tmp_ctx);
204
0
      return LDB_SUCCESS;
205
0
    }
206
0
  }
207
208
0
  talloc_free(tmp_ctx);
209
0
  *matched = false;
210
0
  return LDB_SUCCESS;
211
0
}
212
213
/*
214
 * This function parses the linked attribute value to match, whose syntax
215
 * will be one of the different DN syntaxes, into a ldb_dn struct.
216
 */
217
static int ldb_eval_transitive_filter(TALLOC_CTX *mem_ctx,
218
              struct ldb_context *ldb,
219
              const char *attr,
220
              const struct ldb_val *value_to_match,
221
              struct dsdb_dn *current_object_dn,
222
              bool *matched)
223
0
{
224
0
  const struct dsdb_schema *schema;
225
0
  const struct dsdb_attribute *schema_attr;
226
0
  struct dsdb_dn *dn_to_match;
227
0
  const char *dn_oid;
228
0
  unsigned int count;
229
0
  struct dsdb_dn **visited = NULL;
230
231
0
  schema = dsdb_get_schema(ldb, mem_ctx);
232
0
  if (schema == NULL) {
233
0
    return LDB_ERR_OPERATIONS_ERROR;
234
0
  }
235
236
0
  schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attr);
237
0
  if (schema_attr == NULL) {
238
0
    return LDB_ERR_NO_SUCH_ATTRIBUTE;
239
0
  }
240
241
  /* This is the DN syntax of the attribute being matched */
242
0
  dn_oid = schema_attr->syntax->ldap_oid;
243
244
  /*
245
   * Build a ldb_dn struct holding the value to match, which is the
246
   * value entered in the search filter
247
   */
248
0
  dn_to_match = dsdb_dn_parse(mem_ctx, ldb, value_to_match, dn_oid);
249
0
  if (dn_to_match == NULL) {
250
0
    *matched = false;
251
0
    return LDB_SUCCESS;
252
0
  }
253
254
0
  return ldb_eval_transitive_filter_helper(mem_ctx, ldb, attr,
255
0
             dn_to_match, dn_oid,
256
0
             current_object_dn,
257
0
             &visited, &count, matched);
258
0
}
259
260
/*
261
 * This rule provides recursive search of a link attribute
262
 *
263
 * Documented in [MS-ADTS] section 3.1.1.3.4.4.3 LDAP_MATCHING_RULE_TRANSITIVE_EVAL
264
 * This allows a search filter such as:
265
 *
266
 * member:1.2.840.113556.1.4.1941:=cn=user,cn=users,dc=samba,dc=example,dc=com
267
 *
268
 * This searches not only the member attribute, but also any member
269
 * attributes that point at an object with this member in them.  All the
270
 * various DN syntax types are supported, not just plain DNs.
271
 *
272
 */
273
static int ldb_comparator_trans(struct ldb_context *ldb,
274
        const char *oid,
275
        const struct ldb_message *msg,
276
        const char *attribute_to_match,
277
        const struct ldb_val *value_to_match,
278
        bool *matched)
279
0
{
280
0
  const struct dsdb_schema *schema;
281
0
  const struct dsdb_attribute *schema_attr;
282
0
  struct ldb_dn *msg_dn;
283
0
  struct dsdb_dn *dsdb_msg_dn;
284
0
  TALLOC_CTX *tmp_ctx;
285
0
  int ret;
286
287
0
  tmp_ctx = talloc_new(ldb);
288
0
  if (tmp_ctx == NULL) {
289
0
    return LDB_ERR_OPERATIONS_ERROR;
290
0
  }
291
292
  /*
293
   * If the target attribute to match is not a linked attribute, then
294
   * the filter evaluates to undefined
295
   */
296
0
  schema = dsdb_get_schema(ldb, tmp_ctx);
297
0
  if (schema == NULL) {
298
0
    talloc_free(tmp_ctx);
299
0
    return LDB_ERR_OPERATIONS_ERROR;
300
0
  }
301
302
0
  schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
303
0
  if (schema_attr == NULL) {
304
0
    talloc_free(tmp_ctx);
305
0
    return LDB_ERR_NO_SUCH_ATTRIBUTE;
306
0
  }
307
308
  /*
309
   * This extended match filter is only valid for linked attributes,
310
   * following the MS definition (the schema attribute has a linkID
311
   * defined). See dochelp request 114111212024789 on cifs-protocols
312
   * mailing list.
313
   */
314
0
  if (schema_attr->linkID == 0) {
315
0
    *matched = false;
316
0
    talloc_free(tmp_ctx);
317
0
    return LDB_SUCCESS;
318
0
  }
319
320
  /* Duplicate original msg dn as the msg must not be modified */
321
0
  msg_dn = ldb_dn_copy(tmp_ctx, msg->dn);
322
0
  if (msg_dn == NULL) {
323
0
    talloc_free(tmp_ctx);
324
0
    return LDB_ERR_OPERATIONS_ERROR;
325
0
  }
326
327
  /*
328
   * Build a dsdb dn from the message copied DN, which should be a plain
329
   * DN syntax.
330
   */
331
0
  dsdb_msg_dn = dsdb_dn_construct(tmp_ctx, msg_dn, data_blob_null,
332
0
          LDB_SYNTAX_DN);
333
0
  if (dsdb_msg_dn == NULL) {
334
0
    *matched = false;
335
0
    return LDB_ERR_INVALID_DN_SYNTAX;
336
0
  }
337
338
0
  ret = ldb_eval_transitive_filter(tmp_ctx, ldb,
339
0
           attribute_to_match,
340
0
           value_to_match,
341
0
           dsdb_msg_dn, matched);
342
0
  talloc_free(tmp_ctx);
343
0
  return ret;
344
0
}
345
346
347
/*
348
 * This rule provides match of a dns object with expired records.
349
 *
350
 * This allows a search filter such as:
351
 *
352
 * dnsRecord:1.3.6.1.4.1.7165.4.5.3:=3694869
353
 *
354
 * where the value is a number of hours since the start of 1601.
355
 *
356
 * This allows the caller to find records that should become a DNS
357
 * tombstone, despite that information being deep within an NDR packed
358
 * object
359
 */
360
static int dsdb_match_for_dns_to_tombstone_time(struct ldb_context *ldb,
361
            const char *oid,
362
            const struct ldb_message *msg,
363
            const char *attribute_to_match,
364
            const struct ldb_val *value_to_match,
365
            bool *matched)
366
0
{
367
0
  TALLOC_CTX *tmp_ctx;
368
0
  unsigned int i;
369
0
  struct ldb_message_element *el = NULL;
370
0
  struct auth_session_info *session_info = NULL;
371
0
  uint64_t tombstone_time;
372
0
  struct dnsp_DnssrvRpcRecord *rec = NULL;
373
0
  enum ndr_err_code err;
374
0
  *matched = false;
375
376
  /* Needs to be dnsRecord, no match otherwise */
377
0
  if (ldb_attr_cmp(attribute_to_match, "dnsRecord") != 0) {
378
0
    return LDB_SUCCESS;
379
0
  }
380
381
0
  el = ldb_msg_find_element(msg, attribute_to_match);
382
0
  if (el == NULL) {
383
0
    return LDB_SUCCESS;
384
0
  }
385
386
0
  if (ldb_msg_element_is_inaccessible(el)) {
387
0
    *matched = false;
388
0
    return LDB_SUCCESS;
389
0
  }
390
391
0
  session_info = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"),
392
0
               struct auth_session_info);
393
0
  if (session_info == NULL) {
394
0
    return ldb_oom(ldb);
395
0
  }
396
0
  if (security_session_user_level(session_info, NULL)
397
0
    != SECURITY_SYSTEM) {
398
399
0
    DBG_ERR("unauthorised access\n");
400
0
    return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
401
0
  }
402
403
  /* We only expect uint32_t <= 10 digits */
404
0
  if (value_to_match->length >= 12) {
405
0
    DBG_ERR("Invalid timestamp passed\n");
406
0
    return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
407
0
  } else {
408
0
    int error = 0;
409
0
    char s[12];
410
411
0
    memcpy(s, value_to_match->data, value_to_match->length);
412
0
    s[value_to_match->length] = 0;
413
0
    if (s[0] == '\0') {
414
0
      DBG_ERR("Empty timestamp passed\n");
415
0
      return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
416
0
    }
417
0
    tombstone_time = smb_strtoull(s,
418
0
                NULL,
419
0
                10,
420
0
                &error,
421
0
                SMB_STR_FULL_STR_CONV);
422
0
    if (error != 0) {
423
0
      DBG_ERR("Invalid timestamp string passed\n");
424
0
      return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
425
0
    }
426
0
  }
427
428
0
  tmp_ctx = talloc_new(ldb);
429
0
  if (tmp_ctx == NULL) {
430
0
    return ldb_oom(ldb);
431
0
  }
432
433
0
  for (i = 0; i < el->num_values; i++) {
434
0
    rec = talloc_zero(tmp_ctx, struct dnsp_DnssrvRpcRecord);
435
0
    if (rec == NULL) {
436
0
      TALLOC_FREE(tmp_ctx);
437
0
      return ldb_oom(ldb);
438
0
    }
439
0
    err = ndr_pull_struct_blob(
440
0
      &(el->values[i]),
441
0
      tmp_ctx,
442
0
      rec,
443
0
      (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
444
0
    if (!NDR_ERR_CODE_IS_SUCCESS(err)){
445
0
      DBG_ERR("Failed to pull dns rec blob.\n");
446
0
      TALLOC_FREE(tmp_ctx);
447
0
      return LDB_ERR_OPERATIONS_ERROR;
448
0
    }
449
450
0
    if (rec->wType == DNS_TYPE_SOA || rec->wType == DNS_TYPE_NS) {
451
0
      TALLOC_FREE(rec);
452
0
      continue;
453
0
    }
454
455
0
    if (rec->wType == DNS_TYPE_TOMBSTONE) {
456
0
      TALLOC_FREE(rec);
457
0
      continue;
458
0
    }
459
0
    if (rec->dwTimeStamp == 0) {
460
0
      TALLOC_FREE(rec);
461
0
      continue;
462
0
    }
463
0
    if (rec->dwTimeStamp > tombstone_time) {
464
0
      TALLOC_FREE(rec);
465
0
      continue;
466
0
    }
467
468
0
    *matched = true;
469
0
    break;
470
0
  }
471
472
0
  TALLOC_FREE(tmp_ctx);
473
0
  return LDB_SUCCESS;
474
0
}
475
476
477
/*
478
 * This rule provides match of a link attribute against a 'should be expunged' criteria
479
 *
480
 * This allows a search filter such as:
481
 *
482
 * member:1.3.6.1.4.1.7165.4.5.2:=131139216000000000
483
 *
484
 * This searches the member attribute, but also any member attributes
485
 * that are deleted and should be expunged after the specified NTTIME
486
 * time.
487
 *
488
 */
489
static int dsdb_match_for_expunge(struct ldb_context *ldb,
490
          const char *oid,
491
          const struct ldb_message *msg,
492
          const char *attribute_to_match,
493
          const struct ldb_val *value_to_match,
494
          bool *matched)
495
0
{
496
0
  const struct dsdb_schema *schema;
497
0
  const struct dsdb_attribute *schema_attr;
498
0
  TALLOC_CTX *tmp_ctx;
499
0
  unsigned int i;
500
0
  struct ldb_message_element *el;
501
0
  struct auth_session_info *session_info;
502
0
  uint64_t tombstone_time;
503
0
  *matched = false;
504
505
0
  el = ldb_msg_find_element(msg, attribute_to_match);
506
0
  if (el == NULL) {
507
0
    return LDB_SUCCESS;
508
0
  }
509
510
0
  if (ldb_msg_element_is_inaccessible(el)) {
511
0
    *matched = false;
512
0
    return LDB_SUCCESS;
513
0
  }
514
515
0
  session_info
516
0
    = talloc_get_type(ldb_get_opaque(ldb, DSDB_SESSION_INFO),
517
0
          struct auth_session_info);
518
0
  if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
519
0
    return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
520
0
  }
521
522
  /*
523
   * If the target attribute to match is not a linked attribute, then
524
   * the filter evaluates to undefined
525
   */
526
0
  schema = dsdb_get_schema(ldb, NULL);
527
0
  if (schema == NULL) {
528
0
    return LDB_ERR_OPERATIONS_ERROR;
529
0
  }
530
531
  /* TODO this is O(log n) per attribute */
532
0
  schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
533
0
  if (schema_attr == NULL) {
534
0
    return LDB_ERR_NO_SUCH_ATTRIBUTE;
535
0
  }
536
537
  /*
538
   * This extended match filter is only valid for forward linked attributes.
539
   */
540
0
  if (schema_attr->linkID == 0 || (schema_attr->linkID & 1) == 1) {
541
0
    return LDB_ERR_NO_SUCH_ATTRIBUTE;
542
0
  }
543
544
  /* Just check we don't allow the caller to fill our stack */
545
0
  if (value_to_match->length >=64) {
546
0
    return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
547
0
  } else {
548
0
    int error = 0;
549
0
    char s[value_to_match->length+1];
550
551
0
    memcpy(s, value_to_match->data, value_to_match->length);
552
0
    s[value_to_match->length] = 0;
553
0
    if (s[0] == '\0' || s[0] == '-') {
554
0
      return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
555
0
    }
556
0
    tombstone_time = smb_strtoull(s,
557
0
                NULL,
558
0
                10,
559
0
                &error,
560
0
                SMB_STR_FULL_STR_CONV);
561
0
    if (error != 0) {
562
0
      return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
563
0
    }
564
0
  }
565
566
0
  tmp_ctx = talloc_new(ldb);
567
0
  if (tmp_ctx == NULL) {
568
0
    return LDB_ERR_OPERATIONS_ERROR;
569
0
  }
570
571
0
  for (i = 0; i < el->num_values; i++) {
572
0
    NTSTATUS status;
573
0
    struct dsdb_dn *dn;
574
0
    uint64_t rmd_changetime;
575
0
    if (dsdb_dn_is_deleted_val(&el->values[i]) == false) {
576
0
      continue;
577
0
    }
578
579
0
    dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
580
0
           schema_attr->syntax->ldap_oid);
581
0
    if (dn == NULL) {
582
0
      DEBUG(1, ("Error: Failed to parse linked attribute blob of %s.\n", el->name));
583
0
      continue;
584
0
    }
585
586
0
    status = dsdb_get_extended_dn_uint64(dn->dn, &rmd_changetime,
587
0
                 "RMD_CHANGETIME");
588
0
    if (!NT_STATUS_IS_OK(status)) {
589
0
      DEBUG(1, ("Error: RMD_CHANGETIME is missing on a forward link.\n"));
590
0
      continue;
591
0
    }
592
593
0
    if (rmd_changetime > tombstone_time) {
594
0
      continue;
595
0
    }
596
597
0
    *matched = true;
598
0
    break;
599
0
  }
600
0
  talloc_free(tmp_ctx);
601
0
  return LDB_SUCCESS;
602
0
}
603
604
605
int ldb_register_samba_matching_rules(struct ldb_context *ldb)
606
0
{
607
0
  struct ldb_extended_match_rule *transitive_eval = NULL,
608
0
    *match_for_expunge = NULL,
609
0
    *match_for_dns_to_tombstone_time = NULL;
610
0
  int ret;
611
612
0
  transitive_eval = talloc_zero(ldb, struct ldb_extended_match_rule);
613
0
  transitive_eval->oid = SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL;
614
0
  transitive_eval->callback = ldb_comparator_trans;
615
0
  ret = ldb_register_extended_match_rule(ldb, transitive_eval);
616
0
  if (ret != LDB_SUCCESS) {
617
0
    talloc_free(transitive_eval);
618
0
    return ret;
619
0
  }
620
621
0
  match_for_expunge = talloc_zero(ldb, struct ldb_extended_match_rule);
622
0
  match_for_expunge->oid = DSDB_MATCH_FOR_EXPUNGE;
623
0
  match_for_expunge->callback = dsdb_match_for_expunge;
624
0
  ret = ldb_register_extended_match_rule(ldb, match_for_expunge);
625
0
  if (ret != LDB_SUCCESS) {
626
0
    talloc_free(match_for_expunge);
627
0
    return ret;
628
0
  }
629
630
0
  match_for_dns_to_tombstone_time = talloc_zero(
631
0
    ldb,
632
0
    struct ldb_extended_match_rule);
633
0
  match_for_dns_to_tombstone_time->oid = DSDB_MATCH_FOR_DNS_TO_TOMBSTONE_TIME;
634
0
  match_for_dns_to_tombstone_time->callback
635
0
    = dsdb_match_for_dns_to_tombstone_time;
636
0
  ret = ldb_register_extended_match_rule(ldb,
637
0
                 match_for_dns_to_tombstone_time);
638
0
  if (ret != LDB_SUCCESS) {
639
0
    TALLOC_FREE(match_for_dns_to_tombstone_time);
640
0
    return ret;
641
0
  }
642
643
0
  return LDB_SUCCESS;
644
0
}