s3: libsmb: Fix reversing of oldname/newname paths when creating a reparse point...
[Samba.git] / lib / ldb-samba / ldb_matching_rules.c
blobaa8697941c6325f3313228fd76f4bca8293c40f4
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"
30 static int ldb_eval_transitive_filter_helper(TALLOC_CTX *mem_ctx,
31 struct ldb_context *ldb,
32 const char *attr,
33 const struct dsdb_dn *dn_to_match,
34 const char *dn_oid,
35 struct dsdb_dn *to_visit,
36 struct dsdb_dn ***visited,
37 unsigned int *visited_count,
38 bool *matched)
40 TALLOC_CTX *tmp_ctx;
41 int ret, i, j;
42 struct ldb_result *res;
43 struct ldb_message *msg;
44 struct ldb_message_element *el;
45 const char *attrs[] = { attr, NULL };
47 tmp_ctx = talloc_new(mem_ctx);
48 if (tmp_ctx == NULL) {
49 return LDB_ERR_OPERATIONS_ERROR;
53 * Fetch the entry to_visit
55 * NOTE: This is a new LDB search from the TOP of the module
56 * stack. This means that this search runs the whole stack
57 * from top to bottom.
59 * This may seem to be in-efficient, but it is also the only
60 * way to ensure that the ACLs for this search are applied
61 * correctly.
63 * Note also that we don't have the original request
64 * here, so we can not apply controls or timeouts here.
66 ret = dsdb_search_dn(ldb, tmp_ctx, &res, to_visit->dn, attrs, 0);
67 if (ret != LDB_SUCCESS) {
68 talloc_free(tmp_ctx);
69 return ret;
71 if (res->count != 1) {
72 talloc_free(tmp_ctx);
73 return LDB_ERR_OPERATIONS_ERROR;
75 msg = res->msgs[0];
77 /* Fetch the attribute to match from the entry being visited */
78 el = ldb_msg_find_element(msg, attr);
79 if (el == NULL) {
80 /* This entry does not have the attribute to match */
81 talloc_free(tmp_ctx);
82 *matched = false;
83 return LDB_SUCCESS;
87 * If the value to match is present in the attribute values of the
88 * current entry being visited, set matched to true and return OK
90 for (i=0; i<el->num_values; i++) {
91 struct dsdb_dn *dn;
92 dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
93 if (dn == NULL) {
94 talloc_free(tmp_ctx);
95 *matched = false;
96 return LDB_ERR_INVALID_DN_SYNTAX;
99 if (ldb_dn_compare(dn_to_match->dn, dn->dn) == 0) {
100 talloc_free(tmp_ctx);
101 *matched = true;
102 return LDB_SUCCESS;
107 * If arrived here, the value to match is not in the values of the
108 * entry being visited. Add the entry being visited (to_visit)
109 * to the visited array. The array is (re)allocated in the parent
110 * memory context.
112 if (visited == NULL) {
113 return LDB_ERR_OPERATIONS_ERROR;
114 } else if (*visited == NULL) {
115 *visited = talloc_array(mem_ctx, struct dsdb_dn *, 1);
116 if (*visited == NULL) {
117 talloc_free(tmp_ctx);
118 return LDB_ERR_OPERATIONS_ERROR;
120 (*visited)[0] = to_visit;
121 (*visited_count) = 1;
122 } else {
123 *visited = talloc_realloc(mem_ctx, *visited, struct dsdb_dn *,
124 (*visited_count) + 1);
125 if (*visited == NULL) {
126 talloc_free(tmp_ctx);
127 return LDB_ERR_OPERATIONS_ERROR;
129 (*visited)[(*visited_count)] = to_visit;
130 (*visited_count)++;
134 * steal to_visit into visited array context, as it has to live until
135 * the array is freed.
137 talloc_steal(*visited, to_visit);
140 * Iterate over the values of the attribute of the entry being
141 * visited (to_visit) and follow them, calling this function
142 * recursively.
143 * If the value is in the visited array, skip it.
144 * Otherwise, follow the link and visit it.
146 for (i=0; i<el->num_values; i++) {
147 struct dsdb_dn *next_to_visit;
148 bool skip = false;
150 next_to_visit = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
151 if (next_to_visit == NULL) {
152 talloc_free(tmp_ctx);
153 *matched = false;
154 return LDB_ERR_INVALID_DN_SYNTAX;
158 * If the value is already in the visited array, skip it.
159 * Note the last element of the array is ignored because it is
160 * the current entry DN.
162 for (j=0; j < (*visited_count) - 1; j++) {
163 struct dsdb_dn *visited_dn = (*visited)[j];
164 if (ldb_dn_compare(visited_dn->dn,
165 next_to_visit->dn) == 0) {
166 skip = true;
167 break;
170 if (skip) {
171 talloc_free(next_to_visit);
172 continue;
175 /* If the value is not in the visited array, evaluate it */
176 ret = ldb_eval_transitive_filter_helper(tmp_ctx, ldb, attr,
177 dn_to_match, dn_oid,
178 next_to_visit,
179 visited, visited_count,
180 matched);
181 if (ret != LDB_SUCCESS) {
182 talloc_free(tmp_ctx);
183 return ret;
185 if (*matched) {
186 talloc_free(tmp_ctx);
187 return LDB_SUCCESS;
191 talloc_free(tmp_ctx);
192 *matched = false;
193 return LDB_SUCCESS;
197 * This function parses the linked attribute value to match, whose syntax
198 * will be one of the different DN syntaxes, into a ldb_dn struct.
200 static int ldb_eval_transitive_filter(TALLOC_CTX *mem_ctx,
201 struct ldb_context *ldb,
202 const char *attr,
203 const struct ldb_val *value_to_match,
204 struct dsdb_dn *current_object_dn,
205 bool *matched)
207 const struct dsdb_schema *schema;
208 const struct dsdb_attribute *schema_attr;
209 struct dsdb_dn *dn_to_match;
210 const char *dn_oid;
211 unsigned int count;
212 struct dsdb_dn **visited = NULL;
214 schema = dsdb_get_schema(ldb, mem_ctx);
215 if (schema == NULL) {
216 return LDB_ERR_OPERATIONS_ERROR;
219 schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attr);
220 if (schema_attr == NULL) {
221 return LDB_ERR_NO_SUCH_ATTRIBUTE;
224 /* This is the DN syntax of the attribute being matched */
225 dn_oid = schema_attr->syntax->ldap_oid;
228 * Build a ldb_dn struct holding the value to match, which is the
229 * value entered in the search filter
231 dn_to_match = dsdb_dn_parse(mem_ctx, ldb, value_to_match, dn_oid);
232 if (dn_to_match == NULL) {
233 *matched = false;
234 return LDB_SUCCESS;
237 return ldb_eval_transitive_filter_helper(mem_ctx, ldb, attr,
238 dn_to_match, dn_oid,
239 current_object_dn,
240 &visited, &count, matched);
244 * This rule provides recursive search of a link attribute
246 * Documented in [MS-ADTS] section 3.1.1.3.4.4.3 LDAP_MATCHING_RULE_TRANSITIVE_EVAL
247 * This allows a search filter such as:
249 * member:1.2.840.113556.1.4.1941:=cn=user,cn=users,dc=samba,dc=example,dc=com
251 * This searches not only the member attribute, but also any member
252 * attributes that point at an object with this member in them. All the
253 * various DN syntax types are supported, not just plain DNs.
256 static int ldb_comparator_trans(struct ldb_context *ldb,
257 const char *oid,
258 const struct ldb_message *msg,
259 const char *attribute_to_match,
260 const struct ldb_val *value_to_match,
261 bool *matched)
263 const struct dsdb_schema *schema;
264 const struct dsdb_attribute *schema_attr;
265 struct ldb_dn *msg_dn;
266 struct dsdb_dn *dsdb_msg_dn;
267 TALLOC_CTX *tmp_ctx;
268 int ret;
270 tmp_ctx = talloc_new(ldb);
271 if (tmp_ctx == NULL) {
272 return LDB_ERR_OPERATIONS_ERROR;
276 * If the target attribute to match is not a linked attribute, then
277 * the filter evaluates to undefined
279 schema = dsdb_get_schema(ldb, tmp_ctx);
280 if (schema == NULL) {
281 talloc_free(tmp_ctx);
282 return LDB_ERR_OPERATIONS_ERROR;
285 schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
286 if (schema_attr == NULL) {
287 talloc_free(tmp_ctx);
288 return LDB_ERR_NO_SUCH_ATTRIBUTE;
292 * This extended match filter is only valid for linked attributes,
293 * following the MS definition (the schema attribute has a linkID
294 * defined). See dochelp request 114111212024789 on cifs-protocols
295 * mailing list.
297 if (schema_attr->linkID == 0) {
298 *matched = false;
299 talloc_free(tmp_ctx);
300 return LDB_SUCCESS;
303 /* Duplicate original msg dn as the msg must not be modified */
304 msg_dn = ldb_dn_copy(tmp_ctx, msg->dn);
305 if (msg_dn == NULL) {
306 talloc_free(tmp_ctx);
307 return LDB_ERR_OPERATIONS_ERROR;
311 * Build a dsdb dn from the message copied DN, which should be a plain
312 * DN syntax.
314 dsdb_msg_dn = dsdb_dn_construct(tmp_ctx, msg_dn, data_blob_null,
315 LDB_SYNTAX_DN);
316 if (dsdb_msg_dn == NULL) {
317 *matched = false;
318 return LDB_ERR_INVALID_DN_SYNTAX;
321 ret = ldb_eval_transitive_filter(tmp_ctx, ldb,
322 attribute_to_match,
323 value_to_match,
324 dsdb_msg_dn, matched);
325 talloc_free(tmp_ctx);
326 return ret;
331 * This rule provides match of a link attribute against a 'should be expunged' criteria
333 * This allows a search filter such as:
335 * member:1.3.6.1.4.1.7165.4.5.2:=131139216000000000
337 * This searches the member attribute, but also any member attributes
338 * that are deleted and should be expunged after the specified NTTIME
339 * time.
342 static int dsdb_match_for_expunge(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 const struct dsdb_schema *schema;
350 const struct dsdb_attribute *schema_attr;
351 TALLOC_CTX *tmp_ctx;
352 unsigned int i;
353 struct ldb_message_element *el;
354 struct auth_session_info *session_info;
355 uint64_t tombstone_time;
356 *matched = false;
358 el = ldb_msg_find_element(msg, attribute_to_match);
359 if (el == NULL) {
360 return LDB_SUCCESS;
363 session_info
364 = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"),
365 struct auth_session_info);
366 if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
367 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
371 * If the target attribute to match is not a linked attribute, then
372 * the filter evaluates to undefined
374 schema = dsdb_get_schema(ldb, NULL);
375 if (schema == NULL) {
376 return LDB_ERR_OPERATIONS_ERROR;
379 /* TODO this is O(log n) per attribute */
380 schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
381 if (schema_attr == NULL) {
382 return LDB_ERR_NO_SUCH_ATTRIBUTE;
386 * This extended match filter is only valid for forward linked attributes.
388 if (schema_attr->linkID == 0 || (schema_attr->linkID & 1) == 1) {
389 return LDB_ERR_NO_SUCH_ATTRIBUTE;
392 /* Just check we don't allow the caller to fill our stack */
393 if (value_to_match->length >=64) {
394 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
395 } else {
396 char *p = NULL;
397 char s[value_to_match->length+1];
398 memcpy(s, value_to_match->data, value_to_match->length);
399 s[value_to_match->length] = 0;
400 if (s[0] == '\0' || s[0] == '-') {
401 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
403 tombstone_time = strtoull(s, &p, 10);
404 if (p == NULL || p == s || *p != '\0' || tombstone_time == ULLONG_MAX) {
405 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
409 tmp_ctx = talloc_new(ldb);
410 if (tmp_ctx == NULL) {
411 return LDB_ERR_OPERATIONS_ERROR;
414 for (i = 0; i < el->num_values; i++) {
415 NTSTATUS status;
416 struct dsdb_dn *dn;
417 uint64_t rmd_changetime;
418 if (dsdb_dn_is_deleted_val(&el->values[i]) == false) {
419 continue;
422 dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i],
423 schema_attr->syntax->ldap_oid);
424 if (dn == NULL) {
425 DEBUG(1, ("Error: Failed to parse linked attribute blob of %s.\n", el->name));
426 continue;
429 status = dsdb_get_extended_dn_uint64(dn->dn, &rmd_changetime,
430 "RMD_CHANGETIME");
431 if (!NT_STATUS_IS_OK(status)) {
432 DEBUG(1, ("Error: RMD_CHANGETIME is missing on a forward link.\n"));
433 continue;
436 if (rmd_changetime > tombstone_time) {
437 continue;
440 *matched = true;
441 break;
443 talloc_free(tmp_ctx);
444 return LDB_SUCCESS;
448 int ldb_register_samba_matching_rules(struct ldb_context *ldb)
450 struct ldb_extended_match_rule *transitive_eval = NULL,
451 *match_for_expunge = NULL;
452 int ret;
454 transitive_eval = talloc_zero(ldb, struct ldb_extended_match_rule);
455 transitive_eval->oid = SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL;
456 transitive_eval->callback = ldb_comparator_trans;
457 ret = ldb_register_extended_match_rule(ldb, transitive_eval);
458 if (ret != LDB_SUCCESS) {
459 talloc_free(transitive_eval);
460 return ret;
463 match_for_expunge = talloc_zero(ldb, struct ldb_extended_match_rule);
464 match_for_expunge->oid = DSDB_MATCH_FOR_EXPUNGE;
465 match_for_expunge->callback = dsdb_match_for_expunge;
466 ret = ldb_register_extended_match_rule(ldb, match_for_expunge);
467 if (ret != LDB_SUCCESS) {
468 talloc_free(match_for_expunge);
469 return ret;
472 return LDB_SUCCESS;