0e8eb87501fd5c4838bed90a4b765c0b763183db
[samba.git] / source4 / dsdb / samdb / ldb_modules / operational.c
1 /*
2    ldb database library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2010
5    Copyright (C) Andrew Tridgell 2005
6    Copyright (C) Simo Sorce 2006-2008
7    Copyright (C) Matthias Dieter Wallnöfer 2009
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /*
24   handle operational attributes
25  */
26
27 /*
28   createTimeStamp: HIDDEN, searchable, ldaptime, alias for whenCreated
29   modifyTimeStamp: HIDDEN, searchable, ldaptime, alias for whenChanged
30
31      for the above two, we do the search as normal, and if
32      createTimeStamp or modifyTimeStamp is asked for, then do
33      additional searches for whenCreated and whenChanged and fill in
34      the resulting values
35
36      we also need to replace these with the whenCreated/whenChanged
37      equivalent in the search expression trees
38
39   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
40   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
41
42      on init we need to setup attribute handlers for these so
43      comparisons are done correctly. The resolution is 1 second.
44
45      on add we need to add both the above, for current time
46
47      on modify we need to change whenChanged
48
49   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
50
51      for this one we do the search as normal, then if requested ask
52      for objectclass, change the attribute name, and add it
53
54   primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
55
56      contains the RID of a certain group object
57     
58
59   attributeTypes: in schema only
60   objectClasses: in schema only
61   matchingRules: in schema only
62   matchingRuleUse: in schema only
63   creatorsName: not supported by w2k3?
64   modifiersName: not supported by w2k3?
65 */
66
67 #include "includes.h"
68 #include <ldb.h>
69 #include <ldb_module.h>
70
71 #include "librpc/gen_ndr/ndr_misc.h"
72 #include "librpc/gen_ndr/ndr_drsblobs.h"
73 #include "param/param.h"
74 #include "dsdb/samdb/samdb.h"
75 #include "dsdb/samdb/ldb_modules/util.h"
76
77 #include "libcli/security/security.h"
78
79 #ifndef ARRAY_SIZE
80 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
81 #endif
82
83 struct operational_data {
84         struct ldb_dn *aggregate_dn;
85 };
86
87 enum search_type {
88         TOKEN_GROUPS,
89         TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL,
90         TOKEN_GROUPS_NO_GC_ACCEPTABLE,
91
92         /*
93          * MS-DRSR 4.1.8.1.3 RevMembGetAccountGroups: Transitive membership in
94          * all account groups in a given domain, excluding built-in groups.
95          * (Used internally for msDS-ResultantPSO support)
96          */
97         ACCOUNT_GROUPS
98 };
99
100 /*
101   construct a canonical name from a message
102 */
103 static int construct_canonical_name(struct ldb_module *module,
104                                     struct ldb_message *msg, enum ldb_scope scope,
105                                     struct ldb_request *parent)
106 {
107         char *canonicalName;
108         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
109         if (canonicalName == NULL) {
110                 return ldb_operr(ldb_module_get_ctx(module));
111         }
112         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
113 }
114
115 /*
116   construct a primary group token for groups from a message
117 */
118 static int construct_primary_group_token(struct ldb_module *module,
119                                          struct ldb_message *msg, enum ldb_scope scope,
120                                          struct ldb_request *parent)
121 {
122         struct ldb_context *ldb;
123         uint32_t primary_group_token;
124         
125         ldb = ldb_module_get_ctx(module);
126         if (ldb_match_msg_objectclass(msg, "group") == 1) {
127                 primary_group_token
128                         = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
129                 if (primary_group_token == 0) {
130                         return LDB_SUCCESS;
131                 }
132
133                 return samdb_msg_add_uint(ldb, msg, msg, "primaryGroupToken",
134                         primary_group_token);
135         } else {
136                 return LDB_SUCCESS;
137         }
138 }
139
140 /*
141  * Returns the group SIDs for the user in the given LDB message
142  */
143 static int get_group_sids(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
144                           struct ldb_message *msg, const char *attribute_string,
145                           enum search_type type, struct dom_sid **groupSIDs,
146                           unsigned int *num_groupSIDs)
147 {
148         const char *filter = NULL;
149         NTSTATUS status;
150         struct dom_sid *primary_group_sid;
151         const char *primary_group_string;
152         const char *primary_group_dn;
153         DATA_BLOB primary_group_blob;
154         struct dom_sid *account_sid;
155         const char *account_sid_string;
156         const char *account_sid_dn;
157         DATA_BLOB account_sid_blob;
158         struct dom_sid *domain_sid;
159
160         /* If it's not a user, it won't have a primaryGroupID */
161         if (ldb_msg_find_element(msg, "primaryGroupID") == NULL) {
162                 return LDB_SUCCESS;
163         }
164
165         /* Ensure it has an objectSID too */
166         account_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
167         if (account_sid == NULL) {
168                 return LDB_SUCCESS;
169         }
170
171         status = dom_sid_split_rid(mem_ctx, account_sid, &domain_sid, NULL);
172         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
173                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
174         } else if (!NT_STATUS_IS_OK(status)) {
175                 return LDB_ERR_OPERATIONS_ERROR;
176         }
177
178         primary_group_sid = dom_sid_add_rid(mem_ctx,
179                                             domain_sid,
180                                             ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
181         if (!primary_group_sid) {
182                 return ldb_oom(ldb);
183         }
184
185         /* only return security groups */
186         switch(type) {
187         case TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL:
188                 filter = talloc_asprintf(mem_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u)(|(groupType:1.2.840.113556.1.4.803:=%u)(groupType:1.2.840.113556.1.4.803:=%u)))",
189                                          GROUP_TYPE_SECURITY_ENABLED, GROUP_TYPE_ACCOUNT_GROUP, GROUP_TYPE_UNIVERSAL_GROUP);
190                 break;
191         case TOKEN_GROUPS_NO_GC_ACCEPTABLE:
192         case TOKEN_GROUPS:
193                 filter = talloc_asprintf(mem_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u))",
194                                          GROUP_TYPE_SECURITY_ENABLED);
195                 break;
196
197         /* for RevMembGetAccountGroups, exclude built-in groups */
198         case ACCOUNT_GROUPS:
199                 filter = talloc_asprintf(mem_ctx, "(&(objectClass=group)(!(groupType:1.2.840.113556.1.4.803:=%u))(groupType:1.2.840.113556.1.4.803:=%u))",
200                                 GROUP_TYPE_BUILTIN_LOCAL_GROUP, GROUP_TYPE_SECURITY_ENABLED);
201                 break;
202         }
203
204         if (!filter) {
205                 return ldb_oom(ldb);
206         }
207
208         primary_group_string = dom_sid_string(mem_ctx, primary_group_sid);
209         if (!primary_group_string) {
210                 return ldb_oom(ldb);
211         }
212
213         primary_group_dn = talloc_asprintf(mem_ctx, "<SID=%s>", primary_group_string);
214         if (!primary_group_dn) {
215                 return ldb_oom(ldb);
216         }
217
218         primary_group_blob = data_blob_string_const(primary_group_dn);
219
220         account_sid_string = dom_sid_string(mem_ctx, account_sid);
221         if (!account_sid_string) {
222                 return ldb_oom(ldb);
223         }
224
225         account_sid_dn = talloc_asprintf(mem_ctx, "<SID=%s>", account_sid_string);
226         if (!account_sid_dn) {
227                 return ldb_oom(ldb);
228         }
229
230         account_sid_blob = data_blob_string_const(account_sid_dn);
231
232         status = dsdb_expand_nested_groups(ldb, &account_sid_blob,
233                                            true, /* We don't want to add the object's SID itself,
234                                                     it's not returend in this attribute */
235                                            filter,
236                                            mem_ctx, groupSIDs, num_groupSIDs);
237
238         if (!NT_STATUS_IS_OK(status)) {
239                 ldb_asprintf_errstring(ldb, "Failed to construct %s: expanding groups of SID %s failed: %s",
240                                        attribute_string, account_sid_string,
241                                        nt_errstr(status));
242                 return LDB_ERR_OPERATIONS_ERROR;
243         }
244
245         /* Expands the primary group - this function takes in
246          * memberOf-like values, so we fake one up with the
247          * <SID=S-...> format of DN and then let it expand
248          * them, as long as they meet the filter - so only
249          * domain groups, not builtin groups
250          */
251         status = dsdb_expand_nested_groups(ldb, &primary_group_blob, false, filter,
252                                            mem_ctx, groupSIDs, num_groupSIDs);
253         if (!NT_STATUS_IS_OK(status)) {
254                 ldb_asprintf_errstring(ldb, "Failed to construct %s: expanding groups of SID %s failed: %s",
255                                        attribute_string, account_sid_string,
256                                        nt_errstr(status));
257                 return LDB_ERR_OPERATIONS_ERROR;
258         }
259
260         return LDB_SUCCESS;
261 }
262
263 /*
264   construct the token groups for SAM objects from a message
265 */
266 static int construct_generic_token_groups(struct ldb_module *module,
267                                           struct ldb_message *msg, enum ldb_scope scope,
268                                           struct ldb_request *parent,
269                                           const char *attribute_string,
270                                           enum search_type type)
271 {
272         struct ldb_context *ldb = ldb_module_get_ctx(module);
273         TALLOC_CTX *tmp_ctx = talloc_new(msg);
274         unsigned int i;
275         int ret;
276         struct dom_sid *groupSIDs = NULL;
277         unsigned int num_groupSIDs = 0;
278
279         if (scope != LDB_SCOPE_BASE) {
280                 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
281                 return LDB_ERR_OPERATIONS_ERROR;
282         }
283
284         /* calculate the group SIDs for this object */
285         ret = get_group_sids(ldb, tmp_ctx, msg, attribute_string, type,
286                              &groupSIDs, &num_groupSIDs);
287
288         if (ret != LDB_SUCCESS) {
289                 talloc_free(tmp_ctx);
290                 return LDB_ERR_OPERATIONS_ERROR;
291         }
292
293         /* add these SIDs to the search result */
294         for (i=0; i < num_groupSIDs; i++) {
295                 ret = samdb_msg_add_dom_sid(ldb, msg, msg, attribute_string, &groupSIDs[i]);
296                 if (ret) {
297                         talloc_free(tmp_ctx);
298                         return ret;
299                 }
300         }
301
302         return LDB_SUCCESS;
303 }
304
305 static int construct_token_groups(struct ldb_module *module,
306                                   struct ldb_message *msg, enum ldb_scope scope,
307                                   struct ldb_request *parent)
308 {
309         /**
310          * TODO: Add in a limiting domain when we start to support
311          * trusted domains.
312          */
313         return construct_generic_token_groups(module, msg, scope, parent,
314                                               "tokenGroups",
315                                               TOKEN_GROUPS);
316 }
317
318 static int construct_token_groups_no_gc(struct ldb_module *module,
319                                         struct ldb_message *msg, enum ldb_scope scope,
320                                         struct ldb_request *parent)
321 {
322         /**
323          * TODO: Add in a limiting domain when we start to support
324          * trusted domains.
325          */
326         return construct_generic_token_groups(module, msg, scope, parent,
327                                               "tokenGroupsNoGCAcceptable",
328                                               TOKEN_GROUPS);
329 }
330
331 static int construct_global_universal_token_groups(struct ldb_module *module,
332                                                    struct ldb_message *msg, enum ldb_scope scope,
333                                                    struct ldb_request *parent)
334 {
335         return construct_generic_token_groups(module, msg, scope, parent,
336                                               "tokenGroupsGlobalAndUniversal",
337                                               TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL);
338 }
339 /*
340   construct the parent GUID for an entry from a message
341 */
342 static int construct_parent_guid(struct ldb_module *module,
343                                  struct ldb_message *msg, enum ldb_scope scope,
344                                  struct ldb_request *parent)
345 {
346         struct ldb_result *res, *parent_res;
347         const struct ldb_val *parent_guid;
348         const char *attrs[] = { "instanceType", NULL };
349         const char *attrs2[] = { "objectGUID", NULL };
350         uint32_t instanceType;
351         int ret;
352         struct ldb_dn *parent_dn;
353         struct ldb_val v;
354
355         /* determine if the object is NC by instance type */
356         ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
357                                     DSDB_FLAG_NEXT_MODULE |
358                                     DSDB_SEARCH_SHOW_RECYCLED, parent);
359         if (ret != LDB_SUCCESS) {
360                 return ret;
361         }
362
363         instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
364                                                  "instanceType", 0);
365         talloc_free(res);
366         if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
367                 DEBUG(4,(__location__ ": Object %s is NC\n",
368                          ldb_dn_get_linearized(msg->dn)));
369                 return LDB_SUCCESS;
370         }
371         parent_dn = ldb_dn_get_parent(msg, msg->dn);
372
373         if (parent_dn == NULL) {
374                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
375                                          ldb_dn_get_linearized(msg->dn)));
376                 return LDB_ERR_OTHER;
377         }
378         ret = dsdb_module_search_dn(module, msg, &parent_res, parent_dn, attrs2,
379                                     DSDB_FLAG_NEXT_MODULE |
380                                     DSDB_SEARCH_SHOW_RECYCLED, parent);
381         /* not NC, so the object should have a parent*/
382         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
383                 ret = ldb_error(ldb_module_get_ctx(module), LDB_ERR_OPERATIONS_ERROR, 
384                                  talloc_asprintf(msg, "Parent dn %s for %s does not exist",
385                                                  ldb_dn_get_linearized(parent_dn),
386                                                  ldb_dn_get_linearized(msg->dn)));
387                 talloc_free(parent_dn);
388                 return ret;
389         } else if (ret != LDB_SUCCESS) {
390                 talloc_free(parent_dn);
391                 return ret;
392         }
393         talloc_free(parent_dn);
394
395         parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
396         if (!parent_guid) {
397                 talloc_free(parent_res);
398                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
399         }
400
401         v = data_blob_dup_talloc(parent_res, *parent_guid);
402         if (!v.data) {
403                 talloc_free(parent_res);
404                 return ldb_oom(ldb_module_get_ctx(module));
405         }
406         ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
407         talloc_free(parent_res);
408         return ret;
409 }
410
411 static int construct_modifyTimeStamp(struct ldb_module *module,
412                                         struct ldb_message *msg, enum ldb_scope scope,
413                                         struct ldb_request *parent)
414 {
415         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
416         struct ldb_context *ldb = ldb_module_get_ctx(module);
417
418         /* We may be being called before the init function has finished */
419         if (!data) {
420                 return LDB_SUCCESS;
421         }
422
423         /* Try and set this value up, if possible.  Don't worry if it
424          * fails, we may not have the DB set up yet.
425          */
426         if (!data->aggregate_dn) {
427                 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
428         }
429
430         if (data->aggregate_dn && ldb_dn_compare(data->aggregate_dn, msg->dn) == 0) {
431                 /*
432                  * If we have the DN for the object with common name = Aggregate and
433                  * the request is for this DN then let's do the following:
434                  * 1) search the object which changedUSN correspond to the one of the loaded
435                  * schema.
436                  * 2) Get the whenChanged attribute
437                  * 3) Generate the modifyTimestamp out of the whenChanged attribute
438                  */
439                 const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
440                 char *value = ldb_timestring(msg, schema->ts_last_change);
441
442                 return ldb_msg_add_string(msg, "modifyTimeStamp", value);
443         }
444         return ldb_msg_copy_attr(msg, "whenChanged", "modifyTimeStamp");
445 }
446
447 /*
448   construct a subSchemaSubEntry
449 */
450 static int construct_subschema_subentry(struct ldb_module *module,
451                                         struct ldb_message *msg, enum ldb_scope scope,
452                                         struct ldb_request *parent)
453 {
454         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
455         char *subSchemaSubEntry;
456
457         /* We may be being called before the init function has finished */
458         if (!data) {
459                 return LDB_SUCCESS;
460         }
461
462         /* Try and set this value up, if possible.  Don't worry if it
463          * fails, we may not have the DB set up yet, and it's not
464          * really vital anyway */
465         if (!data->aggregate_dn) {
466                 struct ldb_context *ldb = ldb_module_get_ctx(module);
467                 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
468         }
469
470         if (data->aggregate_dn) {
471                 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
472                 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
473         }
474         return LDB_SUCCESS;
475 }
476
477
478 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
479                                          struct ldb_message *msg,
480                                          struct ldb_message_element *object_category)
481 {
482         struct ldb_context *ldb;
483         struct ldb_dn *dn;
484         const struct ldb_val *val;
485
486         ldb = ldb_module_get_ctx(module);
487         if (!ldb) {
488                 DEBUG(4, (__location__ ": Failed to get ldb \n"));
489                 return LDB_ERR_OPERATIONS_ERROR;
490         }
491
492         dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
493         if (!dn) {
494                 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
495                           (const char *)object_category->values[0].data));
496                 return ldb_operr(ldb);
497         }
498
499         val = ldb_dn_get_rdn_val(dn);
500         if (!val) {
501                 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
502                           ldb_dn_get_linearized(dn)));
503                 return ldb_operr(ldb);
504         }
505
506         if (strequal((const char *)val->data, "NTDS-DSA")) {
507                 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
508         } else {
509                 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
510         }
511         return LDB_SUCCESS;
512 }
513
514 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
515                                                 struct ldb_message *msg,
516                                                 struct ldb_dn *dn,
517                                                 struct ldb_request *parent)
518 {
519         struct ldb_dn *server_dn;
520         const char *attr_obj_cat[] = { "objectCategory", NULL };
521         struct ldb_result *res;
522         struct ldb_message_element *object_category;
523         int ret;
524
525         server_dn = ldb_dn_copy(msg, dn);
526         if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
527                 DEBUG(4, (__location__ ": Failed to add child to %s \n",
528                           ldb_dn_get_linearized(server_dn)));
529                 return ldb_operr(ldb_module_get_ctx(module));
530         }
531
532         ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
533                                     DSDB_FLAG_NEXT_MODULE, parent);
534         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
535                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
536                                          ldb_dn_get_linearized(server_dn)));
537                 return LDB_SUCCESS;
538         } else if (ret != LDB_SUCCESS) {
539                 return ret;
540         }
541
542         object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
543         if (!object_category) {
544                 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
545                          ldb_dn_get_linearized(res->msgs[0]->dn)));
546                 return LDB_SUCCESS;
547         }
548         return construct_msds_isrodc_with_dn(module, msg, object_category);
549 }
550
551 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
552                                                   struct ldb_message *msg,
553                                                   struct ldb_request *parent)
554 {
555         int ret;
556         struct ldb_dn *server_dn;
557
558         ret = dsdb_module_reference_dn(module, msg, msg->dn, "serverReferenceBL",
559                                        &server_dn, parent);
560         if (ret == LDB_ERR_NO_SUCH_OBJECT || ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
561                 /* it's OK if we can't find serverReferenceBL attribute */
562                 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
563                          ldb_dn_get_linearized(msg->dn)));
564                 return LDB_SUCCESS;
565         } else if (ret != LDB_SUCCESS) {
566                 return ret;
567         }
568
569         return construct_msds_isrodc_with_server_dn(module, msg, server_dn, parent);
570 }
571
572 /*
573   construct msDS-isRODC attr
574 */
575 static int construct_msds_isrodc(struct ldb_module *module,
576                                  struct ldb_message *msg, enum ldb_scope scope,
577                                  struct ldb_request *parent)
578 {
579         struct ldb_message_element * object_class;
580         struct ldb_message_element * object_category;
581         unsigned int i;
582
583         object_class = ldb_msg_find_element(msg, "objectClass");
584         if (!object_class) {
585                 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
586                          ldb_dn_get_linearized(msg->dn)));
587                 return ldb_operr(ldb_module_get_ctx(module));
588         }
589
590         for (i=0; i<object_class->num_values; i++) {
591                 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
592                         /* If TO!objectCategory  equals the DN of the classSchema  object for the nTDSDSA
593                          * object class, then TO!msDS-isRODC  is false. Otherwise, TO!msDS-isRODC  is true.
594                          */
595                         object_category = ldb_msg_find_element(msg, "objectCategory");
596                         if (!object_category) {
597                                 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
598                                          ldb_dn_get_linearized(msg->dn)));
599                                 return LDB_SUCCESS;
600                         }
601                         return construct_msds_isrodc_with_dn(module, msg, object_category);
602                 }
603                 if (strequal((const char*)object_class->values[i].data, "server")) {
604                         /* Let TN be the nTDSDSA  object whose DN is "CN=NTDS Settings," prepended to
605                          * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA  object" case,
606                          * substituting TN for TO.
607                          */
608                         return construct_msds_isrodc_with_server_dn(module, msg, msg->dn, parent);
609                 }
610                 if (strequal((const char*)object_class->values[i].data, "computer")) {
611                         /* Let TS be the server  object named by TO!serverReferenceBL. Apply the previous
612                          * rule for the "TO is a server  object" case, substituting TS for TO.
613                          */
614                         return construct_msds_isrodc_with_computer_dn(module, msg, parent);
615                 }
616         }
617
618         return LDB_SUCCESS;
619 }
620
621
622 /*
623   construct msDS-keyVersionNumber attr
624
625   TODO:  Make this based on the 'win2k' DS huristics bit...
626
627 */
628 static int construct_msds_keyversionnumber(struct ldb_module *module,
629                                            struct ldb_message *msg,
630                                            enum ldb_scope scope,
631                                            struct ldb_request *parent)
632 {
633         uint32_t i;
634         enum ndr_err_code ndr_err;
635         const struct ldb_val *omd_value;
636         struct replPropertyMetaDataBlob *omd;
637         int ret;
638
639         omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
640         if (!omd_value) {
641                 /* We can't make up a key version number without meta data */
642                 return LDB_SUCCESS;
643         }
644
645         omd = talloc(msg, struct replPropertyMetaDataBlob);
646         if (!omd) {
647                 ldb_module_oom(module);
648                 return LDB_SUCCESS;
649         }
650
651         ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
652                                        (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
653         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
654                 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
655                          ldb_dn_get_linearized(msg->dn)));
656                 return ldb_operr(ldb_module_get_ctx(module));
657         }
658
659         if (omd->version != 1) {
660                 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
661                          omd->version, ldb_dn_get_linearized(msg->dn)));
662                 talloc_free(omd);
663                 return LDB_SUCCESS;
664         }
665         for (i=0; i<omd->ctr.ctr1.count; i++) {
666                 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
667                         ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
668                                                  msg, msg,
669                                                  "msDS-KeyVersionNumber",
670                                                  omd->ctr.ctr1.array[i].version);
671                         if (ret != LDB_SUCCESS) {
672                                 talloc_free(omd);
673                                 return ret;
674                         }
675                         break;
676                 }
677         }
678         return LDB_SUCCESS;
679
680 }
681
682 #define _UF_TRUST_ACCOUNTS ( \
683         UF_WORKSTATION_TRUST_ACCOUNT | \
684         UF_SERVER_TRUST_ACCOUNT | \
685         UF_INTERDOMAIN_TRUST_ACCOUNT \
686 )
687 #define _UF_NO_EXPIRY_ACCOUNTS ( \
688         UF_SMARTCARD_REQUIRED | \
689         UF_DONT_EXPIRE_PASSWD | \
690         _UF_TRUST_ACCOUNTS \
691 )
692
693 /*
694   calculate msDS-UserPasswordExpiryTimeComputed
695 */
696 static NTTIME get_msds_user_password_expiry_time_computed(struct ldb_module *module,
697                                                 struct ldb_message *msg,
698                                                 struct ldb_dn *domain_dn)
699 {
700         int64_t pwdLastSet, maxPwdAge;
701         uint32_t userAccountControl;
702         NTTIME ret;
703
704         userAccountControl = ldb_msg_find_attr_as_uint(msg,
705                                         "userAccountControl",
706                                         0);
707         if (userAccountControl & _UF_NO_EXPIRY_ACCOUNTS) {
708                 return 0x7FFFFFFFFFFFFFFFULL;
709         }
710
711         pwdLastSet = ldb_msg_find_attr_as_int64(msg, "pwdLastSet", 0);
712         if (pwdLastSet == 0) {
713                 return 0;
714         }
715
716         if (pwdLastSet <= -1) {
717                 /*
718                  * This can't really happen...
719                  */
720                 return 0x7FFFFFFFFFFFFFFFULL;
721         }
722
723         if (pwdLastSet >= 0x7FFFFFFFFFFFFFFFLL) {
724                 /*
725                  * Somethings wrong with the clock...
726                  */
727                 return 0x7FFFFFFFFFFFFFFFULL;
728         }
729
730         /*
731          * Note that maxPwdAge is a stored as negative value.
732          *
733          * Possible values are in the range of:
734          *
735          * maxPwdAge: -864000000001
736          * to
737          * maxPwdAge: -9223372036854775808 (-0x8000000000000000ULL)
738          *
739          */
740         maxPwdAge = samdb_search_int64(ldb_module_get_ctx(module), msg, 0,
741                                        domain_dn, "maxPwdAge", NULL);
742         if (maxPwdAge >= -864000000000) {
743                 /*
744                  * This is not really possible...
745                  */
746                 return 0x7FFFFFFFFFFFFFFFULL;
747         }
748
749         if (maxPwdAge == -0x8000000000000000LL) {
750                 return 0x7FFFFFFFFFFFFFFFULL;
751         }
752
753         /*
754          * Note we already catched maxPwdAge == -0x8000000000000000ULL
755          * and pwdLastSet >= 0x7FFFFFFFFFFFFFFFULL above.
756          *
757          * Remember maxPwdAge is a negative number,
758          * so it results in the following.
759          *
760          * 0x7FFFFFFFFFFFFFFEULL + 0x7FFFFFFFFFFFFFFFULL
761          * =
762          * 0xFFFFFFFFFFFFFFFFULL
763          */
764         ret = (NTTIME)pwdLastSet - (NTTIME)maxPwdAge;
765         if (ret >= 0x7FFFFFFFFFFFFFFFULL) {
766                 return 0x7FFFFFFFFFFFFFFFULL;
767         }
768
769         return ret;
770 }
771
772
773 /*
774   construct msDS-User-Account-Control-Computed attr
775 */
776 static int construct_msds_user_account_control_computed(struct ldb_module *module,
777                                                         struct ldb_message *msg, enum ldb_scope scope,
778                                                         struct ldb_request *parent)
779 {
780         uint32_t userAccountControl;
781         uint32_t msDS_User_Account_Control_Computed = 0;
782         struct ldb_context *ldb = ldb_module_get_ctx(module);
783         NTTIME now;
784         struct ldb_dn *nc_root;
785         int ret;
786
787         ret = dsdb_find_nc_root(ldb, msg, msg->dn, &nc_root);
788         if (ret != 0) {
789                 ldb_asprintf_errstring(ldb,
790                                        "Failed to find NC root of DN: %s: %s",
791                                        ldb_dn_get_linearized(msg->dn),
792                                        ldb_errstring(ldb_module_get_ctx(module)));
793                 return ret;
794         }
795         if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) != 0) {
796                 /* Only calculate this on our default NC */
797                 return 0;
798         }
799         /* Test account expire time */
800         unix_to_nt_time(&now, time(NULL));
801
802         userAccountControl = ldb_msg_find_attr_as_uint(msg,
803                                                        "userAccountControl",
804                                                        0);
805         if (!(userAccountControl & _UF_TRUST_ACCOUNTS)) {
806
807                 int64_t lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
808                 if (lockoutTime != 0) {
809                         int64_t lockoutDuration = samdb_search_int64(ldb,
810                                                                      msg, 0, nc_root,
811                                                                      "lockoutDuration", NULL);
812                         if (lockoutDuration >= 0) {
813                                 msDS_User_Account_Control_Computed |= UF_LOCKOUT;
814                         } else if (lockoutTime - lockoutDuration >= now) {
815                                 msDS_User_Account_Control_Computed |= UF_LOCKOUT;
816                         }
817                 }
818         }
819
820         if (!(userAccountControl & _UF_NO_EXPIRY_ACCOUNTS)) {
821                 NTTIME must_change_time
822                         = get_msds_user_password_expiry_time_computed(module,
823                                                                       msg, nc_root);
824                 /* check for expired password */
825                 if (must_change_time < now) {
826                         msDS_User_Account_Control_Computed |= UF_PASSWORD_EXPIRED;
827                 }
828         }
829
830         return samdb_msg_add_int64(ldb,
831                                    msg->elements, msg,
832                                    "msDS-User-Account-Control-Computed",
833                                    msDS_User_Account_Control_Computed);
834 }
835
836 /*
837   construct msDS-UserPasswordExpiryTimeComputed
838 */
839 static int construct_msds_user_password_expiry_time_computed(struct ldb_module *module,
840                                                              struct ldb_message *msg, enum ldb_scope scope,
841                                                              struct ldb_request *parent)
842 {
843         struct ldb_context *ldb = ldb_module_get_ctx(module);
844         struct ldb_dn *nc_root;
845         int64_t password_expiry_time;
846         int ret;
847
848         ret = dsdb_find_nc_root(ldb, msg, msg->dn, &nc_root);
849         if (ret != 0) {
850                 ldb_asprintf_errstring(ldb,
851                                        "Failed to find NC root of DN: %s: %s",
852                                        ldb_dn_get_linearized(msg->dn),
853                                        ldb_errstring(ldb));
854                 return ret;
855         }
856
857         if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) != 0) {
858                 /* Only calculate this on our default NC */
859                 return 0;
860         }
861
862         password_expiry_time
863                 = get_msds_user_password_expiry_time_computed(module, msg,
864                                                               nc_root);
865
866         return samdb_msg_add_int64(ldb,
867                                    msg->elements, msg,
868                                    "msDS-UserPasswordExpiryTimeComputed",
869                                    password_expiry_time);
870 }
871
872 /*
873  * Checks whether the msDS-ResultantPSO attribute is supported for a given
874  * user object. As per MS-ADTS, section 3.1.1.4.5.36 msDS-ResultantPSO.
875  */
876 static bool pso_is_supported(struct ldb_context *ldb, struct ldb_message *msg)
877 {
878         int functional_level;
879         uint32_t uac;
880         uint32_t user_rid;
881
882         functional_level = dsdb_functional_level(ldb);
883         if (functional_level < DS_DOMAIN_FUNCTION_2008) {
884                 return false;
885         }
886
887         /* msDS-ResultantPSO is only supported for user objects */
888         if (!ldb_match_msg_objectclass(msg, "user")) {
889                 return false;
890         }
891
892         /* ...and only if the ADS_UF_NORMAL_ACCOUNT bit is set */
893         uac = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
894         if (!(uac & UF_NORMAL_ACCOUNT)) {
895                 return false;
896         }
897
898         /* skip it if it's the special KRBTGT default account */
899         user_rid = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
900         if (user_rid == DOMAIN_RID_KRBTGT) {
901                 return false;
902         }
903
904         /* ...or if it's a special KRBTGT account for an RODC KDC */
905         if (ldb_msg_find_ldb_val(msg, "msDS-SecondaryKrbTgtNumber") != NULL) {
906                 return false;
907         }
908
909         return true;
910 }
911
912 /*
913  * Compares two PSO objects returned by a search, to work out the better PSO.
914  * The PSO with the lowest precedence is better, otherwise (if the precedence
915  * is equal) the PSO with the lower GUID wins.
916  */
917 static int pso_compare(struct ldb_message **m1, struct ldb_message **m2,
918                        TALLOC_CTX *mem_ctx)
919 {
920         uint32_t prec1;
921         uint32_t prec2;
922
923         prec1 = ldb_msg_find_attr_as_uint(*m1, "msDS-PasswordSettingsPrecedence",
924                                           0xffffffff);
925         prec2 = ldb_msg_find_attr_as_uint(*m2, "msDS-PasswordSettingsPrecedence",
926                                           0xffffffff);
927
928         /* if precedence is equal, use the lowest GUID */
929         if (prec1 == prec2) {
930                 struct GUID guid1 = samdb_result_guid(*m1, "objectGUID");
931                 struct GUID guid2 = samdb_result_guid(*m2, "objectGUID");
932
933                 return ndr_guid_compare(&guid1, &guid2);
934         } else {
935                 return prec1 - prec2;
936         }
937 }
938
939 /*
940  * Search for PSO objects that apply to the object SIDs specified
941  */
942 static int pso_search_by_sids(struct ldb_module *module, TALLOC_CTX *mem_ctx,
943                               struct ldb_request *parent,
944                               struct dom_sid *sid_array, unsigned int num_sids,
945                               struct ldb_result **result)
946 {
947         int ret;
948         int i;
949         struct ldb_context *ldb = ldb_module_get_ctx(module);
950         char *sid_filter = NULL;
951         struct ldb_dn *domain_dn = NULL;
952         struct ldb_dn *psc_dn = NULL;
953         const char *attrs[] = {
954                 "msDS-PasswordSettingsPrecedence",
955                 "objectGUID",
956                 NULL
957         };
958
959         /* build a query for PSO objects that apply to any of the SIDs given */
960         sid_filter = talloc_strdup(mem_ctx, "");
961
962         for (i = 0; sid_filter && i < num_sids; i++) {
963                 char sid_buf[DOM_SID_STR_BUFLEN] = {0,};
964
965                 dom_sid_string_buf(&sid_array[i], sid_buf, sizeof(sid_buf));
966
967                 sid_filter = talloc_asprintf_append(sid_filter,
968                                                     "(msDS-PSOAppliesTo=<SID=%s>)",
969                                                     sid_buf);
970         }
971
972         if (sid_filter == NULL) {
973                 return ldb_oom(ldb);
974         }
975
976         /* only PSOs located in the Password Settings Container are valid */
977         domain_dn = ldb_get_default_basedn(ldb);
978         psc_dn = ldb_dn_new_fmt(mem_ctx, ldb,
979                                 "CN=Password Settings Container,CN=System,%s",
980                                 ldb_dn_get_linearized(domain_dn));
981         if (psc_dn == NULL) {
982                 return ldb_oom(ldb);
983         }
984
985         ret = dsdb_module_search(module, mem_ctx, result, psc_dn,
986                                  LDB_SCOPE_ONELEVEL, attrs,
987                                  DSDB_FLAG_NEXT_MODULE, parent,
988                                  "(&(objectClass=msDS-PasswordSettings)(|%s))",
989                                  sid_filter);
990         talloc_free(sid_filter);
991         return ret;
992 }
993
994 /*
995  * Returns the best PSO object that applies to the object SID(s) specified
996  */
997 static int pso_find_best(struct ldb_module *module, TALLOC_CTX *mem_ctx,
998                          struct ldb_request *parent, struct dom_sid *sid_array,
999                          unsigned int num_sids, struct ldb_message **best_pso)
1000 {
1001         struct ldb_result *res = NULL;
1002         int ret;
1003
1004         *best_pso = NULL;
1005
1006         /* find any PSOs that apply to the SIDs specified */
1007         ret = pso_search_by_sids(module, mem_ctx, parent, sid_array, num_sids,
1008                                  &res);
1009         if (ret != LDB_SUCCESS) {
1010                 DBG_ERR("Error %d retrieving PSO for SID(s)\n", ret);
1011                 return ret;
1012         }
1013
1014         /* sort the list so that the best PSO is first */
1015         LDB_TYPESAFE_QSORT(res->msgs, res->count, mem_ctx, pso_compare);
1016
1017         if (res->count > 0) {
1018                 *best_pso = res->msgs[0];
1019         }
1020
1021         return LDB_SUCCESS;
1022 }
1023
1024 /*
1025  * Determines the Password Settings Object (PSO) that applies to the given user
1026  */
1027 static int get_pso_for_user(struct ldb_module *module,
1028                             struct ldb_message *user_msg,
1029                             struct ldb_request *parent,
1030                             struct ldb_message **pso_msg)
1031 {
1032         bool pso_supported;
1033         struct dom_sid *groupSIDs = NULL;
1034         unsigned int num_groupSIDs = 0;
1035         struct ldb_context *ldb = ldb_module_get_ctx(module);
1036         struct ldb_message *best_pso = NULL;
1037         int ret;
1038         struct ldb_message_element *el = NULL;
1039         TALLOC_CTX *tmp_ctx = NULL;
1040
1041         *pso_msg = NULL;
1042
1043         /* first, check msDS-ResultantPSO is supported for this object */
1044         pso_supported = pso_is_supported(ldb, user_msg);
1045
1046         if (!pso_supported) {
1047                 return LDB_SUCCESS;
1048         }
1049
1050         tmp_ctx = talloc_new(user_msg);
1051
1052         /*
1053          * if any PSOs apply directly to the user, they are considered first
1054          * before we check group membership PSOs
1055          */
1056         el = ldb_msg_find_element(user_msg, "msDS-PSOApplied");
1057
1058         if (el != NULL && el->num_values > 0) {
1059                 struct dom_sid *user_sid = NULL;
1060
1061                 /* lookup the best PSO object, based on the user's SID */
1062                 user_sid = samdb_result_dom_sid(tmp_ctx, user_msg, "objectSid");
1063
1064                 ret = pso_find_best(module, tmp_ctx, parent, user_sid, 1,
1065                                     &best_pso);
1066                 if (ret != LDB_SUCCESS) {
1067                         talloc_free(tmp_ctx);
1068                         return ret;
1069                 }
1070
1071                 if (best_pso != NULL) {
1072                         *pso_msg = best_pso;
1073                         return LDB_SUCCESS;
1074                 }
1075         }
1076
1077         /*
1078          * If no valid PSO applies directly to the user, then try its groups.
1079          * Work out the SIDs of any account groups the user is a member of
1080          */
1081         ret = get_group_sids(ldb, tmp_ctx, user_msg,
1082                              "msDS-ResultantPSO", ACCOUNT_GROUPS,
1083                              &groupSIDs, &num_groupSIDs);
1084         if (ret != LDB_SUCCESS) {
1085                 DBG_ERR("Error %d determining group SIDs for %s\n", ret,
1086                         ldb_dn_get_linearized(user_msg->dn));
1087                 talloc_free(tmp_ctx);
1088                 return ret;
1089         }
1090
1091         /* lookup the best PSO that applies to any of these groups */
1092         ret = pso_find_best(module, tmp_ctx, parent, groupSIDs,
1093                             num_groupSIDs, &best_pso);
1094         if (ret != LDB_SUCCESS) {
1095                 talloc_free(tmp_ctx);
1096                 return ret;
1097         }
1098
1099         *pso_msg = best_pso;
1100         return LDB_SUCCESS;
1101 }
1102
1103 /*
1104  * Constructs the msDS-ResultantPSO attribute, which is the DN of the Password
1105  * Settings Object (PSO) that applies to that user.
1106  */
1107 static int construct_resultant_pso(struct ldb_module *module,
1108                                    struct ldb_message *msg,
1109                                    enum ldb_scope scope,
1110                                    struct ldb_request *parent)
1111 {
1112         struct ldb_message *pso = NULL;
1113         int ret;
1114
1115         /* work out the PSO (if any) that applies to this user */
1116         ret = get_pso_for_user(module, msg, parent, &pso);
1117         if (ret != LDB_SUCCESS) {
1118                 DBG_ERR("Couldn't determine PSO for %s\n",
1119                         ldb_dn_get_linearized(msg->dn));
1120                 return ret;
1121         }
1122
1123         if (pso != NULL) {
1124                 DBG_INFO("%s is resultant PSO for user %s\n",
1125                          ldb_dn_get_linearized(pso->dn),
1126                          ldb_dn_get_linearized(msg->dn));
1127                 return ldb_msg_add_string(msg, "msDS-ResultantPSO",
1128                                           ldb_dn_get_linearized(pso->dn));
1129         }
1130
1131         /* no PSO applies to this user */
1132         return LDB_SUCCESS;
1133 }
1134
1135 struct op_controls_flags {
1136         bool sd;
1137         bool bypassoperational;
1138 };
1139
1140 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
1141         if (controls_flags->bypassoperational && ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 ) {
1142                 return true;
1143         }
1144         return false;
1145 }
1146
1147 /*
1148   a list of attribute names that should be substituted in the parse
1149   tree before the search is done
1150 */
1151 static const struct {
1152         const char *attr;
1153         const char *replace;
1154 } parse_tree_sub[] = {
1155         { "createTimeStamp", "whenCreated" },
1156         { "modifyTimeStamp", "whenChanged" }
1157 };
1158
1159
1160 struct op_attributes_replace {
1161         const char *attr;
1162         const char *replace;
1163         const char * const *extra_attrs;
1164         int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope, struct ldb_request *);
1165 };
1166
1167
1168 static const char *objectSid_attr[] =
1169 {
1170         "objectSid",
1171         NULL
1172 };
1173
1174
1175 static const char *objectCategory_attr[] =
1176 {
1177         "objectCategory",
1178         NULL
1179 };
1180
1181
1182 static const char *user_account_control_computed_attrs[] =
1183 {
1184         "lockoutTime",
1185         "pwdLastSet",
1186         NULL
1187 };
1188
1189
1190 static const char *user_password_expiry_time_computed_attrs[] =
1191 {
1192         "pwdLastSet",
1193         NULL
1194 };
1195
1196 static const char *resultant_pso_computed_attrs[] =
1197 {
1198         "msDS-PSOApplied",
1199         "userAccountControl",
1200         "objectSid",
1201         "msDS-SecondaryKrbTgtNumber",
1202         "primaryGroupID",
1203         NULL
1204 };
1205
1206 /*
1207   a list of attribute names that are hidden, but can be searched for
1208   using another (non-hidden) name to produce the correct result
1209 */
1210 static const struct op_attributes_replace search_sub[] = {
1211         { "createTimeStamp", "whenCreated", NULL , NULL },
1212         { "modifyTimeStamp", "whenChanged", NULL , construct_modifyTimeStamp},
1213         { "structuralObjectClass", "objectClass", NULL , NULL },
1214         { "canonicalName", NULL, NULL , construct_canonical_name },
1215         { "primaryGroupToken", "objectClass", objectSid_attr, construct_primary_group_token },
1216         { "tokenGroups", "primaryGroupID", objectSid_attr, construct_token_groups },
1217         { "tokenGroupsNoGCAcceptable", "primaryGroupID", objectSid_attr, construct_token_groups_no_gc},
1218         { "tokenGroupsGlobalAndUniversal", "primaryGroupID", objectSid_attr, construct_global_universal_token_groups },
1219         { "parentGUID", NULL, NULL, construct_parent_guid },
1220         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
1221         { "msDS-isRODC", "objectClass", objectCategory_attr, construct_msds_isrodc },
1222         { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber },
1223         { "msDS-User-Account-Control-Computed", "userAccountControl", user_account_control_computed_attrs,
1224           construct_msds_user_account_control_computed },
1225         { "msDS-UserPasswordExpiryTimeComputed", "userAccountControl", user_password_expiry_time_computed_attrs,
1226           construct_msds_user_password_expiry_time_computed },
1227         { "msDS-ResultantPSO", "objectClass", resultant_pso_computed_attrs,
1228           construct_resultant_pso }
1229 };
1230
1231
1232 enum op_remove {
1233         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
1234         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
1235         OPERATIONAL_SD_FLAGS,      /* show if SD_FLAGS_OID set, or asked for */
1236         OPERATIONAL_REMOVE_UNLESS_CONTROL        /* remove always unless an adhoc control has been specified */
1237 };
1238
1239 /*
1240   a list of attributes that may need to be removed from the
1241   underlying db return
1242
1243   Some of these are attributes that were once stored, but are now calculated
1244 */
1245 struct op_attributes_operations {
1246         const char *attr;
1247         enum op_remove op;
1248 };
1249
1250 static const struct op_attributes_operations operational_remove[] = {
1251         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
1252         { "msDS-KeyVersionNumber",   OPERATIONAL_REMOVE_UNLESS_CONTROL  },
1253         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
1254         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
1255 #define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
1256         { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
1257 };
1258
1259
1260 /*
1261   post process a search result record. For any search_sub[] attributes that were
1262   asked for, we need to call the appropriate copy routine to copy the result
1263   into the message, then remove any attributes that we added to the search but
1264   were not asked for by the user
1265 */
1266 static int operational_search_post_process(struct ldb_module *module,
1267                                            struct ldb_message *msg,
1268                                            enum ldb_scope scope,
1269                                            const char * const *attrs_from_user,
1270                                            const char * const *attrs_searched_for,
1271                                            struct op_controls_flags* controls_flags,
1272                                            struct op_attributes_operations *list,
1273                                            unsigned int list_size,
1274                                            struct op_attributes_replace *list_replace,
1275                                            unsigned int list_replace_size,
1276                                            struct ldb_request *parent)
1277 {
1278         struct ldb_context *ldb;
1279         unsigned int i, a = 0;
1280         bool constructed_attributes = false;
1281
1282         ldb = ldb_module_get_ctx(module);
1283
1284         /* removed any attrs that should not be shown to the user */
1285         for (i=0; i < list_size; i++) {
1286                 ldb_msg_remove_attr(msg, list[i].attr);
1287         }
1288
1289         for (a=0; a < list_replace_size; a++) {
1290                 if (check_keep_control_for_attribute(controls_flags,
1291                                                      list_replace[a].attr)) {
1292                         continue;
1293                 }
1294
1295                 /* construct the new attribute, using either a supplied
1296                         constructor or a simple copy */
1297                 constructed_attributes = true;
1298                 if (list_replace[a].constructor != NULL) {
1299                         if (list_replace[a].constructor(module, msg, scope, parent) != LDB_SUCCESS) {
1300                                 goto failed;
1301                         }
1302                 } else if (ldb_msg_copy_attr(msg,
1303                                              list_replace[a].replace,
1304                                              list_replace[a].attr) != LDB_SUCCESS) {
1305                         goto failed;
1306                 }
1307         }
1308
1309         /* Deletion of the search helper attributes are needed if:
1310          * - we generated constructed attributes and
1311          * - we aren't requesting all attributes
1312          */
1313         if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
1314                 for (i=0; i < list_replace_size; i++) {
1315                         /* remove the added search helper attributes, unless
1316                          * they were asked for by the user */
1317                         if (list_replace[i].replace != NULL &&
1318                             !ldb_attr_in_list(attrs_from_user, list_replace[i].replace)) {
1319                                 ldb_msg_remove_attr(msg, list_replace[i].replace);
1320                         }
1321                         if (list_replace[i].extra_attrs != NULL) {
1322                                 unsigned int j;
1323                                 for (j=0; list_replace[i].extra_attrs[j]; j++) {
1324                                         if (!ldb_attr_in_list(attrs_from_user, list_replace[i].extra_attrs[j])) {
1325                                                 ldb_msg_remove_attr(msg, list_replace[i].extra_attrs[j]);
1326                                         }
1327                                 }
1328                         }
1329                 }
1330         }
1331
1332         return 0;
1333
1334 failed:
1335         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
1336                       "operational_search_post_process failed for attribute '%s' - %s",
1337                       list_replace[a].attr, ldb_errstring(ldb));
1338         return -1;
1339 }
1340
1341 /*
1342   hook search operations
1343 */
1344
1345 struct operational_context {
1346         struct ldb_module *module;
1347         struct ldb_request *req;
1348         enum ldb_scope scope;
1349         const char * const *attrs;
1350         struct op_controls_flags* controls_flags;
1351         struct op_attributes_operations *list_operations;
1352         unsigned int list_operations_size;
1353         struct op_attributes_replace *attrs_to_replace;
1354         unsigned int attrs_to_replace_size;
1355 };
1356
1357 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
1358 {
1359         struct operational_context *ac;
1360         int ret;
1361
1362         ac = talloc_get_type(req->context, struct operational_context);
1363
1364         if (!ares) {
1365                 return ldb_module_done(ac->req, NULL, NULL,
1366                                         LDB_ERR_OPERATIONS_ERROR);
1367         }
1368         if (ares->error != LDB_SUCCESS) {
1369                 return ldb_module_done(ac->req, ares->controls,
1370                                         ares->response, ares->error);
1371         }
1372
1373         switch (ares->type) {
1374         case LDB_REPLY_ENTRY:
1375                 /* for each record returned post-process to add any derived
1376                    attributes that have been asked for */
1377                 ret = operational_search_post_process(ac->module,
1378                                                       ares->message,
1379                                                       ac->scope,
1380                                                       ac->attrs,
1381                                                       req->op.search.attrs,
1382                                                       ac->controls_flags,
1383                                                       ac->list_operations,
1384                                                       ac->list_operations_size,
1385                                                       ac->attrs_to_replace,
1386                                                       ac->attrs_to_replace_size,
1387                                                       req);
1388                 if (ret != 0) {
1389                         return ldb_module_done(ac->req, NULL, NULL,
1390                                                 LDB_ERR_OPERATIONS_ERROR);
1391                 }
1392                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
1393
1394         case LDB_REPLY_REFERRAL:
1395                 return ldb_module_send_referral(ac->req, ares->referral);
1396
1397         case LDB_REPLY_DONE:
1398
1399                 return ldb_module_done(ac->req, ares->controls,
1400                                         ares->response, LDB_SUCCESS);
1401         }
1402
1403         talloc_free(ares);
1404         return LDB_SUCCESS;
1405 }
1406
1407 static struct op_attributes_operations* operation_get_op_list(TALLOC_CTX *ctx,
1408                                                               const char* const* attrs,
1409                                                               const char* const* searched_attrs,
1410                                                               struct op_controls_flags* controls_flags)
1411 {
1412         int idx = 0;
1413         int i;
1414         struct op_attributes_operations *list = talloc_zero_array(ctx,
1415                                                                   struct op_attributes_operations,
1416                                                                   ARRAY_SIZE(operational_remove) + 1);
1417
1418         if (list == NULL) {
1419                 return NULL;
1420         }
1421
1422         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
1423                 switch (operational_remove[i].op) {
1424                 case OPERATIONAL_REMOVE_UNASKED:
1425                         if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
1426                                 continue;
1427                         }
1428                         if (ldb_attr_in_list(searched_attrs, operational_remove[i].attr)) {
1429                                 continue;
1430                         }
1431                         list[idx].attr = operational_remove[i].attr;
1432                         list[idx].op = OPERATIONAL_REMOVE_UNASKED;
1433                         idx++;
1434                         break;
1435
1436                 case OPERATIONAL_REMOVE_ALWAYS:
1437                         list[idx].attr = operational_remove[i].attr;
1438                         list[idx].op = OPERATIONAL_REMOVE_ALWAYS;
1439                         idx++;
1440                         break;
1441
1442                 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
1443                         if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
1444                                 list[idx].attr = operational_remove[i].attr;
1445                                 list[idx].op = OPERATIONAL_REMOVE_UNLESS_CONTROL;
1446                                 idx++;
1447                         }
1448                         break;
1449
1450                 case OPERATIONAL_SD_FLAGS:
1451                         if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
1452                                 continue;
1453                         }
1454                         if (controls_flags->sd) {
1455                                 if (attrs == NULL) {
1456                                         continue;
1457                                 }
1458                                 if (attrs[0] == NULL) {
1459                                         continue;
1460                                 }
1461                                 if (ldb_attr_in_list(attrs, "*")) {
1462                                         continue;
1463                                 }
1464                         }
1465                         list[idx].attr = operational_remove[i].attr;
1466                         list[idx].op = OPERATIONAL_SD_FLAGS;
1467                         idx++;
1468                         break;
1469                 }
1470         }
1471
1472         return list;
1473 }
1474
1475 static int operational_search(struct ldb_module *module, struct ldb_request *req)
1476 {
1477         struct ldb_context *ldb;
1478         struct operational_context *ac;
1479         struct ldb_request *down_req;
1480         const char **search_attrs = NULL;
1481         unsigned int i, a;
1482         int ret;
1483
1484         /* There are no operational attributes on special DNs */
1485         if (ldb_dn_is_special(req->op.search.base)) {
1486                 return ldb_next_request(module, req);
1487         }
1488
1489         ldb = ldb_module_get_ctx(module);
1490
1491         ac = talloc(req, struct operational_context);
1492         if (ac == NULL) {
1493                 return ldb_oom(ldb);
1494         }
1495
1496         ac->module = module;
1497         ac->req = req;
1498         ac->scope = req->op.search.scope;
1499         ac->attrs = req->op.search.attrs;
1500
1501         /*  FIXME: We must copy the tree and keep the original
1502          *  unmodified. SSS */
1503         /* replace any attributes in the parse tree that are
1504            searchable, but are stored using a different name in the
1505            backend */
1506         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
1507                 ldb_parse_tree_attr_replace(req->op.search.tree,
1508                                             parse_tree_sub[i].attr,
1509                                             parse_tree_sub[i].replace);
1510         }
1511
1512         ac->controls_flags = talloc(ac, struct op_controls_flags);
1513         /* remember if the SD_FLAGS_OID was set */
1514         ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
1515         /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
1516         ac->controls_flags->bypassoperational =
1517                 (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
1518
1519         ac->attrs_to_replace = NULL;
1520         ac->attrs_to_replace_size = 0;
1521         /* in the list of attributes we are looking for, rename any
1522            attributes to the alias for any hidden attributes that can
1523            be fetched directly using non-hidden names */
1524         for (a=0;ac->attrs && ac->attrs[a];a++) {
1525                 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
1526                         continue;
1527                 }
1528                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
1529
1530                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) != 0 ) {
1531                                 continue;
1532                         }
1533
1534                         ac->attrs_to_replace = talloc_realloc(ac,
1535                                                               ac->attrs_to_replace,
1536                                                               struct op_attributes_replace,
1537                                                               ac->attrs_to_replace_size + 1);
1538
1539                         ac->attrs_to_replace[ac->attrs_to_replace_size] = search_sub[i];
1540                         ac->attrs_to_replace_size++;
1541                         if (!search_sub[i].replace) {
1542                                 continue;
1543                         }
1544
1545                         if (search_sub[i].extra_attrs && search_sub[i].extra_attrs[0]) {
1546                                 unsigned int j;
1547                                 const char **search_attrs2;
1548                                 /* Only adds to the end of the list */
1549                                 for (j = 0; search_sub[i].extra_attrs[j]; j++) {
1550                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
1551                                                                                ? search_attrs
1552                                                                                : ac->attrs, 
1553                                                                                search_sub[i].extra_attrs[j]);
1554                                         if (search_attrs2 == NULL) {
1555                                                 return ldb_operr(ldb);
1556                                         }
1557                                         /* may be NULL, talloc_free() doesn't mind */
1558                                         talloc_free(search_attrs);
1559                                         search_attrs = search_attrs2;
1560                                 }
1561                         }
1562
1563                         if (!search_attrs) {
1564                                 search_attrs = ldb_attr_list_copy(req, ac->attrs);
1565                                 if (search_attrs == NULL) {
1566                                         return ldb_operr(ldb);
1567                                 }
1568                         }
1569                         /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
1570                         search_attrs[a] = search_sub[i].replace;
1571                 }
1572         }
1573         ac->list_operations = operation_get_op_list(ac, ac->attrs,
1574                                                     search_attrs == NULL?req->op.search.attrs:search_attrs,
1575                                                     ac->controls_flags);
1576         ac->list_operations_size = 0;
1577         i = 0;
1578
1579         while (ac->list_operations && ac->list_operations[i].attr != NULL) {
1580                 i++;
1581         }
1582         ac->list_operations_size = i;
1583         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
1584                                         req->op.search.base,
1585                                         req->op.search.scope,
1586                                         req->op.search.tree,
1587                                         /* use new set of attrs if any */
1588                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
1589                                         req->controls,
1590                                         ac, operational_callback,
1591                                         req);
1592         LDB_REQ_SET_LOCATION(down_req);
1593         if (ret != LDB_SUCCESS) {
1594                 return ldb_operr(ldb);
1595         }
1596
1597         /* perform the search */
1598         return ldb_next_request(module, down_req);
1599 }
1600
1601 static int operational_init(struct ldb_module *ctx)
1602 {
1603         struct operational_data *data;
1604         int ret;
1605
1606         ret = ldb_next_init(ctx);
1607
1608         if (ret != LDB_SUCCESS) {
1609                 return ret;
1610         }
1611
1612         data = talloc_zero(ctx, struct operational_data);
1613         if (!data) {
1614                 return ldb_module_oom(ctx);
1615         }
1616
1617         ldb_module_set_private(ctx, data);
1618
1619         return LDB_SUCCESS;
1620 }
1621
1622 static const struct ldb_module_ops ldb_operational_module_ops = {
1623         .name              = "operational",
1624         .search            = operational_search,
1625         .init_context      = operational_init
1626 };
1627
1628 int ldb_operational_module_init(const char *version)
1629 {
1630         LDB_MODULE_CHECK_VERSION(version);
1631         return ldb_register_module(&ldb_operational_module_ops);
1632 }