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