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