03502d3de34ee05ac295f51d2a559e65c4504ebb
[metze/samba/wip.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.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_operr(ldb_module_get_ctx(module));
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                 return ldb_module_oom(module);
150         } else if (!NT_STATUS_IS_OK(status)) {
151                 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, could not create authContext");
152                 talloc_free(tmp_ctx);
153                 return LDB_ERR_OPERATIONS_ERROR;
154         }
155
156         status = auth_get_server_info_principal(tmp_ctx, auth_context, NULL, msg->dn, &server_info);
157         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
158                 talloc_free(tmp_ctx);
159                 return ldb_module_oom(module);
160         } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
161                 /* Not a user, we have no tokenGroups */
162                 talloc_free(tmp_ctx);
163                 return LDB_SUCCESS;
164         } else if (!NT_STATUS_IS_OK(status)) {
165                 talloc_free(tmp_ctx);
166                 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_get_server_info_principal failed: %s", nt_errstr(status));
167                 return LDB_ERR_OPERATIONS_ERROR;
168         }
169
170         status = auth_generate_session_info(tmp_ctx, auth_context, server_info, 0, &session_info);
171         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
172                 talloc_free(tmp_ctx);
173                 return ldb_module_oom(module);
174         } else if (!NT_STATUS_IS_OK(status)) {
175                 talloc_free(tmp_ctx);
176                 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_generate_session_info failed: %s", nt_errstr(status));
177                 return LDB_ERR_OPERATIONS_ERROR;
178         }
179
180         /* We start at 1, as the first SID is the user's SID, not included in the tokenGroups */
181         for (i = 1; i < session_info->security_token->num_sids; i++) {
182                 ret = samdb_msg_add_dom_sid(ldb, msg, msg,
183                                             "tokenGroups",
184                                             session_info->security_token->sids[i]);
185                 if (ret != LDB_SUCCESS) {
186                         talloc_free(tmp_ctx);
187                         return ret;
188                 }
189         }
190
191         return LDB_SUCCESS;
192 }
193
194 /*
195   construct the parent GUID for an entry from a message
196 */
197 static int construct_parent_guid(struct ldb_module *module,
198                                  struct ldb_message *msg, enum ldb_scope scope)
199 {
200         struct ldb_result *res;
201         const struct ldb_val *parent_guid;
202         const char *attrs[] = { "objectGUID", NULL };
203         int ret;
204         struct ldb_val v;
205
206         /* TODO:  In the future, this needs to honour the partition boundaries */
207         struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
208
209         if (parent_dn == NULL) {
210                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
211                                          ldb_dn_get_linearized(msg->dn)));
212                 return LDB_SUCCESS;
213         }
214
215         ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
216         talloc_free(parent_dn);
217
218         /* if there is no parent for this object, then return */
219         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
220                 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
221                          ldb_dn_get_linearized(msg->dn)));
222                 return LDB_SUCCESS;
223         } else if (ret != LDB_SUCCESS) {
224                 return ret;
225         }
226
227         parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
228         if (!parent_guid) {
229                 talloc_free(res);
230                 return LDB_SUCCESS;
231         }
232
233         v = data_blob_dup_talloc(res, parent_guid);
234         if (!v.data) {
235                 talloc_free(res);
236                 return ldb_oom(ldb_module_get_ctx(module));
237         }
238         ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
239         talloc_free(res);
240         return ret;
241 }
242
243 /*
244   construct a subSchemaSubEntry
245 */
246 static int construct_subschema_subentry(struct ldb_module *module,
247                                         struct ldb_message *msg, enum ldb_scope scope)
248 {
249         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
250         char *subSchemaSubEntry;
251
252         /* We may be being called before the init function has finished */
253         if (!data) {
254                 return LDB_SUCCESS;
255         }
256
257         /* Try and set this value up, if possible.  Don't worry if it
258          * fails, we may not have the DB set up yet, and it's not
259          * really vital anyway */
260         if (!data->aggregate_dn) {
261                 struct ldb_context *ldb = ldb_module_get_ctx(module);
262                 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
263         }
264
265         if (data->aggregate_dn) {
266                 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
267                 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
268         }
269         return LDB_SUCCESS;
270 }
271
272
273 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
274                                          struct ldb_message *msg,
275                                          struct ldb_message_element *object_category)
276 {
277         struct ldb_context *ldb;
278         struct ldb_dn *dn;
279         const struct ldb_val *val;
280
281         ldb = ldb_module_get_ctx(module);
282         if (!ldb) {
283                 DEBUG(4, (__location__ ": Failed to get ldb \n"));
284                 return ldb_operr(ldb);
285         }
286
287         dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
288         if (!dn) {
289                 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
290                           (const char *)object_category->values[0].data));
291                 return ldb_operr(ldb);
292         }
293
294         val = ldb_dn_get_rdn_val(dn);
295         if (!val) {
296                 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
297                           ldb_dn_get_linearized(dn)));
298                 return ldb_operr(ldb);
299         }
300
301         if (strequal((const char *)val->data, "NTDS-DSA")) {
302                 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
303         } else {
304                 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
305         }
306         return LDB_SUCCESS;
307 }
308
309 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
310                                                 struct ldb_message *msg,
311                                                 struct ldb_dn *dn)
312 {
313         struct ldb_dn *server_dn;
314         const char *attr_obj_cat[] = { "objectCategory", NULL };
315         struct ldb_result *res;
316         struct ldb_message_element *object_category;
317         int ret;
318
319         server_dn = ldb_dn_copy(msg, dn);
320         if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
321                 DEBUG(4, (__location__ ": Failed to add child to %s \n",
322                           ldb_dn_get_linearized(server_dn)));
323                 return ldb_operr(ldb_module_get_ctx(module));
324         }
325
326         ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat, 0);
327         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
328                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
329                                          ldb_dn_get_linearized(server_dn)));
330                 return LDB_SUCCESS;
331         } else if (ret != LDB_SUCCESS) {
332                 return ret;
333         }
334
335         object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
336         if (!object_category) {
337                 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
338                          ldb_dn_get_linearized(res->msgs[0]->dn)));
339                 return LDB_SUCCESS;
340         }
341         return construct_msds_isrodc_with_dn(module, msg, object_category);
342 }
343
344 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
345                                                   struct ldb_message *msg)
346 {
347         struct ldb_context *ldb;
348         const char *attr[] = { "serverReferenceBL", NULL };
349         struct ldb_result *res;
350         int ret;
351         struct ldb_dn *server_dn;
352
353         ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attr, 0);
354         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
355                 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
356                          ldb_dn_get_linearized(msg->dn)));
357                 return LDB_SUCCESS;
358         } else if (ret != LDB_SUCCESS) {
359                 return ret;
360         }
361
362         ldb = ldb_module_get_ctx(module);
363         if (!ldb) {
364                 return LDB_SUCCESS;
365         }
366
367         server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
368         if (!server_dn) {
369                 DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
370                          ldb_dn_get_linearized(res->msgs[0]->dn)));
371                 return LDB_SUCCESS;
372         }
373         return construct_msds_isrodc_with_server_dn(module, msg, server_dn);
374 }
375
376 /*
377   construct msDS-isRODC attr
378 */
379 static int construct_msds_isrodc(struct ldb_module *module,
380                                  struct ldb_message *msg, enum ldb_scope scope)
381 {
382         struct ldb_message_element * object_class;
383         struct ldb_message_element * object_category;
384         unsigned int i;
385
386         object_class = ldb_msg_find_element(msg, "objectClass");
387         if (!object_class) {
388                 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
389                          ldb_dn_get_linearized(msg->dn)));
390                 return ldb_operr(ldb_module_get_ctx(module));
391         }
392
393         for (i=0; i<object_class->num_values; i++) {
394                 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
395                         /* If TO!objectCategory  equals the DN of the classSchema  object for the nTDSDSA
396                          * object class, then TO!msDS-isRODC  is false. Otherwise, TO!msDS-isRODC  is true.
397                          */
398                         object_category = ldb_msg_find_element(msg, "objectCategory");
399                         if (!object_category) {
400                                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
401                                          ldb_dn_get_linearized(msg->dn)));
402                                 return LDB_SUCCESS;
403                         }
404                         return construct_msds_isrodc_with_dn(module, msg, object_category);
405                 }
406                 if (strequal((const char*)object_class->values[i].data, "server")) {
407                         /* Let TN be the nTDSDSA  object whose DN is "CN=NTDS Settings," prepended to
408                          * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA  object" case,
409                          * substituting TN for TO.
410                          */
411                         return construct_msds_isrodc_with_server_dn(module, msg, msg->dn);
412                 }
413                 if (strequal((const char*)object_class->values[i].data, "computer")) {
414                         /* Let TS be the server  object named by TO!serverReferenceBL. Apply the previous
415                          * rule for the "TO is a server  object" case, substituting TS for TO.
416                          */
417                         return construct_msds_isrodc_with_computer_dn(module, msg);
418                 }
419         }
420
421         return LDB_SUCCESS;
422 }
423
424
425 /*
426   construct msDS-keyVersionNumber attr
427
428   TODO:  Make this based on the 'win2k' DS huristics bit...
429
430 */
431 static int construct_msds_keyversionnumber(struct ldb_module *module,
432                                            struct ldb_message *msg,
433                                            enum ldb_scope scope)
434 {
435         uint32_t i;
436         enum ndr_err_code ndr_err;
437         const struct ldb_val *omd_value;
438         struct replPropertyMetaDataBlob *omd;
439
440         omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
441         if (!omd_value) {
442                 /* We can't make up a key version number without meta data */
443                 return LDB_SUCCESS;
444         }
445         if (!omd_value) {
446                 return LDB_SUCCESS;
447         }
448
449         omd = talloc(msg, struct replPropertyMetaDataBlob);
450         if (!omd) {
451                 ldb_module_oom(module);
452                 return LDB_SUCCESS;
453         }
454
455         ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
456                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
457         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
458                 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
459                          ldb_dn_get_linearized(msg->dn)));
460                 return ldb_operr(ldb_module_get_ctx(module));
461         }
462
463         if (omd->version != 1) {
464                 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
465                          omd->version, ldb_dn_get_linearized(msg->dn)));
466                 talloc_free(omd);
467                 return LDB_SUCCESS;
468         }
469         for (i=0; i<omd->ctr.ctr1.count; i++) {
470                 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTRIBUTE_unicodePwd) {
471                         ldb_msg_add_fmt(msg, "msDS-KeyVersionNumber", "%u", omd->ctr.ctr1.array[i].version);
472                         break;
473                 }
474         }
475         return LDB_SUCCESS;
476
477 }
478
479 struct op_controls_flags {
480         bool sd;
481         bool bypassoperational;
482 };
483
484 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
485         if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
486                 return true;
487         }
488         return false;
489 }
490
491 /*
492   a list of attribute names that should be substituted in the parse
493   tree before the search is done
494 */
495 static const struct {
496         const char *attr;
497         const char *replace;
498 } parse_tree_sub[] = {
499         { "createTimestamp", "whenCreated" },
500         { "modifyTimestamp", "whenChanged" }
501 };
502
503
504 /*
505   a list of attribute names that are hidden, but can be searched for
506   using another (non-hidden) name to produce the correct result
507 */
508 static const struct {
509         const char *attr;
510         const char *replace;
511         const char *extra_attr;
512         int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
513 } search_sub[] = {
514         { "createTimestamp", "whenCreated", NULL , NULL },
515         { "modifyTimestamp", "whenChanged", NULL , NULL },
516         { "structuralObjectClass", "objectClass", NULL , NULL },
517         { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
518         { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
519         { "tokenGroups", "objectClass", NULL, construct_token_groups },
520         { "parentGUID", NULL, NULL, construct_parent_guid },
521         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
522         { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
523         { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
524 };
525
526
527 enum op_remove {
528         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
529         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
530         OPERATIONAL_SD_FLAGS,      /* show if SD_FLAGS_OID set, or asked for */
531         OPERATIONAL_REMOVE_UNLESS_CONTROL        /* remove always unless an adhoc control has been specified */
532 };
533
534 /*
535   a list of attributes that may need to be removed from the
536   underlying db return
537
538   Some of these are attributes that were once stored, but are now calculated
539 */
540 static const struct {
541         const char *attr;
542         enum op_remove op;
543 } operational_remove[] = {
544         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
545         { "msDS-KeyVersionNumber",   OPERATIONAL_REMOVE_UNLESS_CONTROL  },
546         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
547         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
548         { "unicodePwd",              OPERATIONAL_REMOVE_UNASKED },
549         { "dBCSPwd",                 OPERATIONAL_REMOVE_UNASKED },
550         { "ntPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
551         { "lmPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
552         { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
553 };
554
555
556 /*
557   post process a search result record. For any search_sub[] attributes that were
558   asked for, we need to call the appropriate copy routine to copy the result
559   into the message, then remove any attributes that we added to the search but
560   were not asked for by the user
561 */
562 static int operational_search_post_process(struct ldb_module *module,
563                                            struct ldb_message *msg,
564                                            enum ldb_scope scope,
565                                            const char * const *attrs_from_user,
566                                            const char * const *attrs_searched_for,
567                                            struct op_controls_flags* controls_flags)
568 {
569         struct ldb_context *ldb;
570         unsigned int i, a = 0;
571         bool constructed_attributes = false;
572
573         ldb = ldb_module_get_ctx(module);
574
575         /* removed any attrs that should not be shown to the user */
576         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
577                 switch (operational_remove[i].op) {
578                 case OPERATIONAL_REMOVE_UNASKED:
579                         if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
580                                 continue;
581                         }
582                         if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
583                                 continue;
584                         }
585                 case OPERATIONAL_REMOVE_ALWAYS:
586                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
587                         break;
588                 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
589                         if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
590                                 ldb_msg_remove_attr(msg, operational_remove[i].attr);
591                                 break;
592                         } else {
593                                 continue;
594                         }
595                 case OPERATIONAL_SD_FLAGS:
596                         if (controls_flags->sd ||
597                             ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
598                                 continue;
599                         }
600                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
601                         break;
602                 }
603         }
604
605         for (a=0;attrs_from_user && attrs_from_user[a];a++) {
606                 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
607                         continue;
608                 }
609                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
610                         if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
611                                 continue;
612                         }
613
614                         /* construct the new attribute, using either a supplied
615                            constructor or a simple copy */
616                         constructed_attributes = true;
617                         if (search_sub[i].constructor != NULL) {
618                                 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
619                                         goto failed;
620                                 }
621                         } else if (ldb_msg_copy_attr(msg,
622                                                      search_sub[i].replace,
623                                                      search_sub[i].attr) != LDB_SUCCESS) {
624                                 goto failed;
625                         }
626                 }
627         }
628
629         /* Deletion of the search helper attributes are needed if:
630          * - we generated constructed attributes and
631          * - we aren't requesting all attributes
632          */
633         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
634                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
635                         /* remove the added search helper attributes, unless
636                          * they were asked for by the user */
637                         if (search_sub[i].replace != NULL && 
638                             !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
639                                 ldb_msg_remove_attr(msg, search_sub[i].replace);
640                         }
641                         if (search_sub[i].extra_attr != NULL && 
642                             !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
643                                 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
644                         }
645                 }
646         }
647
648         return 0;
649
650 failed:
651         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
652                       "operational_search_post_process failed for attribute '%s'",
653                       attrs_from_user[a]);
654         return -1;
655 }
656
657 /*
658   hook search operations
659 */
660
661 struct operational_context {
662         struct ldb_module *module;
663         struct ldb_request *req;
664         enum ldb_scope scope;
665         const char * const *attrs;
666         struct op_controls_flags* controls_flags;
667 };
668
669 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
670 {
671         struct operational_context *ac;
672         int ret;
673
674         ac = talloc_get_type(req->context, struct operational_context);
675
676         if (!ares) {
677                 return ldb_module_done(ac->req, NULL, NULL,
678                                         LDB_ERR_OPERATIONS_ERROR);
679         }
680         if (ares->error != LDB_SUCCESS) {
681                 return ldb_module_done(ac->req, ares->controls,
682                                         ares->response, ares->error);
683         }
684
685         switch (ares->type) {
686         case LDB_REPLY_ENTRY:
687                 /* for each record returned post-process to add any derived
688                    attributes that have been asked for */
689                 ret = operational_search_post_process(ac->module,
690                                                       ares->message,
691                                                       ac->scope,
692                                                       ac->attrs,
693                                                       req->op.search.attrs,
694                                                       ac->controls_flags);
695                 if (ret != 0) {
696                         return ldb_module_done(ac->req, NULL, NULL,
697                                                 LDB_ERR_OPERATIONS_ERROR);
698                 }
699                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
700
701         case LDB_REPLY_REFERRAL:
702                 return ldb_module_send_referral(ac->req, ares->referral);
703
704         case LDB_REPLY_DONE:
705
706                 return ldb_module_done(ac->req, ares->controls,
707                                         ares->response, LDB_SUCCESS);
708         }
709
710         talloc_free(ares);
711         return LDB_SUCCESS;
712 }
713
714 static int operational_search(struct ldb_module *module, struct ldb_request *req)
715 {
716         struct ldb_context *ldb;
717         struct operational_context *ac;
718         struct ldb_request *down_req;
719         const char **search_attrs = NULL;
720         unsigned int i, a;
721         int ret;
722
723         /* There are no operational attributes on special DNs */
724         if (ldb_dn_is_special(req->op.search.base)) {
725                 return ldb_next_request(module, req);
726         }
727
728         ldb = ldb_module_get_ctx(module);
729
730         ac = talloc(req, struct operational_context);
731         if (ac == NULL) {
732                 return ldb_oom(ldb);
733         }
734
735         ac->module = module;
736         ac->req = req;
737         ac->scope = req->op.search.scope;
738         ac->attrs = req->op.search.attrs;
739
740         /*  FIXME: We must copy the tree and keep the original
741          *  unmodified. SSS */
742         /* replace any attributes in the parse tree that are
743            searchable, but are stored using a different name in the
744            backend */
745         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
746                 ldb_parse_tree_attr_replace(req->op.search.tree,
747                                             parse_tree_sub[i].attr,
748                                             parse_tree_sub[i].replace);
749         }
750
751         ac->controls_flags = talloc(ac, struct op_controls_flags);
752         /* remember if the SD_FLAGS_OID was set */
753         ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
754         /* remember if the LDB_CONTROL_BYPASSOPERATIONAL_OID */
755         ac->controls_flags->bypassoperational = (ldb_request_get_control(req,
756                                                         LDB_CONTROL_BYPASSOPERATIONAL_OID) != NULL);
757
758         /* in the list of attributes we are looking for, rename any
759            attributes to the alias for any hidden attributes that can
760            be fetched directly using non-hidden names */
761         for (a=0;ac->attrs && ac->attrs[a];a++) {
762                 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
763                         continue;
764                 }
765                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
766                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
767                             search_sub[i].replace) {
768
769                                 if (search_sub[i].extra_attr) {
770                                         const char **search_attrs2;
771                                         /* Only adds to the end of the list */
772                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
773                                                                                ? search_attrs
774                                                                                : ac->attrs, 
775                                                                                search_sub[i].extra_attr);
776                                         if (search_attrs2 == NULL) {
777                                                 return ldb_operr(ldb);
778                                         }
779                                         /* may be NULL, talloc_free() doesn't mind */
780                                         talloc_free(search_attrs);
781                                         search_attrs = search_attrs2;
782                                 }
783
784                                 if (!search_attrs) {
785                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
786                                         if (search_attrs == NULL) {
787                                                 return ldb_operr(ldb);
788                                         }
789                                 }
790                                 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
791                                 search_attrs[a] = search_sub[i].replace;
792                         }
793                 }
794         }
795
796         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
797                                         req->op.search.base,
798                                         req->op.search.scope,
799                                         req->op.search.tree,
800                                         /* use new set of attrs if any */
801                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
802                                         req->controls,
803                                         ac, operational_callback,
804                                         req);
805         if (ret != LDB_SUCCESS) {
806                 return ldb_operr(ldb);
807         }
808
809         /* perform the search */
810         return ldb_next_request(module, down_req);
811 }
812
813 static int operational_init(struct ldb_module *ctx)
814 {
815         struct operational_data *data;
816         int ret;
817         auth_init();
818
819         ret = ldb_next_init(ctx);
820
821         if (ret != LDB_SUCCESS) {
822                 return ret;
823         }
824
825         data = talloc_zero(ctx, struct operational_data);
826         if (!data) {
827                 return ldb_module_oom(ctx);
828         }
829
830         ldb_module_set_private(ctx, data);
831
832         return LDB_SUCCESS;
833 }
834
835 _PUBLIC_ const struct ldb_module_ops ldb_operational_module_ops = {
836         .name              = "operational",
837         .search            = operational_search,
838         .init_context      = operational_init
839 };