Search for location of waf script
[Samba.git] / lib / ldb-samba / ldb_matching_rules.c
blob2aaaeb7450b15f67f3c11b92199529c909bf1bd0
1 /*
2 Unix SMB/CIFS implementation.
4 ldb database library - Extended match rules
6 Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me>
7 Copyright (C) Andrew Bartlett <abartlet@samba.org>
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.
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.
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/>.
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"
31 static int ldb_eval_transitive_filter_helper(TALLOC_CTX *mem_ctx,
32 struct ldb_context *ldb,
33 const char *attr,
34 const struct dsdb_dn *dn_to_match,
35 const char *dn_oid,
36 struct dsdb_dn *to_visit,
37 struct dsdb_dn ***visited,
38 unsigned int *visited_count,
39 bool *matched)
41 TALLOC_CTX *tmp_ctx;
42 int ret, i, j;
43 struct ldb_result *res;
44 struct ldb_message *msg;
45 struct ldb_message_element *el;
46 const char *attrs[] = { attr, NULL };
48 tmp_ctx = talloc_new(mem_ctx);
49 if (tmp_ctx == NULL) {
50 return LDB_ERR_OPERATIONS_ERROR;
54 * Fetch the entry to_visit
56 * NOTE: This is a new LDB search from the TOP of the module
57 * stack. This means that this search runs the whole stack
58 * from top to bottom.
60 * This may seem to be in-efficient, but it is also the only
61 * way to ensure that the ACLs for this search are applied
62 * correctly.
64 * Note also that we don't have the original request
65 * here, so we can not apply controls or timeouts here.
67 ret = dsdb_search_dn(ldb, tmp_ctx, &res, to_visit->dn, attrs, 0);
68 if (ret != LDB_SUCCESS) {
69 talloc_free(tmp_ctx);
70 return ret;
72 if (res->count != 1) {
73 talloc_free(tmp_ctx);
74 return LDB_ERR_OPERATIONS_ERROR;
76 msg = res->msgs[0];
78 /* Fetch the attribute to match from the entry being visited */
79 el = ldb_msg_find_element(msg, attr);
80 if (el == NULL) {
81 /* This entry does not have the attribute to match */
82 talloc_free(tmp_ctx);
83 *matched = false;
84 return LDB_SUCCESS;
88 * If the value to match is present in the attribute values of the
89 * current entry being visited, set matched to true and return OK
91 for (i=0; i<el->num_values; i++) {
92 struct dsdb_dn *dn;
93 dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
94 if (dn == NULL) {
95 talloc_free(tmp_ctx);
96 *matched = false;
97 return LDB_ERR_INVALID_DN_SYNTAX;
100 if (ldb_dn_compare(dn_to_match->dn, dn->dn) == 0) {
101 talloc_free(tmp_ctx);
102 *matched = true;
103 return LDB_SUCCESS;
108 * If arrived here, the value to match is not in the values of the
109 * entry being visited. Add the entry being visited (to_visit)
110 * to the visited array. The array is (re)allocated in the parent
111 * memory context.
113 if (visited == NULL) {
114 return LDB_ERR_OPERATIONS_ERROR;
115 } else if (*visited == NULL) {
116 *visited = talloc_array(mem_ctx, struct dsdb_dn *, 1);
117 if (*visited == NULL) {
118 talloc_free(tmp_ctx);
119 return LDB_ERR_OPERATIONS_ERROR;
121 (*visited)[0] = to_visit;
122 (*visited_count) = 1;
123 } else {
124 *visited = talloc_realloc(mem_ctx, *visited, struct dsdb_dn *,
125 (*visited_count) + 1);
126 if (*visited == NULL) {
127 talloc_free(tmp_ctx);
128 return LDB_ERR_OPERATIONS_ERROR;
130 (*visited)[(*visited_count)] = to_visit;
131 (*visited_count)++;
135 * steal to_visit into visited array context, as it has to live until
136 * the array is freed.
138 talloc_steal(*visited, to_visit);
141 * Iterate over the values of the attribute of the entry being
142 * visited (to_visit) and follow them, calling this function
143 * recursively.
144 * If the value is in the visited array, skip it.
145 * Otherwise, follow the link and visit it.
147 for (i=0; i<el->num_values; i++) {
148 struct dsdb_dn *next_to_visit;
149 bool skip = false;
151 next_to_visit = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
152 if (next_to_visit == NULL) {
153 talloc_free(tmp_ctx);
154 *matched = false;
155 return LDB_ERR_INVALID_DN_SYNTAX;
159 * If the value is already in the visited array, skip it.
160 * Note the last element of the array is ignored because it is
161 * the current entry DN.
163 for (j=0; j < (*visited_count) - 1; j++) {
164 struct dsdb_dn *visited_dn = (*visited)[j];
165 if (ldb_dn_compare(visited_dn->dn,
166 next_to_visit->dn) == 0) {
167 skip = true;
168 break;
171 if (skip) {
172 talloc_free(next_to_visit);
173 continue;
176 /* If the value is not in the visited array, evaluate it */
177 ret = ldb_eval_transitive_filter_helper(tmp_ctx, ldb, attr,
178 dn_to_match, dn_oid,
179 next_to_visit,
180 visited, visited_count,
181 matched);
182 if (ret != LDB_SUCCESS) {
183 talloc_free(tmp_ctx);
184 return ret;
186 if (*matched) {
187 talloc_free(tmp_ctx);
188 return LDB_SUCCESS;
192 talloc_free(tmp_ctx);
193 *matched = false;
194 return LDB_SUCCESS;
198 * This function parses the linked attribute value to match, whose syntax
199 * will be one of the different DN syntaxes, into a ldb_dn struct.
201 static int ldb_eval_transitive_filter(TALLOC_CTX *mem_ctx,
202 struct ldb_context *ldb,
203 const char *attr,
204 const struct ldb_val *value_to_match,
205 struct dsdb_dn *current_object_dn,
206 bool *matched)
208 const struct dsdb_schema *schema;
209 const struct dsdb_attribute *schema_attr;
210 struct dsdb_dn *dn_to_match;
211 const char *dn_oid;
212 unsigned int count;
213 struct dsdb_dn **visited = NULL;
215 schema = dsdb_get_schema(ldb, mem_ctx);
216 if (schema == NULL) {
217 return LDB_ERR_OPERATIONS_ERROR;
220 schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attr);
221 if (schema_attr == NULL) {
222 return LDB_ERR_NO_SUCH_ATTRIBUTE;
225 /* This is the DN syntax of the attribute being matched */
226 dn_oid = schema_attr->syntax->ldap_oid;
229 * Build a ldb_dn struct holding the value to match, which is the
230 * value entered in the search filter
232 dn_to_match = dsdb_dn_parse(mem_ctx, ldb, value_to_match, dn_oid);
233 if (dn_to_match == NULL) {
234 *matched = false;
235 return LDB_SUCCESS;
238 return ldb_eval_transitive_filter_helper(mem_ctx, ldb, attr,
239 dn_to_match, dn_oid,
240 current_object_dn,
241 &visited, &count, matched);
245 * This rule provides recursive search of a link attribute
247 * Documented in [MS-ADTS] section 3.1.1.3.4.4.3 LDAP_MATCHING_RULE_TRANSITIVE_EVAL
248 * This allows a search filter such as:
250 * member:1.2.840.113556.1.4.1941:=cn=user,cn=users,dc=samba,dc=example,dc=com
252 * This searches not only the member attribute, but also any member
253 * attributes that point at an object with this member in them. All the
254 * various DN syntax types are supported, not just plain DNs.
257 static int ldb_comparator_trans(struct ldb_context *ldb,
258 const char *oid,
259 const struct ldb_message *msg,
260 const char *attribute_to_match,
261 const struct ldb_val *value_to_match,
262 bool *matched)
264 const struct dsdb_schema *schema;
265 const struct dsdb_attribute *schema_attr;
266 struct ldb_dn *msg_dn;
267 struct dsdb_dn *dsdb_msg_dn;
268 TALLOC_CTX *tmp_ctx;
269 int ret;
271 tmp_ctx = talloc_new(ldb);
272 if (tmp_ctx == NULL) {
273 return LDB_ERR_OPERATIONS_ERROR;
277 * If the target attribute to match is not a linked attribute, then
278 * the filter evaluates to undefined
280 schema = dsdb_get_schema(ldb, tmp_ctx);
281 if (schema == NULL) {
282 talloc_free(tmp_ctx);
283 return LDB_ERR_OPERATIONS_ERROR;
286 schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
287 if (schema_attr == NULL) {
288 talloc_free(tmp_ctx);
289 return LDB_ERR_NO_SUCH_ATTRIBUTE;
293 * This extended match filter is only valid for linked attributes,
294 * following the MS definition (the schema attribute has a linkID
295 * defined). See dochelp request 114111212024789 on cifs-protocols
296 * mailing list.
298 if (schema_attr->linkID == 0) {
299 *matched = false;
300 talloc_free(tmp_ctx);
301 return LDB_SUCCESS;
304 /* Duplicate original msg dn as the msg must not be modified */
305 msg_dn = ldb_dn_copy(tmp_ctx, msg->dn);
306 if (msg_dn == NULL) {
307 talloc_free(tmp_ctx);
308 return LDB_ERR_OPERATIONS_ERROR;
312 * Build a dsdb dn from the message copied DN, which should be a plain
313 * DN syntax.
315 dsdb_msg_dn = dsdb_dn_construct(tmp_ctx, msg_dn, data_blob_null,
316 LDB_SYNTAX_DN);
317 if (dsdb_msg_dn == NULL) {
318 *matched = false;
319 return LDB_ERR_INVALID_DN_SYNTAX;
322 ret = ldb_eval_transitive_filter(tmp_ctx, ldb,
323 attribute_to_match,
324 value_to_match,
325 dsdb_msg_dn, matched);
326 talloc_free(tmp_ctx);
327 return ret;
332 * This rule provides match of a dns object with expired records.
334 * This allows a search filter such as:
336 * dnsRecord:1.3.6.1.4.1.7165.4.5.3:=131139216000000000
338 * This allows the caller to find records that should become a DNS
339 * tomestone, despite that information being deep within an NDR packed
340 * object
342 static int dsdb_match_for_dns_to_tombstone_time(struct ldb_context *ldb,
343 const char *oid,
344 const struct ldb_message *msg,
345 const char *attribute_to_match,
346 const struct ldb_val *value_to_match,
347 bool *matched)
349 TALLOC_CTX *tmp_ctx;
350 unsigned int i;
351 struct ldb_message_element *el = NULL;
352 struct auth_session_info *session_info = NULL;
353 uint64_t tombstone_time;
354 struct dnsp_DnssrvRpcRecord *rec = NULL;
355 enum ndr_err_code err;
356 *matched = false;
358 /* Needs to be dnsRecord, no match otherwise */
359 if (ldb_attr_cmp(attribute_to_match, "dnsRecord") != 0) {
360 return LDB_SUCCESS;
363 el = ldb_msg_find_element(msg, attribute_to_match);
364 if (el == NULL) {
365 return LDB_SUCCESS;
368 session_info = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"),
369 struct auth_session_info);
370 if (session_info == NULL) {
371 return ldb_oom(ldb);
373 if (security_session_user_level(session_info, NULL)
374 != SECURITY_SYSTEM) {
376 DBG_ERR("unauthorised access\n");
377 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
380 /* Just check we don't allow the caller to fill our stack */
381 if (value_to_match->length >= 64) {
382 DBG_ERR("Invalid timestamp passed\n");
383 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
384 } else {
385 char *p = NULL;
386 char s[value_to_match->length+1];
387 memcpy(s, value_to_match->data, value_to_match->length);
388 s[value_to_match->length] = 0;
389 if (s[0] == '\0' || s[0] == '-') {
390 DBG_ERR("Empty timestamp passed\n");
391 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
393 tombstone_time = strtoull(s, &p, 10);
394 if (p == NULL || p == s || *p != '\0' ||
395 tombstone_time == ULLONG_MAX) {
396 DBG_ERR("Invalid timestamp string passed\n");
397 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
401 tmp_ctx = talloc_new(ldb);
402 if (tmp_ctx == NULL) {
403 return ldb_oom(ldb);
406 for (i = 0; i < el->num_values; i++) {
407 rec = talloc_zero(tmp_ctx, struct dnsp_DnssrvRpcRecord);
408 if (rec == NULL) {
409 TALLOC_FREE(tmp_ctx);
410 return ldb_oom(ldb);
412 err = ndr_pull_struct_blob(
413 &(el->values[i]),
414 tmp_ctx,
415 rec,
416 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
417 if (!NDR_ERR_CODE_IS_SUCCESS(err)){
418 DBG_ERR("Failed to pull dns rec blob.\n");
419 TALLOC_FREE(tmp_ctx);
420 return LDB_ERR_OPERATIONS_ERROR;
423 if (rec->wType == DNS_TYPE_SOA || rec->wType == DNS_TYPE_NS) {
424 TALLOC_FREE(tmp_ctx);
425 continue;
428 if (rec->wType == DNS_TYPE_TOMBSTONE) {
429 TALLOC_FREE(tmp_ctx);
430 continue;
432 if (rec->dwTimeStamp == 0) {
433 TALLOC_FREE(tmp_ctx);
434 continue;
436 if (rec->dwTimeStamp > tombstone_time) {
437 TALLOC_FREE(tmp_ctx);
438 continue;
441 *matched = true;
442 break;
445 TALLOC_FREE(tmp_ctx);
446 return LDB_SUCCESS;
451 * This rule provides match of a link attribute against a 'should be expunged' criteria
453 * This allows a search filter such as:
455 * member:1.3.6.1.4.1.7165.4.5.2:=131139216000000000
457 * This searches the member attribute, but also any member attributes
458 * that are deleted and should be expunged after the specified NTTIME
459 * time.
462 static int dsdb_match_for_expunge(struct ldb_context *ldb,
463 const char *oid,
464 const struct ldb_message *msg,
465 const char *attribute_to_match,
466 const struct ldb_val *value_to_match,
467 bool *matched)
469 const struct dsdb_schema *schema;
470 const struct dsdb_attribute *schema_attr;
471 TALLOC_CTX *tmp_ctx;
472 unsigned int i;
473 struct ldb_message_element *el;
474 struct auth_session_info *session_info;
475 uint64_t tombstone_time;
476 *matched = false;
478 el = ldb_msg_find_element(msg, attribute_to_match);
479 if (el == NULL) {
480 return LDB_SUCCESS;
483 session_info
484 = talloc_get_type(ldb_get_opaque(ldb, DSDB_SESSION_INFO),
485 struct auth_session_info);
486 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
487 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
491 * If the target attribute to match is not a linked attribute, then
492 * the filter evaluates to undefined
494 schema = dsdb_get_schema(ldb, NULL);
495 if (schema == NULL) {
496 return LDB_ERR_OPERATIONS_ERROR;
499 /* TODO this is O(log n) per attribute */
500 schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
501 if (schema_attr == NULL) {
502 return LDB_ERR_NO_SUCH_ATTRIBUTE;
506 * This extended match filter is only valid for forward linked attributes.
508 if (schema_attr->linkID == 0 || (schema_attr->linkID & 1) == 1) {
509 return LDB_ERR_NO_SUCH_ATTRIBUTE;
512 /* Just check we don't allow the caller to fill our stack */
513 if (value_to_match->length >=64) {
514 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
515 } else {
516 char *p = NULL;
517 char s[value_to_match->length+1];
518 memcpy(s, value_to_match->data, value_to_match->length);
519 s[value_to_match->length] = 0;
520 if (s[0] == '\0' || s[0] == '-') {
521 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
523 tombstone_time = strtoull(s, &p, 10);
524 if (p == NULL || p == s || *p != '\0' || tombstone_time == ULLONG_MAX) {
525 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
529 tmp_ctx = talloc_new(ldb);
530 if (tmp_ctx == NULL) {
531 return LDB_ERR_OPERATIONS_ERROR;
534 for (i = 0; i < el->num_values; i++) {
535 NTSTATUS status;
536 struct dsdb_dn *dn;
537 uint64_t rmd_changetime;
538 if (dsdb_dn_is_deleted_val(&el->values[i]) == false) {
539 continue;
542 dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
543 schema_attr->syntax->ldap_oid);
544 if (dn == NULL) {
545 DEBUG(1, ("Error: Failed to parse linked attribute blob of %s.\n", el->name));
546 continue;
549 status = dsdb_get_extended_dn_uint64(dn->dn, &rmd_changetime,
550 "RMD_CHANGETIME");
551 if (!NT_STATUS_IS_OK(status)) {
552 DEBUG(1, ("Error: RMD_CHANGETIME is missing on a forward link.\n"));
553 continue;
556 if (rmd_changetime > tombstone_time) {
557 continue;
560 *matched = true;
561 break;
563 talloc_free(tmp_ctx);
564 return LDB_SUCCESS;
568 int ldb_register_samba_matching_rules(struct ldb_context *ldb)
570 struct ldb_extended_match_rule *transitive_eval = NULL,
571 *match_for_expunge = NULL,
572 *match_for_dns_to_tombstone_time = NULL;
573 int ret;
575 transitive_eval = talloc_zero(ldb, struct ldb_extended_match_rule);
576 transitive_eval->oid = SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL;
577 transitive_eval->callback = ldb_comparator_trans;
578 ret = ldb_register_extended_match_rule(ldb, transitive_eval);
579 if (ret != LDB_SUCCESS) {
580 talloc_free(transitive_eval);
581 return ret;
584 match_for_expunge = talloc_zero(ldb, struct ldb_extended_match_rule);
585 match_for_expunge->oid = DSDB_MATCH_FOR_EXPUNGE;
586 match_for_expunge->callback = dsdb_match_for_expunge;
587 ret = ldb_register_extended_match_rule(ldb, match_for_expunge);
588 if (ret != LDB_SUCCESS) {
589 talloc_free(match_for_expunge);
590 return ret;
593 match_for_dns_to_tombstone_time = talloc_zero(
594 ldb,
595 struct ldb_extended_match_rule);
596 match_for_dns_to_tombstone_time->oid = DSDB_MATCH_FOR_DNS_TO_TOMBSTONE_TIME;
597 match_for_dns_to_tombstone_time->callback
598 = dsdb_match_for_dns_to_tombstone_time;
599 ret = ldb_register_extended_match_rule(ldb,
600 match_for_dns_to_tombstone_time);
601 if (ret != LDB_SUCCESS) {
602 TALLOC_FREE(match_for_dns_to_tombstone_time);
603 return ret;
606 return LDB_SUCCESS;