tests: Replace realpath with the more available 'readlink -f'
[Samba.git] / lib / ldb-samba / ldb_matching_rules.c
blob1692a73f5348e771df24329dcd3658a525c80279
1 /*
2 Unix SMB/CIFS implementation.
4 ldb database library - Extended match rules
6 Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include <ldb_module.h>
24 #include "dsdb/samdb/samdb.h"
25 #include "ldb_matching_rules.h"
27 static int ldb_eval_transitive_filter_helper(TALLOC_CTX *mem_ctx,
28 struct ldb_context *ldb,
29 const char *attr,
30 const struct dsdb_dn *dn_to_match,
31 const char *dn_oid,
32 struct dsdb_dn *to_visit,
33 struct dsdb_dn ***visited,
34 unsigned int *visited_count,
35 bool *matched)
37 TALLOC_CTX *tmp_ctx;
38 int ret, i, j;
39 struct ldb_result *res;
40 struct ldb_message *msg;
41 struct ldb_message_element *el;
42 const char *attrs[] = { attr, NULL };
44 tmp_ctx = talloc_new(mem_ctx);
45 if (tmp_ctx == NULL) {
46 return LDB_ERR_OPERATIONS_ERROR;
50 * Fetch the entry to_visit
52 * NOTE: This is a new LDB search from the TOP of the module
53 * stack. This means that this search runs the whole stack
54 * from top to bottom.
56 * This may seem to be in-efficient, but it is also the only
57 * way to ensure that the ACLs for this search are applied
58 * correctly.
60 * Note also that we don't have the original request
61 * here, so we can not apply controls or timeouts here.
63 ret = dsdb_search_dn(ldb, tmp_ctx, &res, to_visit->dn, attrs, 0);
64 if (ret != LDB_SUCCESS) {
65 talloc_free(tmp_ctx);
66 return ret;
68 if (res->count != 1) {
69 talloc_free(tmp_ctx);
70 return LDB_ERR_OPERATIONS_ERROR;
72 msg = res->msgs[0];
74 /* Fetch the attribute to match from the entry being visited */
75 el = ldb_msg_find_element(msg, attr);
76 if (el == NULL) {
77 /* This entry does not have the attribute to match */
78 talloc_free(tmp_ctx);
79 *matched = false;
80 return LDB_SUCCESS;
84 * If the value to match is present in the attribute values of the
85 * current entry being visited, set matched to true and return OK
87 for (i=0; i<el->num_values; i++) {
88 struct dsdb_dn *dn;
89 dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
90 if (dn == NULL) {
91 talloc_free(tmp_ctx);
92 *matched = false;
93 return LDB_ERR_INVALID_DN_SYNTAX;
96 if (ldb_dn_compare(dn_to_match->dn, dn->dn) == 0) {
97 talloc_free(tmp_ctx);
98 *matched = true;
99 return LDB_SUCCESS;
104 * If arrived here, the value to match is not in the values of the
105 * entry being visited. Add the entry being visited (to_visit)
106 * to the visited array. The array is (re)allocated in the parent
107 * memory context.
109 if (visited == NULL) {
110 return LDB_ERR_OPERATIONS_ERROR;
111 } else if (*visited == NULL) {
112 *visited = talloc_array(mem_ctx, struct dsdb_dn *, 1);
113 if (*visited == NULL) {
114 talloc_free(tmp_ctx);
115 return LDB_ERR_OPERATIONS_ERROR;
117 (*visited)[0] = to_visit;
118 (*visited_count) = 1;
119 } else {
120 *visited = talloc_realloc(mem_ctx, *visited, struct dsdb_dn *,
121 (*visited_count) + 1);
122 if (*visited == NULL) {
123 talloc_free(tmp_ctx);
124 return LDB_ERR_OPERATIONS_ERROR;
126 (*visited)[(*visited_count)] = to_visit;
127 (*visited_count)++;
131 * steal to_visit into visited array context, as it has to live until
132 * the array is freed.
134 talloc_steal(*visited, to_visit);
137 * Iterate over the values of the attribute of the entry being
138 * visited (to_visit) and follow them, calling this function
139 * recursively.
140 * If the value is in the visited array, skip it.
141 * Otherwise, follow the link and visit it.
143 for (i=0; i<el->num_values; i++) {
144 struct dsdb_dn *next_to_visit;
145 bool skip = false;
147 next_to_visit = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], dn_oid);
148 if (next_to_visit == NULL) {
149 talloc_free(tmp_ctx);
150 *matched = false;
151 return LDB_ERR_INVALID_DN_SYNTAX;
155 * If the value is already in the visited array, skip it.
156 * Note the last element of the array is ignored because it is
157 * the current entry DN.
159 for (j=0; j < (*visited_count) - 1; j++) {
160 struct dsdb_dn *visited_dn = (*visited)[j];
161 if (ldb_dn_compare(visited_dn->dn,
162 next_to_visit->dn) == 0) {
163 skip = true;
164 break;
167 if (skip) {
168 talloc_free(next_to_visit);
169 continue;
172 /* If the value is not in the visited array, evaluate it */
173 ret = ldb_eval_transitive_filter_helper(tmp_ctx, ldb, attr,
174 dn_to_match, dn_oid,
175 next_to_visit,
176 visited, visited_count,
177 matched);
178 if (ret != LDB_SUCCESS) {
179 talloc_free(tmp_ctx);
180 return ret;
182 if (*matched) {
183 talloc_free(tmp_ctx);
184 return LDB_SUCCESS;
188 talloc_free(tmp_ctx);
189 *matched = false;
190 return LDB_SUCCESS;
194 * This function parses the linked attribute value to match, whose syntax
195 * will be one of the different DN syntaxes, into a ldb_dn struct.
197 static int ldb_eval_transitive_filter(TALLOC_CTX *mem_ctx,
198 struct ldb_context *ldb,
199 const char *attr,
200 const struct ldb_val *value_to_match,
201 struct dsdb_dn *current_object_dn,
202 bool *matched)
204 const struct dsdb_schema *schema;
205 const struct dsdb_attribute *schema_attr;
206 struct dsdb_dn *dn_to_match;
207 const char *dn_oid;
208 unsigned int count;
209 struct dsdb_dn **visited;
211 schema = dsdb_get_schema(ldb, mem_ctx);
212 if (schema == NULL) {
213 return LDB_ERR_OPERATIONS_ERROR;
216 schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attr);
217 if (schema_attr == NULL) {
218 return LDB_ERR_NO_SUCH_ATTRIBUTE;
221 /* This is the DN syntax of the attribute being matched */
222 dn_oid = schema_attr->syntax->ldap_oid;
225 * Build a ldb_dn struct holding the value to match, which is the
226 * value entered in the search filter
228 dn_to_match = dsdb_dn_parse(mem_ctx, ldb, value_to_match, dn_oid);
229 if (dn_to_match == NULL) {
230 *matched = false;
231 return LDB_SUCCESS;
234 return ldb_eval_transitive_filter_helper(mem_ctx, ldb, attr,
235 dn_to_match, dn_oid,
236 current_object_dn,
237 &visited, &count, matched);
241 * This rule provides recursive search of a link attribute
243 * Documented in [MS-ADTS] section 3.1.1.3.4.4.3 LDAP_MATCHING_RULE_TRANSITIVE_EVAL
244 * This allows a search filter such as:
246 * member:1.2.840.113556.1.4.1941:=cn=user,cn=users,dc=samba,dc=example,dc=com
248 * This searches not only the member attribute, but also any member
249 * attributes that point at an object with this member in them. All the
250 * various DN syntax types are supported, not just plain DNs.
253 static int ldb_comparator_trans(struct ldb_context *ldb,
254 const char *oid,
255 const struct ldb_message *msg,
256 const char *attribute_to_match,
257 const struct ldb_val *value_to_match,
258 bool *matched)
260 const struct dsdb_schema *schema;
261 const struct dsdb_attribute *schema_attr;
262 struct ldb_dn *msg_dn;
263 struct dsdb_dn *dsdb_msg_dn;
264 TALLOC_CTX *tmp_ctx;
265 int ret;
267 tmp_ctx = talloc_new(ldb);
268 if (tmp_ctx == NULL) {
269 return LDB_ERR_OPERATIONS_ERROR;
273 * If the target attribute to match is not a linked attribute, then
274 * the filter evaluates to undefined
276 schema = dsdb_get_schema(ldb, tmp_ctx);
277 if (schema == NULL) {
278 talloc_free(tmp_ctx);
279 return LDB_ERR_OPERATIONS_ERROR;
282 schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attribute_to_match);
283 if (schema_attr == NULL) {
284 talloc_free(tmp_ctx);
285 return LDB_ERR_NO_SUCH_ATTRIBUTE;
289 * This extended match filter is only valid for linked attributes,
290 * following the MS definition (the schema attribute has a linkID
291 * defined). See dochelp request 114111212024789 on cifs-protocols
292 * mailing list.
294 if (schema_attr->linkID == 0) {
295 *matched = false;
296 talloc_free(tmp_ctx);
297 return LDB_SUCCESS;
300 /* Duplicate original msg dn as the msg must not be modified */
301 msg_dn = ldb_dn_copy(tmp_ctx, msg->dn);
302 if (msg_dn == NULL) {
303 talloc_free(tmp_ctx);
304 return LDB_ERR_OPERATIONS_ERROR;
308 * Build a dsdb dn from the message copied DN, which should be a plain
309 * DN syntax.
311 dsdb_msg_dn = dsdb_dn_construct(tmp_ctx, msg_dn, data_blob_null,
312 LDB_SYNTAX_DN);
313 if (dsdb_msg_dn == NULL) {
314 *matched = false;
315 return LDB_ERR_INVALID_DN_SYNTAX;
318 ret = ldb_eval_transitive_filter(tmp_ctx, ldb,
319 attribute_to_match,
320 value_to_match,
321 dsdb_msg_dn, matched);
322 talloc_free(tmp_ctx);
323 return ret;
327 int ldb_register_samba_matching_rules(struct ldb_context *ldb)
329 struct ldb_extended_match_rule *transitive_eval;
330 int ret;
332 transitive_eval = talloc_zero(ldb, struct ldb_extended_match_rule);
333 transitive_eval->oid = SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL;
334 transitive_eval->callback = ldb_comparator_trans;
335 ret = ldb_register_extended_match_rule(ldb, transitive_eval);
336 if (ret != LDB_SUCCESS) {
337 talloc_free(transitive_eval);
338 return ret;
341 return LDB_SUCCESS;