42cbea74491311ff0a1bcb4b6e4b1ab3859625c3
[kamenim/samba.git] / source4 / dsdb / samdb / ldb_modules / operational.c
1 /*
2    ldb database library
3
4    Copyright (C) Andrew Tridgell 2005
5    Copyright (C) Simo Sorce 2006-2008
6    Copyright (C) Matthias Dieter Wallnöfer 2009
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /*
23   handle operational attributes
24  */
25
26 /*
27   createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
28   modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
29
30      for the above two, we do the search as normal, and if
31      createTimestamp or modifyTimestamp is asked for, then do
32      additional searches for whenCreated and whenChanged and fill in
33      the resulting values
34
35      we also need to replace these with the whenCreated/whenChanged
36      equivalent in the search expression trees
37
38   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
39   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
40
41      on init we need to setup attribute handlers for these so
42      comparisons are done correctly. The resolution is 1 second.
43
44      on add we need to add both the above, for current time
45
46      on modify we need to change whenChanged
47
48   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
49
50      for this one we do the search as normal, then if requested ask
51      for objectclass, change the attribute name, and add it
52
53   primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
54
55      contains the RID of a certain group object
56     
57
58   attributeTypes: in schema only
59   objectClasses: in schema only
60   matchingRules: in schema only
61   matchingRuleUse: in schema only
62   creatorsName: not supported by w2k3?
63   modifiersName: not supported by w2k3?
64 */
65
66 #include "includes.h"
67 #include "ldb_includes.h"
68 #include "ldb_module.h"
69
70 #include "librpc/gen_ndr/ndr_misc.h"
71 #include "librpc/gen_ndr/ndr_drsblobs.h"
72 #include "param/param.h"
73 #include "dsdb/samdb/samdb.h"
74 #include "dsdb/samdb/ldb_modules/util.h"
75
76 #include "auth/auth.h"
77 #include "libcli/security/dom_sid.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 {
93         char *canonicalName;
94         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
95         if (canonicalName == NULL) {
96                 return LDB_ERR_OPERATIONS_ERROR;
97         }
98         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
99 }
100
101 /*
102   construct a primary group token for groups from a message
103 */
104 static int construct_primary_group_token(struct ldb_module *module,
105                                          struct ldb_message *msg, enum ldb_scope scope)
106 {
107         struct ldb_context *ldb;
108         uint32_t primary_group_token;
109         
110         ldb = ldb_module_get_ctx(module);
111         if (ldb_match_msg_objectclass(msg, "group") == 1) {
112                 primary_group_token
113                         = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
114                 if (primary_group_token == 0) {
115                         return LDB_SUCCESS;
116                 }
117
118                 return samdb_msg_add_int(ldb, msg, msg, "primaryGroupToken",
119                         primary_group_token);
120         } else {
121                 return LDB_SUCCESS;
122         }
123 }
124
125 /*
126   construct the token groups for SAM objects from a message
127 */
128 static int construct_token_groups(struct ldb_module *module,
129                                   struct ldb_message *msg, enum ldb_scope scope)
130 {
131         struct ldb_context *ldb = ldb_module_get_ctx(module);;
132         struct auth_context *auth_context;
133         struct auth_serversupplied_info *server_info;
134         struct auth_session_info *session_info;
135         TALLOC_CTX *tmp_ctx = talloc_new(msg);
136         uint32_t i;
137         int ret;
138
139         NTSTATUS status;
140
141         if (scope != LDB_SCOPE_BASE) {
142                 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
143                 return LDB_ERR_OPERATIONS_ERROR;
144         }
145
146         status = auth_context_create_from_ldb(tmp_ctx, ldb, &auth_context);
147         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
148                 talloc_free(tmp_ctx);
149                 ldb_module_oom(module);
150                 return LDB_ERR_OPERATIONS_ERROR;
151         } else if (!NT_STATUS_IS_OK(status)) {
152                 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, could not create authContext");
153                 talloc_free(tmp_ctx);
154                 return LDB_ERR_OPERATIONS_ERROR;
155         }
156
157         status = auth_get_server_info_principal(tmp_ctx, auth_context, NULL, msg->dn, &server_info);
158         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
159                 talloc_free(tmp_ctx);
160                 ldb_module_oom(module);
161                 return LDB_ERR_OPERATIONS_ERROR;
162         } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
163                 /* Not a user, we have no tokenGroups */
164                 talloc_free(tmp_ctx);
165                 return LDB_SUCCESS;
166         } else if (!NT_STATUS_IS_OK(status)) {
167                 talloc_free(tmp_ctx);
168                 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_get_server_info_principal failed: %s", nt_errstr(status));
169                 return LDB_ERR_OPERATIONS_ERROR;
170         }
171
172         status = auth_generate_session_info(tmp_ctx, auth_context, server_info, 0, &session_info);
173         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
174                 talloc_free(tmp_ctx);
175                 ldb_module_oom(module);
176                 return LDB_ERR_OPERATIONS_ERROR;
177         } else if (!NT_STATUS_IS_OK(status)) {
178                 talloc_free(tmp_ctx);
179                 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_generate_session_info failed: %s", nt_errstr(status));
180                 return LDB_ERR_OPERATIONS_ERROR;
181         }
182
183         /* We start at 1, as the first SID is the user's SID, not included in the tokenGroups */
184         for (i = 1; i < session_info->security_token->num_sids; i++) {
185                 ret = samdb_msg_add_dom_sid(ldb, msg, msg,
186                                             "tokenGroups",
187                                             session_info->security_token->sids[i]);
188                 if (ret != LDB_SUCCESS) {
189                         talloc_free(tmp_ctx);
190                         return ret;
191                 }
192         }
193
194         return LDB_SUCCESS;
195 }
196
197 /*
198   construct the parent GUID for an entry from a message
199 */
200 static int construct_parent_guid(struct ldb_module *module,
201                                  struct ldb_message *msg, enum ldb_scope scope)
202 {
203         struct ldb_result *res;
204         const struct ldb_val *parent_guid;
205         const char *attrs[] = { "objectGUID", NULL };
206         int ret;
207         struct ldb_val v;
208
209         /* TODO:  In the future, this needs to honour the partition boundaries */
210         struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
211
212         if (parent_dn == NULL) {
213                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
214                                          ldb_dn_get_linearized(msg->dn)));
215                 return LDB_SUCCESS;
216         }
217
218         ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
219         talloc_free(parent_dn);
220         /* if there is no parentGUID for this object, then return */
221         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
222                 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
223                          ldb_dn_get_linearized(msg->dn)));
224                 return LDB_SUCCESS;
225         } else if (ret != LDB_SUCCESS) {
226                 return ret;
227         }
228
229         parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
230         if (!parent_guid) {
231                 talloc_free(res);
232                 return LDB_SUCCESS;
233         }
234
235         v = data_blob_dup_talloc(res, parent_guid);
236         if (!v.data) {
237                 talloc_free(res);
238                 return LDB_ERR_OPERATIONS_ERROR;
239         }
240         ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
241         talloc_free(res);
242         return ret;
243 }
244
245 /*
246   construct a subSchemaSubEntry
247 */
248 static int construct_subschema_subentry(struct ldb_module *module,
249                                         struct ldb_message *msg, enum ldb_scope scope)
250 {
251         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
252         char *subSchemaSubEntry;
253
254         /* We may be being called before the init function has finished */
255         if (!data) {
256                 return LDB_SUCCESS;
257         }
258
259         /* Try and set this value up, if possible.  Don't worry if it
260          * fails, we may not have the DB set up yet, and it's not
261          * really vital anyway */
262         if (!data->aggregate_dn) {
263                 struct ldb_context *ldb = ldb_module_get_ctx(module);
264                 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
265         }
266
267         if (data->aggregate_dn) {
268                 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
269                 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
270         }
271         return LDB_SUCCESS;
272 }
273
274
275 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
276                                          struct ldb_message *msg,
277                                          struct ldb_message_element *object_category)
278 {
279         struct ldb_context *ldb;
280         struct ldb_dn *dn;
281         const struct ldb_val *val;
282
283         ldb = ldb_module_get_ctx(module);
284         if (!ldb) {
285                 DEBUG(4, (__location__ ": Failed to get ldb \n"));
286                 return LDB_ERR_OPERATIONS_ERROR;
287         }
288
289         dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
290         if (!dn) {
291                 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
292                           (const char *)object_category->values[0].data));
293                 return LDB_ERR_OPERATIONS_ERROR;
294         }
295
296         val = ldb_dn_get_rdn_val(dn);
297         if (!val) {
298                 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
299                           ldb_dn_get_linearized(dn)));
300                 return LDB_ERR_OPERATIONS_ERROR;
301         }
302
303         if (strequal((const char *)val->data, "NTDS-DSA")) {
304                 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
305         } else {
306                 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
307         }
308         return LDB_SUCCESS;
309 }
310
311 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
312                                                 struct ldb_message *msg,
313                                                 struct ldb_dn *dn)
314 {
315         struct ldb_dn *server_dn;
316         const char *attr_obj_cat[] = { "objectCategory", NULL };
317         struct ldb_result *res;
318         struct ldb_message_element *object_category;
319         int ret;
320
321         server_dn = ldb_dn_copy(msg, dn);
322         if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
323                 DEBUG(4, (__location__ ": Failed to add child to %s \n",
324                           ldb_dn_get_linearized(server_dn)));
325                 return LDB_ERR_OPERATIONS_ERROR;
326         }
327
328         ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat, 0);
329         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
330                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
331                                          ldb_dn_get_linearized(server_dn)));
332                 return LDB_SUCCESS;
333         } else if (ret != LDB_SUCCESS) {
334                 return ret;
335         }
336
337         object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
338         if (!object_category) {
339                 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
340                          ldb_dn_get_linearized(res->msgs[0]->dn)));
341                 return LDB_SUCCESS;
342         }
343         return construct_msds_isrodc_with_dn(module, msg, object_category);
344 }
345
346 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
347                                                   struct ldb_message *msg)
348 {
349         struct ldb_context *ldb;
350         const char *attr[] = { "serverReferenceBL", NULL };
351         struct ldb_result *res;
352         int ret;
353         struct ldb_dn *server_dn;
354
355         ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attr, 0);
356         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
357                 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
358                          ldb_dn_get_linearized(msg->dn)));
359                 return LDB_SUCCESS;
360         } else if (ret != LDB_SUCCESS) {
361                 return ret;
362         }
363
364         ldb = ldb_module_get_ctx(module);
365         if (!ldb) {
366                 return LDB_SUCCESS;
367         }
368
369         server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
370         if (!server_dn) {
371                 DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
372                          ldb_dn_get_linearized(res->msgs[0]->dn)));
373                 return LDB_SUCCESS;
374         }
375         return construct_msds_isrodc_with_server_dn(module, msg, server_dn);
376 }
377
378 /*
379   construct msDS-isRODC attr
380 */
381 static int construct_msds_isrodc(struct ldb_module *module,
382                                  struct ldb_message *msg, enum ldb_scope scope)
383 {
384         struct ldb_message_element * object_class;
385         struct ldb_message_element * object_category;
386         unsigned int i;
387
388         object_class = ldb_msg_find_element(msg, "objectClass");
389         if (!object_class) {
390                 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
391                          ldb_dn_get_linearized(msg->dn)));
392                 return LDB_ERR_OPERATIONS_ERROR;
393         }
394
395         for (i=0; i<object_class->num_values; i++) {
396                 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
397                         /* If TO!objectCategory  equals the DN of the classSchema  object for the nTDSDSA
398                          * object class, then TO!msDS-isRODC  is false. Otherwise, TO!msDS-isRODC  is true.
399                          */
400                         object_category = ldb_msg_find_element(msg, "objectCategory");
401                         if (!object_category) {
402                                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
403                                          ldb_dn_get_linearized(msg->dn)));
404                                 return LDB_SUCCESS;
405                         }
406                         return construct_msds_isrodc_with_dn(module, msg, object_category);
407                 }
408                 if (strequal((const char*)object_class->values[i].data, "server")) {
409                         /* Let TN be the nTDSDSA  object whose DN is "CN=NTDS Settings," prepended to
410                          * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA  object" case,
411                          * substituting TN for TO.
412                          */
413                         return construct_msds_isrodc_with_server_dn(module, msg, msg->dn);
414                 }
415                 if (strequal((const char*)object_class->values[i].data, "computer")) {
416                         /* Let TS be the server  object named by TO!serverReferenceBL. Apply the previous
417                          * rule for the "TO is a server  object" case, substituting TS for TO.
418                          */
419                         return construct_msds_isrodc_with_computer_dn(module, msg);
420                 }
421         }
422
423         return LDB_SUCCESS;
424 }
425
426
427 /*
428   construct msDS-keyVersionNumber attr
429
430   TODO:  Make this based on the 'win2k' DS huristics bit...
431
432 */
433 static int construct_msds_keyversionnumber(struct ldb_module *module,
434                                            struct ldb_message *msg,
435                                            enum ldb_scope scope)
436 {
437         uint32_t i;
438         enum ndr_err_code ndr_err;
439         const struct ldb_val *omd_value;
440         struct replPropertyMetaDataBlob *omd;
441
442         omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
443         if (!omd_value) {
444                 /* We can't make up a key version number without meta data */
445                 return LDB_SUCCESS;
446         }
447         if (!omd_value) {
448                 return LDB_SUCCESS;
449         }
450
451         omd = talloc(msg, struct replPropertyMetaDataBlob);
452         if (!omd) {
453                 ldb_module_oom(module);
454                 return LDB_SUCCESS;
455         }
456
457         ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
458                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
459         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
460                 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
461                          ldb_dn_get_linearized(msg->dn)));
462                 return LDB_ERR_OPERATIONS_ERROR;
463         }
464
465         if (omd->version != 1) {
466                 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
467                          omd->version, ldb_dn_get_linearized(msg->dn)));
468                 talloc_free(omd);
469                 return LDB_SUCCESS;
470         }
471         for (i=0; i<omd->ctr.ctr1.count; i++) {
472                 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTRIBUTE_unicodePwd) {
473                         ldb_msg_add_fmt(msg, "msDS-KeyVersionNumber", "%u", omd->ctr.ctr1.array[i].version);
474                         break;
475                 }
476         }
477         return LDB_SUCCESS;
478
479 }
480
481 /*
482   a list of attribute names that should be substituted in the parse
483   tree before the search is done
484 */
485 static const struct {
486         const char *attr;
487         const char *replace;
488 } parse_tree_sub[] = {
489         { "createTimestamp", "whenCreated" },
490         { "modifyTimestamp", "whenChanged" }
491 };
492
493
494 /*
495   a list of attribute names that are hidden, but can be searched for
496   using another (non-hidden) name to produce the correct result
497 */
498 static const struct {
499         const char *attr;
500         const char *replace;
501         const char *extra_attr;
502         int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
503 } search_sub[] = {
504         { "createTimestamp", "whenCreated", NULL , NULL },
505         { "modifyTimestamp", "whenChanged", NULL , NULL },
506         { "structuralObjectClass", "objectClass", NULL , NULL },
507         { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
508         { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
509         { "tokenGroups", "objectClass", NULL, construct_token_groups },
510         { "parentGUID", NULL, NULL, construct_parent_guid },
511         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
512         { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
513         { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
514 };
515
516
517 enum op_remove {
518         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
519         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
520         OPERATIONAL_SD_FLAGS       /* show if SD_FLAGS_OID set, or asked for */
521 };
522
523 /*
524   a list of attributes that may need to be removed from the
525   underlying db return
526
527   Some of these are attributes that were once stored, but are now calculated
528 */
529 static const struct {
530         const char *attr;
531         enum op_remove op;
532 } operational_remove[] = {
533         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
534         { "msDS-KeyVersionNumber",   OPERATIONAL_REMOVE_ALWAYS  },
535         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
536         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
537         { "unicodePwd",              OPERATIONAL_REMOVE_UNASKED },
538         { "dBCSPwd",                 OPERATIONAL_REMOVE_UNASKED },
539         { "ntPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
540         { "lmPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
541         { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
542 };
543
544
545 /*
546   post process a search result record. For any search_sub[] attributes that were
547   asked for, we need to call the appropriate copy routine to copy the result
548   into the message, then remove any attributes that we added to the search but
549   were not asked for by the user
550 */
551 static int operational_search_post_process(struct ldb_module *module,
552                                            struct ldb_message *msg,
553                                            enum ldb_scope scope,
554                                            const char * const *attrs_from_user,
555                                            const char * const *attrs_searched_for,
556                                            bool sd_flags_set)
557 {
558         struct ldb_context *ldb;
559         unsigned int i, a = 0;
560         bool constructed_attributes = false;
561
562         ldb = ldb_module_get_ctx(module);
563
564         /* removed any attrs that should not be shown to the user */
565         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
566                 switch (operational_remove[i].op) {
567                 case OPERATIONAL_REMOVE_UNASKED:
568                         if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
569                                 continue;
570                         }
571                         if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
572                                 continue;
573                         }
574                 case OPERATIONAL_REMOVE_ALWAYS:
575                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
576                         break;
577                 case OPERATIONAL_SD_FLAGS:
578                         if (sd_flags_set ||
579                             ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
580                                 continue;
581                         }
582                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
583                         break;
584                 }
585         }
586
587         for (a=0;attrs_from_user && attrs_from_user[a];a++) {
588                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
589                         if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
590                                 continue;
591                         }
592
593                         /* construct the new attribute, using either a supplied
594                            constructor or a simple copy */
595                         constructed_attributes = true;
596                         if (search_sub[i].constructor != NULL) {
597                                 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
598                                         goto failed;
599                                 }
600                         } else if (ldb_msg_copy_attr(msg,
601                                                      search_sub[i].replace,
602                                                      search_sub[i].attr) != LDB_SUCCESS) {
603                                 goto failed;
604                         }
605                 }
606         }
607
608         /* Deletion of the search helper attributes are needed if:
609          * - we generated constructed attributes and
610          * - we aren't requesting all attributes
611          */
612         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
613                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
614                         /* remove the added search helper attributes, unless
615                          * they were asked for by the user */
616                         if (search_sub[i].replace != NULL && 
617                             !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
618                                 ldb_msg_remove_attr(msg, search_sub[i].replace);
619                         }
620                         if (search_sub[i].extra_attr != NULL && 
621                             !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
622                                 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
623                         }
624                 }
625         }
626
627         return 0;
628
629 failed:
630         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
631                       "operational_search_post_process failed for attribute '%s'",
632                       attrs_from_user[a]);
633         return -1;
634 }
635
636
637 /*
638   hook search operations
639 */
640
641 struct operational_context {
642         struct ldb_module *module;
643         struct ldb_request *req;
644         enum ldb_scope scope;
645         const char * const *attrs;
646         bool sd_flags_set;
647 };
648
649 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
650 {
651         struct operational_context *ac;
652         int ret;
653
654         ac = talloc_get_type(req->context, struct operational_context);
655
656         if (!ares) {
657                 return ldb_module_done(ac->req, NULL, NULL,
658                                         LDB_ERR_OPERATIONS_ERROR);
659         }
660         if (ares->error != LDB_SUCCESS) {
661                 return ldb_module_done(ac->req, ares->controls,
662                                         ares->response, ares->error);
663         }
664
665         switch (ares->type) {
666         case LDB_REPLY_ENTRY:
667                 /* for each record returned post-process to add any derived
668                    attributes that have been asked for */
669                 ret = operational_search_post_process(ac->module,
670                                                       ares->message,
671                                                       ac->scope,
672                                                       ac->attrs,
673                                                       req->op.search.attrs,
674                                                       ac->sd_flags_set);
675                 if (ret != 0) {
676                         return ldb_module_done(ac->req, NULL, NULL,
677                                                 LDB_ERR_OPERATIONS_ERROR);
678                 }
679                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
680
681         case LDB_REPLY_REFERRAL:
682                 return ldb_module_send_referral(ac->req, ares->referral);
683
684         case LDB_REPLY_DONE:
685
686                 return ldb_module_done(ac->req, ares->controls,
687                                         ares->response, LDB_SUCCESS);
688         }
689
690         talloc_free(ares);
691         return LDB_SUCCESS;
692 }
693
694 static int operational_search(struct ldb_module *module, struct ldb_request *req)
695 {
696         struct ldb_context *ldb;
697         struct operational_context *ac;
698         struct ldb_request *down_req;
699         const char **search_attrs = NULL;
700         unsigned int i, a;
701         int ret;
702
703         /* There are no operational attributes on special DNs */
704         if (ldb_dn_is_special(req->op.search.base)) {
705                 return ldb_next_request(module, req);
706         }
707
708         ldb = ldb_module_get_ctx(module);
709
710         ac = talloc(req, struct operational_context);
711         if (ac == NULL) {
712                 return LDB_ERR_OPERATIONS_ERROR;
713         }
714
715         ac->module = module;
716         ac->req = req;
717         ac->scope = req->op.search.scope;
718         ac->attrs = req->op.search.attrs;
719
720         /*  FIXME: We must copy the tree and keep the original
721          *  unmodified. SSS */
722         /* replace any attributes in the parse tree that are
723            searchable, but are stored using a different name in the
724            backend */
725         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
726                 ldb_parse_tree_attr_replace(req->op.search.tree,
727                                             parse_tree_sub[i].attr,
728                                             parse_tree_sub[i].replace);
729         }
730
731         /* in the list of attributes we are looking for, rename any
732            attributes to the alias for any hidden attributes that can
733            be fetched directly using non-hidden names */
734         for (a=0;ac->attrs && ac->attrs[a];a++) {
735                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
736                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
737                             search_sub[i].replace) {
738
739                                 if (search_sub[i].extra_attr) {
740                                         const char **search_attrs2;
741                                         /* Only adds to the end of the list */
742                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
743                                                                                ? search_attrs
744                                                                                : ac->attrs, 
745                                                                                search_sub[i].extra_attr);
746                                         if (search_attrs2 == NULL) {
747                                                 return LDB_ERR_OPERATIONS_ERROR;
748                                         }
749                                         /* may be NULL, talloc_free() doesn't mind */
750                                         talloc_free(search_attrs);
751                                         search_attrs = search_attrs2;
752                                 }
753
754                                 if (!search_attrs) {
755                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
756                                         if (search_attrs == NULL) {
757                                                 return LDB_ERR_OPERATIONS_ERROR;
758                                         }
759                                 }
760                                 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
761                                 search_attrs[a] = search_sub[i].replace;
762                         }
763                 }
764         }
765
766         /* remember if the SD_FLAGS_OID was set */
767         ac->sd_flags_set = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
768
769         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
770                                         req->op.search.base,
771                                         req->op.search.scope,
772                                         req->op.search.tree,
773                                         /* use new set of attrs if any */
774                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
775                                         req->controls,
776                                         ac, operational_callback,
777                                         req);
778         if (ret != LDB_SUCCESS) {
779                 return LDB_ERR_OPERATIONS_ERROR;
780         }
781
782         /* perform the search */
783         return ldb_next_request(module, down_req);
784 }
785
786 static int operational_init(struct ldb_module *ctx)
787 {
788         struct operational_data *data;
789         int ret;
790         auth_init();
791
792         ret = ldb_next_init(ctx);
793
794         if (ret != LDB_SUCCESS) {
795                 return ret;
796         }
797
798         data = talloc_zero(ctx, struct operational_data);
799         if (!data) {
800                 ldb_module_oom(ctx);
801                 return LDB_ERR_OPERATIONS_ERROR;
802         }
803
804         ldb_module_set_private(ctx, data);
805
806         return LDB_SUCCESS;
807 }
808
809 const struct ldb_module_ops ldb_operational_module_ops = {
810         .name              = "operational",
811         .search            = operational_search,
812         .init_context      = operational_init
813 };