04b746108983d1bf3aa66fa7dc097729da383c3e
[ddiss/samba.git] / source4 / dsdb / samdb / ldb_modules / operational.c
1 /*
2    ldb database library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2010
5    Copyright (C) Andrew Tridgell 2005
6    Copyright (C) Simo Sorce 2006-2008
7    Copyright (C) Matthias Dieter Wallnöfer 2009
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 /*
24   handle operational attributes
25  */
26
27 /*
28   createTimeStamp: HIDDEN, searchable, ldaptime, alias for whenCreated
29   modifyTimeStamp: HIDDEN, searchable, ldaptime, alias for whenChanged
30
31      for the above two, we do the search as normal, and if
32      createTimeStamp or modifyTimeStamp is asked for, then do
33      additional searches for whenCreated and whenChanged and fill in
34      the resulting values
35
36      we also need to replace these with the whenCreated/whenChanged
37      equivalent in the search expression trees
38
39   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
40   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
41
42      on init we need to setup attribute handlers for these so
43      comparisons are done correctly. The resolution is 1 second.
44
45      on add we need to add both the above, for current time
46
47      on modify we need to change whenChanged
48
49   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
50
51      for this one we do the search as normal, then if requested ask
52      for objectclass, change the attribute name, and add it
53
54   primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
55
56      contains the RID of a certain group object
57     
58
59   attributeTypes: in schema only
60   objectClasses: in schema only
61   matchingRules: in schema only
62   matchingRuleUse: in schema only
63   creatorsName: not supported by w2k3?
64   modifiersName: not supported by w2k3?
65 */
66
67 #include "includes.h"
68 #include <ldb.h>
69 #include <ldb_module.h>
70
71 #include "librpc/gen_ndr/ndr_misc.h"
72 #include "librpc/gen_ndr/ndr_drsblobs.h"
73 #include "param/param.h"
74 #include "dsdb/samdb/samdb.h"
75 #include "dsdb/samdb/ldb_modules/util.h"
76
77 #include "libcli/security/security.h"
78
79 #ifndef ARRAY_SIZE
80 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
81 #endif
82
83 struct operational_data {
84         struct ldb_dn *aggregate_dn;
85 };
86
87 /*
88   construct a canonical name from a message
89 */
90 static int construct_canonical_name(struct ldb_module *module,
91                                     struct ldb_message *msg, enum ldb_scope scope,
92                                     struct ldb_request *parent)
93 {
94         char *canonicalName;
95         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
96         if (canonicalName == NULL) {
97                 return ldb_operr(ldb_module_get_ctx(module));
98         }
99         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
100 }
101
102 /*
103   construct a primary group token for groups from a message
104 */
105 static int construct_primary_group_token(struct ldb_module *module,
106                                          struct ldb_message *msg, enum ldb_scope scope,
107                                          struct ldb_request *parent)
108 {
109         struct ldb_context *ldb;
110         uint32_t primary_group_token;
111         
112         ldb = ldb_module_get_ctx(module);
113         if (ldb_match_msg_objectclass(msg, "group") == 1) {
114                 primary_group_token
115                         = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
116                 if (primary_group_token == 0) {
117                         return LDB_SUCCESS;
118                 }
119
120                 return samdb_msg_add_uint(ldb, msg, msg, "primaryGroupToken",
121                         primary_group_token);
122         } else {
123                 return LDB_SUCCESS;
124         }
125 }
126
127 /*
128   construct the token groups for SAM objects from a message
129 */
130 static int construct_token_groups(struct ldb_module *module,
131                                   struct ldb_message *msg, enum ldb_scope scope,
132                                   struct ldb_request *parent)
133 {
134         struct ldb_context *ldb = ldb_module_get_ctx(module);;
135         TALLOC_CTX *tmp_ctx = talloc_new(msg);
136         unsigned int i;
137         int ret;
138         const char *filter;
139
140         NTSTATUS status;
141
142         struct dom_sid *primary_group_sid;
143         const char *primary_group_string;
144         const char *primary_group_dn;
145         DATA_BLOB primary_group_blob;
146
147         struct dom_sid *account_sid;
148         const char *account_sid_string;
149         const char *account_sid_dn;
150         DATA_BLOB account_sid_blob;
151         struct dom_sid *groupSIDs = NULL;
152         unsigned int num_groupSIDs = 0;
153
154         struct dom_sid *domain_sid;
155
156         if (scope != LDB_SCOPE_BASE) {
157                 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
158                 return LDB_ERR_OPERATIONS_ERROR;
159         }
160
161         /* If it's not a user, it won't have a primaryGroupID */
162         if (ldb_msg_find_element(msg, "primaryGroupID") == NULL) {
163                 talloc_free(tmp_ctx);
164                 return LDB_SUCCESS;
165         }
166
167         /* Ensure it has an objectSID too */
168         account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
169         if (account_sid == NULL) {
170                 talloc_free(tmp_ctx);
171                 return LDB_SUCCESS;
172         }
173
174         status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
175         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
176                 talloc_free(tmp_ctx);
177                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
178         } else if (!NT_STATUS_IS_OK(status)) {
179                 talloc_free(tmp_ctx);
180                 return LDB_ERR_OPERATIONS_ERROR;
181         }
182
183         primary_group_sid = dom_sid_add_rid(tmp_ctx,
184                                             domain_sid,
185                                             ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
186         if (!primary_group_sid) {
187                 talloc_free(tmp_ctx);
188                 return ldb_oom(ldb);
189         }
190
191         /* only return security groups */
192         filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u))",
193                                  GROUP_TYPE_SECURITY_ENABLED);
194         if (!filter) {
195                 talloc_free(tmp_ctx);
196                 return ldb_oom(ldb);
197         }
198
199         primary_group_string = dom_sid_string(tmp_ctx, primary_group_sid);
200         if (!primary_group_string) {
201                 talloc_free(tmp_ctx);
202                 return ldb_oom(ldb);
203         }
204
205         primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string);
206         if (!primary_group_dn) {
207                 talloc_free(tmp_ctx);
208                 return ldb_oom(ldb);
209         }
210
211         primary_group_blob = data_blob_string_const(primary_group_dn);
212
213         account_sid_string = dom_sid_string(tmp_ctx, account_sid);
214         if (!account_sid_string) {
215                 talloc_free(tmp_ctx);
216                 return ldb_oom(ldb);
217         }
218
219         account_sid_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", account_sid_string);
220         if (!account_sid_dn) {
221                 talloc_free(tmp_ctx);
222                 return ldb_oom(ldb);
223         }
224
225         account_sid_blob = data_blob_string_const(account_sid_dn);
226
227         status = dsdb_expand_nested_groups(ldb, &account_sid_blob,
228                                            true, /* We don't want to add the object's SID itself,
229                                                     it's not returend in this attribute */
230                                            filter,
231                                            tmp_ctx, &groupSIDs, &num_groupSIDs);
232
233         if (!NT_STATUS_IS_OK(status)) {
234                 ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s",
235                                        account_sid_string, nt_errstr(status));
236                 talloc_free(tmp_ctx);
237                 return LDB_ERR_OPERATIONS_ERROR;
238         }
239
240         /* Expands the primary group - this function takes in
241          * memberOf-like values, so we fake one up with the
242          * <SID=S-...> format of DN and then let it expand
243          * them, as long as they meet the filter - so only
244          * domain groups, not builtin groups
245          */
246         status = dsdb_expand_nested_groups(ldb, &primary_group_blob, false, filter,
247                                            tmp_ctx, &groupSIDs, &num_groupSIDs);
248         if (!NT_STATUS_IS_OK(status)) {
249                 ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s",
250                                        account_sid_string, nt_errstr(status));
251                 talloc_free(tmp_ctx);
252                 return LDB_ERR_OPERATIONS_ERROR;
253         }
254
255         for (i=0; i < num_groupSIDs; i++) {
256                 ret = samdb_msg_add_dom_sid(ldb, msg, msg, "tokenGroups", &groupSIDs[i]);
257                 if (ret) {
258                         talloc_free(tmp_ctx);
259                         return ret;
260                 }
261         }
262
263         return LDB_SUCCESS;
264 }
265
266 /*
267   construct the parent GUID for an entry from a message
268 */
269 static int construct_parent_guid(struct ldb_module *module,
270                                  struct ldb_message *msg, enum ldb_scope scope,
271                                  struct ldb_request *parent)
272 {
273         struct ldb_result *res, *parent_res;
274         const struct ldb_val *parent_guid;
275         const char *attrs[] = { "instanceType", NULL };
276         const char *attrs2[] = { "objectGUID", NULL };
277         uint32_t instanceType;
278         int ret;
279         struct ldb_dn *parent_dn;
280         struct ldb_val v;
281
282         /* determine if the object is NC by instance type */
283         ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
284                                     DSDB_FLAG_NEXT_MODULE |
285                                     DSDB_SEARCH_SHOW_RECYCLED, parent);
286         if (ret != LDB_SUCCESS) {
287                 return ret;
288         }
289
290         instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
291                                                  "instanceType", 0);
292         talloc_free(res);
293         if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
294                 DEBUG(4,(__location__ ": Object %s is NC\n",
295                          ldb_dn_get_linearized(msg->dn)));
296                 return LDB_SUCCESS;
297         }
298         parent_dn = ldb_dn_get_parent(msg, msg->dn);
299
300         if (parent_dn == NULL) {
301                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
302                                          ldb_dn_get_linearized(msg->dn)));
303                 return LDB_SUCCESS;
304         }
305         ret = dsdb_module_search_dn(module, msg, &parent_res, parent_dn, attrs2,
306                                     DSDB_FLAG_NEXT_MODULE |
307                                     DSDB_SEARCH_SHOW_RECYCLED, parent);
308         talloc_free(parent_dn);
309
310         /* not NC, so the object should have a parent*/
311         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
312                 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
313                          ldb_dn_get_linearized(msg->dn)));
314                 return ldb_operr(ldb_module_get_ctx(module));
315         } else if (ret != LDB_SUCCESS) {
316                 return ret;
317         }
318
319         parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
320         if (!parent_guid) {
321                 talloc_free(parent_res);
322                 return LDB_SUCCESS;
323         }
324
325         v = data_blob_dup_talloc(parent_res, *parent_guid);
326         if (!v.data) {
327                 talloc_free(parent_res);
328                 return ldb_oom(ldb_module_get_ctx(module));
329         }
330         ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
331         talloc_free(parent_res);
332         return ret;
333 }
334
335 /*
336   construct a subSchemaSubEntry
337 */
338 static int construct_subschema_subentry(struct ldb_module *module,
339                                         struct ldb_message *msg, enum ldb_scope scope,
340                                         struct ldb_request *parent)
341 {
342         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
343         char *subSchemaSubEntry;
344
345         /* We may be being called before the init function has finished */
346         if (!data) {
347                 return LDB_SUCCESS;
348         }
349
350         /* Try and set this value up, if possible.  Don't worry if it
351          * fails, we may not have the DB set up yet, and it's not
352          * really vital anyway */
353         if (!data->aggregate_dn) {
354                 struct ldb_context *ldb = ldb_module_get_ctx(module);
355                 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
356         }
357
358         if (data->aggregate_dn) {
359                 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
360                 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
361         }
362         return LDB_SUCCESS;
363 }
364
365
366 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
367                                          struct ldb_message *msg,
368                                          struct ldb_message_element *object_category)
369 {
370         struct ldb_context *ldb;
371         struct ldb_dn *dn;
372         const struct ldb_val *val;
373
374         ldb = ldb_module_get_ctx(module);
375         if (!ldb) {
376                 DEBUG(4, (__location__ ": Failed to get ldb \n"));
377                 return ldb_operr(ldb);
378         }
379
380         dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
381         if (!dn) {
382                 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
383                           (const char *)object_category->values[0].data));
384                 return ldb_operr(ldb);
385         }
386
387         val = ldb_dn_get_rdn_val(dn);
388         if (!val) {
389                 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
390                           ldb_dn_get_linearized(dn)));
391                 return ldb_operr(ldb);
392         }
393
394         if (strequal((const char *)val->data, "NTDS-DSA")) {
395                 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
396         } else {
397                 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
398         }
399         return LDB_SUCCESS;
400 }
401
402 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
403                                                 struct ldb_message *msg,
404                                                 struct ldb_dn *dn,
405                                                 struct ldb_request *parent)
406 {
407         struct ldb_dn *server_dn;
408         const char *attr_obj_cat[] = { "objectCategory", NULL };
409         struct ldb_result *res;
410         struct ldb_message_element *object_category;
411         int ret;
412
413         server_dn = ldb_dn_copy(msg, dn);
414         if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
415                 DEBUG(4, (__location__ ": Failed to add child to %s \n",
416                           ldb_dn_get_linearized(server_dn)));
417                 return ldb_operr(ldb_module_get_ctx(module));
418         }
419
420         ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
421                                     DSDB_FLAG_NEXT_MODULE, parent);
422         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
423                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
424                                          ldb_dn_get_linearized(server_dn)));
425                 return LDB_SUCCESS;
426         } else if (ret != LDB_SUCCESS) {
427                 return ret;
428         }
429
430         object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
431         if (!object_category) {
432                 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
433                          ldb_dn_get_linearized(res->msgs[0]->dn)));
434                 return LDB_SUCCESS;
435         }
436         return construct_msds_isrodc_with_dn(module, msg, object_category);
437 }
438
439 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
440                                                   struct ldb_message *msg,
441                                                   struct ldb_request *parent)
442 {
443         int ret;
444         struct ldb_dn *server_dn;
445
446         ret = dsdb_module_reference_dn(module, msg, msg->dn, "serverReferenceBL",
447                                        &server_dn, parent);
448         if (ret == LDB_ERR_NO_SUCH_OBJECT || ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
449                 /* it's OK if we can't find serverReferenceBL attribute */
450                 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
451                          ldb_dn_get_linearized(msg->dn)));
452                 return LDB_SUCCESS;
453         } else if (ret != LDB_SUCCESS) {
454                 return ret;
455         }
456
457         return construct_msds_isrodc_with_server_dn(module, msg, server_dn, parent);
458 }
459
460 /*
461   construct msDS-isRODC attr
462 */
463 static int construct_msds_isrodc(struct ldb_module *module,
464                                  struct ldb_message *msg, enum ldb_scope scope,
465                                  struct ldb_request *parent)
466 {
467         struct ldb_message_element * object_class;
468         struct ldb_message_element * object_category;
469         unsigned int i;
470
471         object_class = ldb_msg_find_element(msg, "objectClass");
472         if (!object_class) {
473                 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
474                          ldb_dn_get_linearized(msg->dn)));
475                 return ldb_operr(ldb_module_get_ctx(module));
476         }
477
478         for (i=0; i<object_class->num_values; i++) {
479                 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
480                         /* If TO!objectCategory  equals the DN of the classSchema  object for the nTDSDSA
481                          * object class, then TO!msDS-isRODC  is false. Otherwise, TO!msDS-isRODC  is true.
482                          */
483                         object_category = ldb_msg_find_element(msg, "objectCategory");
484                         if (!object_category) {
485                                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
486                                          ldb_dn_get_linearized(msg->dn)));
487                                 return LDB_SUCCESS;
488                         }
489                         return construct_msds_isrodc_with_dn(module, msg, object_category);
490                 }
491                 if (strequal((const char*)object_class->values[i].data, "server")) {
492                         /* Let TN be the nTDSDSA  object whose DN is "CN=NTDS Settings," prepended to
493                          * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA  object" case,
494                          * substituting TN for TO.
495                          */
496                         return construct_msds_isrodc_with_server_dn(module, msg, msg->dn, parent);
497                 }
498                 if (strequal((const char*)object_class->values[i].data, "computer")) {
499                         /* Let TS be the server  object named by TO!serverReferenceBL. Apply the previous
500                          * rule for the "TO is a server  object" case, substituting TS for TO.
501                          */
502                         return construct_msds_isrodc_with_computer_dn(module, msg, parent);
503                 }
504         }
505
506         return LDB_SUCCESS;
507 }
508
509
510 /*
511   construct msDS-keyVersionNumber attr
512
513   TODO:  Make this based on the 'win2k' DS huristics bit...
514
515 */
516 static int construct_msds_keyversionnumber(struct ldb_module *module,
517                                            struct ldb_message *msg,
518                                            enum ldb_scope scope,
519                                            struct ldb_request *parent)
520 {
521         uint32_t i;
522         enum ndr_err_code ndr_err;
523         const struct ldb_val *omd_value;
524         struct replPropertyMetaDataBlob *omd;
525         int ret;
526
527         omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
528         if (!omd_value) {
529                 /* We can't make up a key version number without meta data */
530                 return LDB_SUCCESS;
531         }
532         if (!omd_value) {
533                 return LDB_SUCCESS;
534         }
535
536         omd = talloc(msg, struct replPropertyMetaDataBlob);
537         if (!omd) {
538                 ldb_module_oom(module);
539                 return LDB_SUCCESS;
540         }
541
542         ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
543                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
544         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
545                 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
546                          ldb_dn_get_linearized(msg->dn)));
547                 return ldb_operr(ldb_module_get_ctx(module));
548         }
549
550         if (omd->version != 1) {
551                 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
552                          omd->version, ldb_dn_get_linearized(msg->dn)));
553                 talloc_free(omd);
554                 return LDB_SUCCESS;
555         }
556         for (i=0; i<omd->ctr.ctr1.count; i++) {
557                 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
558                         ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
559                                                  msg, msg,
560                                                  "msDS-KeyVersionNumber",
561                                                  omd->ctr.ctr1.array[i].version);
562                         if (ret != LDB_SUCCESS) {
563                                 talloc_free(omd);
564                                 return ret;
565                         }
566                         break;
567                 }
568         }
569         return LDB_SUCCESS;
570
571 }
572
573 struct op_controls_flags {
574         bool sd;
575         bool bypassoperational;
576 };
577
578 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
579         if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
580                 return true;
581         }
582         return false;
583 }
584
585 /*
586   a list of attribute names that should be substituted in the parse
587   tree before the search is done
588 */
589 static const struct {
590         const char *attr;
591         const char *replace;
592 } parse_tree_sub[] = {
593         { "createTimeStamp", "whenCreated" },
594         { "modifyTimeStamp", "whenChanged" }
595 };
596
597
598 /*
599   a list of attribute names that are hidden, but can be searched for
600   using another (non-hidden) name to produce the correct result
601 */
602 static const struct {
603         const char *attr;
604         const char *replace;
605         const char *extra_attr;
606         int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope, struct ldb_request *);
607 } search_sub[] = {
608         { "createTimeStamp", "whenCreated", NULL , NULL },
609         { "modifyTimeStamp", "whenChanged", NULL , NULL },
610         { "structuralObjectClass", "objectClass", NULL , NULL },
611         { "canonicalName", NULL, NULL , construct_canonical_name },
612         { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
613         { "tokenGroups", "primaryGroupID", "objectSid", construct_token_groups },
614         { "parentGUID", NULL, NULL, construct_parent_guid },
615         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
616         { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
617         { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
618 };
619
620
621 enum op_remove {
622         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
623         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
624         OPERATIONAL_SD_FLAGS,      /* show if SD_FLAGS_OID set, or asked for */
625         OPERATIONAL_REMOVE_UNLESS_CONTROL        /* remove always unless an adhoc control has been specified */
626 };
627
628 /*
629   a list of attributes that may need to be removed from the
630   underlying db return
631
632   Some of these are attributes that were once stored, but are now calculated
633 */
634 static const struct {
635         const char *attr;
636         enum op_remove op;
637 } operational_remove[] = {
638         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
639         { "msDS-KeyVersionNumber",   OPERATIONAL_REMOVE_UNLESS_CONTROL  },
640         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
641         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
642 #define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
643         { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
644 };
645
646
647 /*
648   post process a search result record. For any search_sub[] attributes that were
649   asked for, we need to call the appropriate copy routine to copy the result
650   into the message, then remove any attributes that we added to the search but
651   were not asked for by the user
652 */
653 static int operational_search_post_process(struct ldb_module *module,
654                                            struct ldb_message *msg,
655                                            enum ldb_scope scope,
656                                            const char * const *attrs_from_user,
657                                            const char * const *attrs_searched_for,
658                                            struct op_controls_flags* controls_flags,
659                                            struct ldb_request *parent)
660 {
661         struct ldb_context *ldb;
662         unsigned int i, a = 0;
663         bool constructed_attributes = false;
664
665         ldb = ldb_module_get_ctx(module);
666
667         /* removed any attrs that should not be shown to the user */
668         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
669                 switch (operational_remove[i].op) {
670                 case OPERATIONAL_REMOVE_UNASKED:
671                         if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
672                                 continue;
673                         }
674                         if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
675                                 continue;
676                         }
677                 case OPERATIONAL_REMOVE_ALWAYS:
678                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
679                         break;
680                 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
681                         if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
682                                 ldb_msg_remove_attr(msg, operational_remove[i].attr);
683                                 break;
684                         } else {
685                                 continue;
686                         }
687                 case OPERATIONAL_SD_FLAGS:
688                         if (controls_flags->sd ||
689                             ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
690                                 continue;
691                         }
692                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
693                         break;
694                 }
695         }
696
697         for (a=0;attrs_from_user && attrs_from_user[a];a++) {
698                 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
699                         continue;
700                 }
701                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
702                         if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
703                                 continue;
704                         }
705
706                         /* construct the new attribute, using either a supplied
707                            constructor or a simple copy */
708                         constructed_attributes = true;
709                         if (search_sub[i].constructor != NULL) {
710                                 if (search_sub[i].constructor(module, msg, scope, parent) != LDB_SUCCESS) {
711                                         goto failed;
712                                 }
713                         } else if (ldb_msg_copy_attr(msg,
714                                                      search_sub[i].replace,
715                                                      search_sub[i].attr) != LDB_SUCCESS) {
716                                 goto failed;
717                         }
718                 }
719         }
720
721         /* Deletion of the search helper attributes are needed if:
722          * - we generated constructed attributes and
723          * - we aren't requesting all attributes
724          */
725         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
726                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
727                         /* remove the added search helper attributes, unless
728                          * they were asked for by the user */
729                         if (search_sub[i].replace != NULL && 
730                             !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
731                                 ldb_msg_remove_attr(msg, search_sub[i].replace);
732                         }
733                         if (search_sub[i].extra_attr != NULL && 
734                             !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
735                                 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
736                         }
737                 }
738         }
739
740         return 0;
741
742 failed:
743         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
744                       "operational_search_post_process failed for attribute '%s' - %s",
745                       attrs_from_user[a], ldb_errstring(ldb));
746         return -1;
747 }
748
749 /*
750   hook search operations
751 */
752
753 struct operational_context {
754         struct ldb_module *module;
755         struct ldb_request *req;
756         enum ldb_scope scope;
757         const char * const *attrs;
758         struct op_controls_flags* controls_flags;
759 };
760
761 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
762 {
763         struct operational_context *ac;
764         int ret;
765
766         ac = talloc_get_type(req->context, struct operational_context);
767
768         if (!ares) {
769                 return ldb_module_done(ac->req, NULL, NULL,
770                                         LDB_ERR_OPERATIONS_ERROR);
771         }
772         if (ares->error != LDB_SUCCESS) {
773                 return ldb_module_done(ac->req, ares->controls,
774                                         ares->response, ares->error);
775         }
776
777         switch (ares->type) {
778         case LDB_REPLY_ENTRY:
779                 /* for each record returned post-process to add any derived
780                    attributes that have been asked for */
781                 ret = operational_search_post_process(ac->module,
782                                                       ares->message,
783                                                       ac->scope,
784                                                       ac->attrs,
785                                                       req->op.search.attrs,
786                                                       ac->controls_flags, req);
787                 if (ret != 0) {
788                         return ldb_module_done(ac->req, NULL, NULL,
789                                                 LDB_ERR_OPERATIONS_ERROR);
790                 }
791                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
792
793         case LDB_REPLY_REFERRAL:
794                 return ldb_module_send_referral(ac->req, ares->referral);
795
796         case LDB_REPLY_DONE:
797
798                 return ldb_module_done(ac->req, ares->controls,
799                                         ares->response, LDB_SUCCESS);
800         }
801
802         talloc_free(ares);
803         return LDB_SUCCESS;
804 }
805
806 static int operational_search(struct ldb_module *module, struct ldb_request *req)
807 {
808         struct ldb_context *ldb;
809         struct operational_context *ac;
810         struct ldb_request *down_req;
811         const char **search_attrs = NULL;
812         unsigned int i, a;
813         int ret;
814
815         /* There are no operational attributes on special DNs */
816         if (ldb_dn_is_special(req->op.search.base)) {
817                 return ldb_next_request(module, req);
818         }
819
820         ldb = ldb_module_get_ctx(module);
821
822         ac = talloc(req, struct operational_context);
823         if (ac == NULL) {
824                 return ldb_oom(ldb);
825         }
826
827         ac->module = module;
828         ac->req = req;
829         ac->scope = req->op.search.scope;
830         ac->attrs = req->op.search.attrs;
831
832         /*  FIXME: We must copy the tree and keep the original
833          *  unmodified. SSS */
834         /* replace any attributes in the parse tree that are
835            searchable, but are stored using a different name in the
836            backend */
837         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
838                 ldb_parse_tree_attr_replace(req->op.search.tree,
839                                             parse_tree_sub[i].attr,
840                                             parse_tree_sub[i].replace);
841         }
842
843         ac->controls_flags = talloc(ac, struct op_controls_flags);
844         /* remember if the SD_FLAGS_OID was set */
845         ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
846         /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
847         ac->controls_flags->bypassoperational =
848                 (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
849
850         /* in the list of attributes we are looking for, rename any
851            attributes to the alias for any hidden attributes that can
852            be fetched directly using non-hidden names */
853         for (a=0;ac->attrs && ac->attrs[a];a++) {
854                 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
855                         continue;
856                 }
857                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
858                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
859                             search_sub[i].replace) {
860
861                                 if (search_sub[i].extra_attr) {
862                                         const char **search_attrs2;
863                                         /* Only adds to the end of the list */
864                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
865                                                                                ? search_attrs
866                                                                                : ac->attrs, 
867                                                                                search_sub[i].extra_attr);
868                                         if (search_attrs2 == NULL) {
869                                                 return ldb_operr(ldb);
870                                         }
871                                         /* may be NULL, talloc_free() doesn't mind */
872                                         talloc_free(search_attrs);
873                                         search_attrs = search_attrs2;
874                                 }
875
876                                 if (!search_attrs) {
877                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
878                                         if (search_attrs == NULL) {
879                                                 return ldb_operr(ldb);
880                                         }
881                                 }
882                                 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
883                                 search_attrs[a] = search_sub[i].replace;
884                         }
885                 }
886         }
887
888         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
889                                         req->op.search.base,
890                                         req->op.search.scope,
891                                         req->op.search.tree,
892                                         /* use new set of attrs if any */
893                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
894                                         req->controls,
895                                         ac, operational_callback,
896                                         req);
897         LDB_REQ_SET_LOCATION(down_req);
898         if (ret != LDB_SUCCESS) {
899                 return ldb_operr(ldb);
900         }
901
902         /* perform the search */
903         return ldb_next_request(module, down_req);
904 }
905
906 static int operational_init(struct ldb_module *ctx)
907 {
908         struct operational_data *data;
909         int ret;
910
911         ret = ldb_next_init(ctx);
912
913         if (ret != LDB_SUCCESS) {
914                 return ret;
915         }
916
917         data = talloc_zero(ctx, struct operational_data);
918         if (!data) {
919                 return ldb_module_oom(ctx);
920         }
921
922         ldb_module_set_private(ctx, data);
923
924         return LDB_SUCCESS;
925 }
926
927 static const struct ldb_module_ops ldb_operational_module_ops = {
928         .name              = "operational",
929         .search            = operational_search,
930         .init_context      = operational_init
931 };
932
933 int ldb_operational_module_init(const char *version)
934 {
935         LDB_MODULE_CHECK_VERSION(version);
936         return ldb_register_module(&ldb_operational_module_ops);
937 }