lib: crypt: Prepare the existing code to switch to Intel AES hardware instructions.
[vlendec/samba-autobuild/.git] / lib / ldb-samba / ldb_matching_rules.c
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
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)
39 {
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 };
46
47         tmp_ctx = talloc_new(mem_ctx);
48         if (tmp_ctx == NULL) {
49                 return LDB_ERR_OPERATIONS_ERROR;
50         }
51
52         /*
53          * Fetch the entry to_visit
54          *
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.
58          *
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.
62          *
63          * Note also that we don't have the original request
64          * here, so we can not apply controls or timeouts here.
65          */
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;
70         }
71         if (res->count != 1) {
72                 talloc_free(tmp_ctx);
73                 return LDB_ERR_OPERATIONS_ERROR;
74         }
75         msg = res->msgs[0];
76
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;
84         }
85
86         /*
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
89          */
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;
97                 }
98
99                 if (ldb_dn_compare(dn_to_match->dn, dn->dn) == 0) {
100                         talloc_free(tmp_ctx);
101                         *matched = true;
102                         return LDB_SUCCESS;
103                 }
104         }
105
106         /*
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.
111          */
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;
119                 }
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;
128                 }
129                 (*visited)[(*visited_count)] = to_visit;
130                 (*visited_count)++;
131         }
132
133         /*
134          * steal to_visit into visited array context, as it has to live until
135          * the array is freed.
136          */
137         talloc_steal(*visited, to_visit);
138
139         /*
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.
145          */
146         for (i=0; i<el->num_values; i++) {
147                 struct dsdb_dn *next_to_visit;
148                 bool skip = false;
149
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;
155                 }
156
157                 /*
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.
161                  */
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;
168                         }
169                 }
170                 if (skip) {
171                         talloc_free(next_to_visit);
172                         continue;
173                 }
174
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;
184                 }
185                 if (*matched) {
186                         talloc_free(tmp_ctx);
187                         return LDB_SUCCESS;
188                 }
189         }
190
191         talloc_free(tmp_ctx);
192         *matched = false;
193         return LDB_SUCCESS;
194 }
195
196 /*
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.
199  */
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)
206 {
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;
213
214         schema = dsdb_get_schema(ldb, mem_ctx);
215         if (schema == NULL) {
216                 return LDB_ERR_OPERATIONS_ERROR;
217         }
218
219         schema_attr = dsdb_attribute_by_lDAPDisplayName(schema, attr);
220         if (schema_attr == NULL) {
221                 return LDB_ERR_NO_SUCH_ATTRIBUTE;
222         }
223
224         /* This is the DN syntax of the attribute being matched */
225         dn_oid = schema_attr->syntax->ldap_oid;
226
227         /*
228          * Build a ldb_dn struct holding the value to match, which is the
229          * value entered in the search filter
230          */
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;
235         }
236
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);
241 }
242
243 /*
244  * This rule provides recursive search of a link attribute
245  *
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:
248  *
249  * member:1.2.840.113556.1.4.1941:=cn=user,cn=users,dc=samba,dc=example,dc=com
250  *
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.
254  *
255  */
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)
262 {
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;
269
270         tmp_ctx = talloc_new(ldb);
271         if (tmp_ctx == NULL) {
272                 return LDB_ERR_OPERATIONS_ERROR;
273         }
274
275         /*
276          * If the target attribute to match is not a linked attribute, then
277          * the filter evaluates to undefined
278          */
279         schema = dsdb_get_schema(ldb, tmp_ctx);
280         if (schema == NULL) {
281                 talloc_free(tmp_ctx);
282                 return LDB_ERR_OPERATIONS_ERROR;
283         }
284
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;
289         }
290
291         /*
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.
296          */
297         if (schema_attr->linkID == 0) {
298                 *matched = false;
299                 talloc_free(tmp_ctx);
300                 return LDB_SUCCESS;
301         }
302
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;
308         }
309
310         /*
311          * Build a dsdb dn from the message copied DN, which should be a plain
312          * DN syntax.
313          */
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;
319         }
320
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;
327 }
328
329
330 /*
331  * This rule provides match of a link attribute against a 'should be expunged' criteria
332  *
333  * This allows a search filter such as:
334  *
335  * member:1.3.6.1.4.1.7165.4.5.2:=131139216000000000
336  *
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.
340  *
341  */
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)
348 {
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;
357
358         el = ldb_msg_find_element(msg, attribute_to_match);
359         if (el == NULL) {
360                 return LDB_SUCCESS;
361         }
362
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;
368         }
369
370         /*
371          * If the target attribute to match is not a linked attribute, then
372          * the filter evaluates to undefined
373          */
374         schema = dsdb_get_schema(ldb, NULL);
375         if (schema == NULL) {
376                 return LDB_ERR_OPERATIONS_ERROR;
377         }
378
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;
383         }
384
385         /*
386          * This extended match filter is only valid for forward linked attributes.
387          */
388         if (schema_attr->linkID == 0 || (schema_attr->linkID & 1) == 1) {
389                 return LDB_ERR_NO_SUCH_ATTRIBUTE;
390         }
391
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;
402                 }
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;
406                 }
407         }
408
409         tmp_ctx = talloc_new(ldb);
410         if (tmp_ctx == NULL) {
411                 return LDB_ERR_OPERATIONS_ERROR;
412         }
413
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;
420                 }
421
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;
427                 }
428
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;
434                 }
435
436                 if (rmd_changetime > tombstone_time) {
437                         continue;
438                 }
439
440                 *matched = true;
441                 break;
442         }
443         talloc_free(tmp_ctx);
444         return LDB_SUCCESS;
445 }
446
447
448 int ldb_register_samba_matching_rules(struct ldb_context *ldb)
449 {
450         struct ldb_extended_match_rule *transitive_eval = NULL,
451                 *match_for_expunge = NULL;
452         int ret;
453
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;
461         }
462
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;
470         }
471
472         return LDB_SUCCESS;
473 }