s4:operational LDB - don't accidentally "ate" search helper attributes if we need...
[ira/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_includes.h"
68 #include "ldb_module.h"
69
70 #include "librpc/gen_ndr/ndr_misc.h"
71 #include "param/param.h"
72 #include "dsdb/samdb/samdb.h"
73 #include "dsdb/samdb/ldb_modules/util.h"
74
75 #include "auth/auth.h"
76 #include "libcli/security/dom_sid.h"
77
78 #ifndef ARRAY_SIZE
79 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
80 #endif
81
82 struct operational_data {
83         struct ldb_dn *aggregate_dn;
84 };
85
86 /*
87   construct a canonical name from a message
88 */
89 static int construct_canonical_name(struct ldb_module *module,
90         struct ldb_message *msg)
91 {
92         char *canonicalName;
93         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
94         if (canonicalName == NULL) {
95                 return LDB_ERR_OPERATIONS_ERROR;
96         }
97         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
98 }
99
100 /*
101   construct a primary group token for groups from a message
102 */
103 static int construct_primary_group_token(struct ldb_module *module,
104                                          struct ldb_message *msg)
105 {
106         struct ldb_context *ldb;
107         uint32_t primary_group_token;
108         
109         ldb = ldb_module_get_ctx(module);
110         if (ldb_match_msg_objectclass(msg, "group") == 1) {
111                 primary_group_token
112                         = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
113                 if (primary_group_token == 0) {
114                         return LDB_SUCCESS;
115                 }
116
117                 return samdb_msg_add_int(ldb, msg, msg, "primaryGroupToken",
118                         primary_group_token);
119         } else {
120                 return LDB_SUCCESS;
121         }
122 }
123
124 /*
125   construct the token groups for SAM objects from a message
126 */
127 static int construct_token_groups(struct ldb_module *module,
128                                   struct ldb_message *msg)
129 {
130         struct ldb_context *ldb;
131         const struct dom_sid *sid;
132
133         ldb = ldb_module_get_ctx(module);
134
135         sid = samdb_result_dom_sid(msg, msg, "objectSid");
136         if (sid != NULL) {
137                 NTSTATUS status;
138                 uint32_t prim_group_rid;
139                 struct dom_sid **sids = NULL;
140                 unsigned int i, num_sids = 0;
141                 int ret;
142
143                 prim_group_rid = samdb_result_uint(msg, "primaryGroupID", 0);
144                 if (prim_group_rid != 0) {
145                         struct dom_sid *prim_group_sid;
146
147                         prim_group_sid = dom_sid_add_rid(msg,
148                                                          samdb_domain_sid(ldb),
149                                                          prim_group_rid);
150                         if (prim_group_sid == NULL) {
151                                 ldb_oom(ldb);
152                                 return LDB_ERR_OPERATIONS_ERROR;
153                         }
154
155                         /* onlyChilds = false, we want to consider also the
156                          * "primaryGroupID" for membership */
157                         status = authsam_expand_nested_groups(ldb,
158                                                               prim_group_sid,
159                                                               false, msg,
160                                                               &sids, &num_sids);
161                         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
162                                 ldb_oom(ldb);
163                                 return LDB_ERR_OPERATIONS_ERROR;
164                         }
165                         if (!NT_STATUS_IS_OK(status)) {
166                                 return LDB_ERR_OPERATIONS_ERROR;
167                         }
168
169                         for (i = 0; i < num_sids; i++) {
170                                 ret = samdb_msg_add_dom_sid(ldb, msg, msg,
171                                                             "tokenGroups",
172                                                             sids[i]);
173                                 if (ret != LDB_SUCCESS) {
174                                         talloc_free(sids);
175                                         return ret;
176                                 }
177                         }
178
179                         talloc_free(sids);
180                 }
181
182                 sids = NULL;
183                 num_sids = 0;
184
185                 /* onlyChils = true, we don't want to have the SAM object itself
186                  * in the result */
187                 status = authsam_expand_nested_groups(ldb, sid, true, msg,
188                                                       &sids, &num_sids);
189                 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
190                         ldb_oom(ldb);
191                         return LDB_ERR_OPERATIONS_ERROR;
192                 }
193                 if (!NT_STATUS_IS_OK(status)) {
194                         return LDB_ERR_OPERATIONS_ERROR;
195                 }
196
197                 for (i = 0; i < num_sids; i++) {
198                         ret = samdb_msg_add_dom_sid(ldb, msg, msg,
199                                                     "tokenGroups", sids[i]);
200                         if (ret != LDB_SUCCESS) {
201                                 talloc_free(sids);
202                                 return ret;
203                         }
204                 }
205
206                 talloc_free(sids);
207         }
208
209         return LDB_SUCCESS;
210 }
211
212 /*
213   construct the parent GUID for an entry from a message
214 */
215 static int construct_parent_guid(struct ldb_module *module,
216                                  struct ldb_message *msg)
217 {
218         struct ldb_result *res;
219         const struct ldb_val *parent_guid;
220         const char *attrs[] = { "objectGUID", NULL };
221         int ret;
222         struct ldb_val v;
223
224         /* TODO:  In the future, this needs to honour the partition boundaries */
225         struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
226
227         if (parent_dn == NULL) {
228                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
229                                          ldb_dn_get_linearized(msg->dn)));
230                 return LDB_SUCCESS;
231         }
232
233         ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
234         talloc_free(parent_dn);
235         /* if there is no parentGUID for this object, then return */
236         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
237                 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
238                          ldb_dn_get_linearized(msg->dn)));
239                 return LDB_SUCCESS;
240         } else if (ret != LDB_SUCCESS) {
241                 return ret;
242         }
243
244         parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
245         if (!parent_guid) {
246                 talloc_free(res);
247                 return LDB_SUCCESS;
248         }
249
250         v = data_blob_dup_talloc(res, parent_guid);
251         if (!v.data) {
252                 talloc_free(res);
253                 return LDB_ERR_OPERATIONS_ERROR;
254         }
255         ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
256         talloc_free(res);
257         return ret;
258 }
259
260 /*
261   construct a subSchemaSubEntry
262 */
263 static int construct_subschema_subentry(struct ldb_module *module,
264                                         struct ldb_message *msg)
265 {
266         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
267         char *subSchemaSubEntry;
268         if (data && data->aggregate_dn) {
269                 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
270                 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
271         }
272         return LDB_SUCCESS;
273 }
274
275
276 /*
277   a list of attribute names that should be substituted in the parse
278   tree before the search is done
279 */
280 static const struct {
281         const char *attr;
282         const char *replace;
283 } parse_tree_sub[] = {
284         { "createTimestamp", "whenCreated" },
285         { "modifyTimestamp", "whenChanged" }
286 };
287
288
289 /*
290   a list of attribute names that are hidden, but can be searched for
291   using another (non-hidden) name to produce the correct result
292 */
293 static const struct {
294         const char *attr;
295         const char *replace;
296         const char *extra_attr;
297         int (*constructor)(struct ldb_module *, struct ldb_message *);
298 } search_sub[] = {
299         { "createTimestamp", "whenCreated", NULL , NULL },
300         { "modifyTimestamp", "whenChanged", NULL , NULL },
301         { "structuralObjectClass", "objectClass", NULL , NULL },
302         { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
303         { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
304         { "tokenGroups", "objectSid", "primaryGroupID", construct_token_groups },
305         { "parentGUID", NULL, NULL, construct_parent_guid },
306         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry }
307 };
308
309
310 enum op_remove {
311         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
312         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
313         OPERATIONAL_SD_FLAGS       /* show if SD_FLAGS_OID set, or asked for */
314 };
315
316 /*
317   a list of attributes that may need to be removed from the
318   underlying db return
319 */
320 static const struct {
321         const char *attr;
322         enum op_remove op;
323 } operational_remove[] = {
324         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
325         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
326         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
327         { "unicodePwd",              OPERATIONAL_REMOVE_UNASKED },
328         { "dBCSPwd",                 OPERATIONAL_REMOVE_UNASKED },
329         { "ntPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
330         { "lmPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
331         { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
332 };
333
334
335 /*
336   post process a search result record. For any search_sub[] attributes that were
337   asked for, we need to call the appropriate copy routine to copy the result
338   into the message, then remove any attributes that we added to the search but
339   were not asked for by the user
340 */
341 static int operational_search_post_process(struct ldb_module *module,
342                                            struct ldb_message *msg,
343                                            const char * const *attrs,
344                                            bool sd_flags_set)
345 {
346         struct ldb_context *ldb;
347         unsigned int i, a = 0;
348         bool constructed_attributes = false;
349
350         ldb = ldb_module_get_ctx(module);
351
352         /* removed any attrs that should not be shown to the user */
353         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
354                 switch (operational_remove[i].op) {
355                 case OPERATIONAL_REMOVE_UNASKED:
356                         if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
357                                 continue;
358                         }
359                 case OPERATIONAL_REMOVE_ALWAYS:
360                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
361                         break;
362                 case OPERATIONAL_SD_FLAGS:
363                         if (sd_flags_set ||
364                             ldb_attr_in_list(attrs, operational_remove[i].attr)) {
365                                 continue;
366                         }
367                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
368                         break;
369                 }
370         }
371
372         for (a=0;attrs && attrs[a];a++) {
373                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
374                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
375                                 continue;
376                         }
377
378                         /* construct the new attribute, using either a supplied
379                            constructor or a simple copy */
380                         constructed_attributes = true;
381                         if (search_sub[i].constructor != NULL) {
382                                 if (search_sub[i].constructor(module, msg) != LDB_SUCCESS) {
383                                         goto failed;
384                                 }
385                         } else if (ldb_msg_copy_attr(msg,
386                                                      search_sub[i].replace,
387                                                      search_sub[i].attr) != LDB_SUCCESS) {
388                                 goto failed;
389                         }
390                 }
391         }
392
393         /* Deletion of the search helper attributes are needed if:
394          * - we generated constructed attributes and
395          * - we aren't requesting all attributes
396          */
397         if ((constructed_attributes) && (!ldb_attr_in_list(attrs, "*"))) {
398                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
399                         /* remove the added search helper attributes, unless
400                          * they were asked for by the user */
401                         if (search_sub[i].replace != NULL && 
402                             !ldb_attr_in_list(attrs, search_sub[i].replace)) {
403                                 ldb_msg_remove_attr(msg, search_sub[i].replace);
404                         }
405                         if (search_sub[i].extra_attr != NULL && 
406                             !ldb_attr_in_list(attrs, search_sub[i].extra_attr)) {
407                                 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
408                         }
409                 }
410         }
411
412         return 0;
413
414 failed:
415         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
416                       "operational_search_post_process failed for attribute '%s'",
417                       attrs[a]);
418         return -1;
419 }
420
421
422 /*
423   hook search operations
424 */
425
426 struct operational_context {
427         struct ldb_module *module;
428         struct ldb_request *req;
429
430         const char * const *attrs;
431         bool sd_flags_set;
432 };
433
434 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
435 {
436         struct operational_context *ac;
437         int ret;
438
439         ac = talloc_get_type(req->context, struct operational_context);
440
441         if (!ares) {
442                 return ldb_module_done(ac->req, NULL, NULL,
443                                         LDB_ERR_OPERATIONS_ERROR);
444         }
445         if (ares->error != LDB_SUCCESS) {
446                 return ldb_module_done(ac->req, ares->controls,
447                                         ares->response, ares->error);
448         }
449
450         switch (ares->type) {
451         case LDB_REPLY_ENTRY:
452                 /* for each record returned post-process to add any derived
453                    attributes that have been asked for */
454                 ret = operational_search_post_process(ac->module,
455                                                       ares->message,
456                                                       ac->attrs,
457                                                       ac->sd_flags_set);
458                 if (ret != 0) {
459                         return ldb_module_done(ac->req, NULL, NULL,
460                                                 LDB_ERR_OPERATIONS_ERROR);
461                 }
462                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
463
464         case LDB_REPLY_REFERRAL:
465                 return ldb_module_send_referral(ac->req, ares->referral);
466
467         case LDB_REPLY_DONE:
468
469                 return ldb_module_done(ac->req, ares->controls,
470                                         ares->response, LDB_SUCCESS);
471         }
472
473         talloc_free(ares);
474         return LDB_SUCCESS;
475 }
476
477 static int operational_search(struct ldb_module *module, struct ldb_request *req)
478 {
479         struct ldb_context *ldb;
480         struct operational_context *ac;
481         struct ldb_request *down_req;
482         const char **search_attrs = NULL;
483         unsigned int i, a;
484         int ret;
485
486         ldb = ldb_module_get_ctx(module);
487
488         ac = talloc(req, struct operational_context);
489         if (ac == NULL) {
490                 return LDB_ERR_OPERATIONS_ERROR;
491         }
492
493         ac->module = module;
494         ac->req = req;
495         ac->attrs = req->op.search.attrs;
496
497         /*  FIXME: We must copy the tree and keep the original
498          *  unmodified. SSS */
499         /* replace any attributes in the parse tree that are
500            searchable, but are stored using a different name in the
501            backend */
502         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
503                 ldb_parse_tree_attr_replace(req->op.search.tree,
504                                             parse_tree_sub[i].attr,
505                                             parse_tree_sub[i].replace);
506         }
507
508         /* in the list of attributes we are looking for, rename any
509            attributes to the alias for any hidden attributes that can
510            be fetched directly using non-hidden names */
511         for (a=0;ac->attrs && ac->attrs[a];a++) {
512                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
513                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
514                             search_sub[i].replace) {
515
516                                 if (search_sub[i].extra_attr) {
517                                         const char **search_attrs2;
518                                         /* Only adds to the end of the list */
519                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
520                                                                                ? search_attrs
521                                                                                : ac->attrs, 
522                                                                                search_sub[i].extra_attr);
523                                         if (search_attrs2 == NULL) {
524                                                 return LDB_ERR_OPERATIONS_ERROR;
525                                         }
526                                         /* may be NULL, talloc_free() doesn't mind */
527                                         talloc_free(search_attrs);
528                                         search_attrs = search_attrs2;
529                                 }
530
531                                 if (!search_attrs) {
532                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
533                                         if (search_attrs == NULL) {
534                                                 return LDB_ERR_OPERATIONS_ERROR;
535                                         }
536                                 }
537                                 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
538                                 search_attrs[a] = search_sub[i].replace;
539                         }
540                 }
541         }
542
543         /* remember if the SD_FLAGS_OID was set */
544         ac->sd_flags_set = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
545
546         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
547                                         req->op.search.base,
548                                         req->op.search.scope,
549                                         req->op.search.tree,
550                                         /* use new set of attrs if any */
551                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
552                                         req->controls,
553                                         ac, operational_callback,
554                                         req);
555         if (ret != LDB_SUCCESS) {
556                 return LDB_ERR_OPERATIONS_ERROR;
557         }
558
559         /* perform the search */
560         return ldb_next_request(module, down_req);
561 }
562
563 static int operational_init(struct ldb_module *ctx)
564 {
565         struct operational_data *data;
566         struct ldb_context *ldb = ldb_module_get_ctx(ctx);
567         int ret = ldb_next_init(ctx);
568
569         if (ret != LDB_SUCCESS) {
570                 return ret;
571         }
572
573         data = talloc(ctx, struct operational_data);
574         if (!data) {
575                 ldb_module_oom(ctx);
576                 return LDB_ERR_OPERATIONS_ERROR;
577         }
578
579         data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
580         if (!data->aggregate_dn) {
581                 ldb_set_errstring(ldb, "Could not build aggregate schema DN");
582                 return LDB_ERR_OPERATIONS_ERROR;
583         }
584
585         ldb_module_set_private(ctx, data);
586
587         return LDB_SUCCESS;
588 }
589
590 const struct ldb_module_ops ldb_operational_module_ops = {
591         .name              = "operational",
592         .search            = operational_search,
593         .init_context      = operational_init
594 };