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/>.
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
,
30 const struct dsdb_dn
*dn_to_match
,
32 struct dsdb_dn
*to_visit
,
33 struct dsdb_dn
**visited
,
34 unsigned int *visited_count
,
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
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
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
) {
68 if (res
->count
!= 1) {
70 return LDB_ERR_OPERATIONS_ERROR
;
74 /* Fetch the attribute to match from the entry being visited */
75 el
= ldb_msg_find_element(msg
, attr
);
77 /* This entry does not have the attribute to match */
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
++) {
89 dn
= dsdb_dn_parse(tmp_ctx
, ldb
, &el
->values
[i
], dn_oid
);
93 return LDB_ERR_INVALID_DN_SYNTAX
;
96 if (ldb_dn_compare(dn_to_match
->dn
, dn
->dn
) == 0) {
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
109 if (visited
== NULL
) {
110 visited
= talloc_array(mem_ctx
, struct dsdb_dn
*, 1);
111 if (visited
== NULL
) {
112 talloc_free(tmp_ctx
);
113 return LDB_ERR_OPERATIONS_ERROR
;
115 visited
[0] = to_visit
;
116 (*visited_count
) = 1;
118 visited
= talloc_realloc(mem_ctx
, visited
, struct dsdb_dn
*,
119 (*visited_count
) + 1);
120 if (visited
== NULL
) {
121 talloc_free(tmp_ctx
);
122 return LDB_ERR_OPERATIONS_ERROR
;
124 visited
[(*visited_count
)] = to_visit
;
129 * steal to_visit into visited array context, as it has to live until
130 * the array is freed.
132 talloc_steal(visited
, to_visit
);
135 * Iterate over the values of the attribute of the entry being
136 * visited (to_visit) and follow them, calling this function
138 * If the value is in the visited array, skip it.
139 * Otherwise, follow the link and visit it.
141 for (i
=0; i
<el
->num_values
; i
++) {
142 struct dsdb_dn
*next_to_visit
;
145 next_to_visit
= dsdb_dn_parse(tmp_ctx
, ldb
, &el
->values
[i
], dn_oid
);
146 if (next_to_visit
== NULL
) {
147 talloc_free(tmp_ctx
);
149 return LDB_ERR_INVALID_DN_SYNTAX
;
153 * If the value is already in the visited array, skip it.
154 * Note the last element of the array is ignored because it is
155 * the current entry DN.
157 for (j
=0; j
< (*visited_count
) - 1; j
++) {
158 struct dsdb_dn
*visited_dn
= visited
[j
];
159 if (ldb_dn_compare(visited_dn
->dn
,
160 next_to_visit
->dn
) == 0) {
166 talloc_free(next_to_visit
);
170 /* If the value is not in the visited array, evaluate it */
171 ret
= ldb_eval_transitive_filter_helper(tmp_ctx
, ldb
, attr
,
174 visited
, visited_count
,
176 if (ret
!= LDB_SUCCESS
) {
177 talloc_free(tmp_ctx
);
181 talloc_free(tmp_ctx
);
186 talloc_free(tmp_ctx
);
192 * This function parses the linked attribute value to match, whose syntax
193 * will be one of the different DN syntaxes, into a ldb_dn struct.
195 static int ldb_eval_transitive_filter(TALLOC_CTX
*mem_ctx
,
196 struct ldb_context
*ldb
,
198 const struct ldb_val
*value_to_match
,
199 struct dsdb_dn
*current_object_dn
,
202 const struct dsdb_schema
*schema
;
203 const struct dsdb_attribute
*schema_attr
;
204 struct dsdb_dn
*dn_to_match
;
208 schema
= dsdb_get_schema(ldb
, mem_ctx
);
209 if (schema
== NULL
) {
210 return LDB_ERR_OPERATIONS_ERROR
;
213 schema_attr
= dsdb_attribute_by_lDAPDisplayName(schema
, attr
);
214 if (schema_attr
== NULL
) {
215 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
218 /* This is the DN syntax of the attribute being matched */
219 dn_oid
= schema_attr
->syntax
->ldap_oid
;
222 * Build a ldb_dn struct holding the value to match, which is the
223 * value entered in the search filter
225 dn_to_match
= dsdb_dn_parse(mem_ctx
, ldb
, value_to_match
, dn_oid
);
226 if (dn_to_match
== NULL
) {
228 return LDB_ERR_INVALID_DN_SYNTAX
;
231 return ldb_eval_transitive_filter_helper(mem_ctx
, ldb
, attr
,
234 NULL
, &count
, matched
);
238 * This rule provides recursive search of a link attribute
240 * Documented in [MS-ADTS] section 3.1.1.3.4.4.3 LDAP_MATCHING_RULE_TRANSITIVE_EVAL
241 * This allows a search filter such as:
243 * member:1.2.840.113556.1.4.1941:=cn=user,cn=users,dc=samba,dc=example,dc=com
245 * This searches not only the member attribute, but also any member
246 * attributes that point at an object with this member in them. All the
247 * various DN syntax types are supported, not just plain DNs.
250 static int ldb_comparator_trans(struct ldb_context
*ldb
,
252 const struct ldb_message
*msg
,
253 const char *attribute_to_match
,
254 const struct ldb_val
*value_to_match
,
257 const struct dsdb_schema
*schema
;
258 const struct dsdb_attribute
*schema_attr
;
259 struct ldb_dn
*msg_dn
;
260 struct dsdb_dn
*dsdb_msg_dn
;
264 tmp_ctx
= talloc_new(ldb
);
265 if (tmp_ctx
== NULL
) {
266 return LDB_ERR_OPERATIONS_ERROR
;
270 * If the target attribute to match is not a linked attribute, then
271 * the filter evaluates to undefined
273 schema
= dsdb_get_schema(ldb
, tmp_ctx
);
274 if (schema
== NULL
) {
275 talloc_free(tmp_ctx
);
276 return LDB_ERR_OPERATIONS_ERROR
;
279 schema_attr
= dsdb_attribute_by_lDAPDisplayName(schema
, attribute_to_match
);
280 if (schema_attr
== NULL
) {
281 talloc_free(tmp_ctx
);
282 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
286 * This extended match filter is only valid for linked attributes,
287 * following the MS definition (the schema attribute has a linkID
288 * defined). See dochelp request 114111212024789 on cifs-protocols
291 if (schema_attr
->linkID
== 0) {
292 talloc_free(tmp_ctx
);
293 return LDB_ERR_INAPPROPRIATE_MATCHING
;
296 /* Duplicate original msg dn as the msg must not be modified */
297 msg_dn
= ldb_dn_copy(tmp_ctx
, msg
->dn
);
298 if (msg_dn
== NULL
) {
299 talloc_free(tmp_ctx
);
300 return LDB_ERR_OPERATIONS_ERROR
;
304 * Build a dsdb dn from the message copied DN, which should be a plain
307 dsdb_msg_dn
= dsdb_dn_construct(tmp_ctx
, msg_dn
, data_blob_null
,
309 if (dsdb_msg_dn
== NULL
) {
311 return LDB_ERR_INVALID_DN_SYNTAX
;
314 ret
= ldb_eval_transitive_filter(tmp_ctx
, ldb
,
317 dsdb_msg_dn
, matched
);
318 talloc_free(tmp_ctx
);
323 int ldb_register_samba_matching_rules(struct ldb_context
*ldb
)
325 struct ldb_extended_match_rule
*transitive_eval
;
328 transitive_eval
= talloc_zero(ldb
, struct ldb_extended_match_rule
);
329 transitive_eval
->oid
= SAMBA_LDAP_MATCH_RULE_TRANSITIVE_EVAL
;
330 transitive_eval
->callback
= ldb_comparator_trans
;
331 ret
= ldb_register_extended_match_rule(ldb
, transitive_eval
);
332 if (ret
!= LDB_SUCCESS
) {
333 talloc_free(transitive_eval
);