s4:dsdb Make parentGUID handler use dsdb_module_search_dn()
[abartlet/samba.git/.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 #ifndef ARRAY_SIZE
76 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
77 #endif
78
79 struct operational_data {
80         struct ldb_dn *aggregate_dn;
81 };
82
83 /*
84   construct a canonical name from a message
85 */
86 static int construct_canonical_name(struct ldb_module *module,
87         struct ldb_message *msg)
88 {
89         char *canonicalName;
90         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
91         if (canonicalName == NULL) {
92                 return LDB_ERR_OPERATIONS_ERROR;
93         }
94         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
95 }
96
97 /*
98   construct a primary group token for groups from a message
99 */
100 static int construct_primary_group_token(struct ldb_module *module,
101         struct ldb_message *msg)
102 {
103         struct ldb_context *ldb;
104         uint32_t primary_group_token;
105
106         ldb = ldb_module_get_ctx(module);
107
108         /* this is horrendously inefficient! we're doing a subtree
109          * search for every DN we return. So that's N^2 in the
110          * total number of objects! */
111         if (samdb_search_count(ldb, msg->dn, "(objectclass=group)") == 1) {
112                 primary_group_token
113                         = samdb_result_rid_from_sid(ldb, msg, "objectSid", 0);
114                 return samdb_msg_add_int(ldb, ldb, msg, "primaryGroupToken",
115                         primary_group_token);
116         } else {
117                 return LDB_SUCCESS;
118         }
119 }
120
121 static int construct_parent_guid(struct ldb_module *module,
122                                  struct ldb_message *msg)
123 {
124         struct ldb_result *res;
125         const struct ldb_val *parent_guid;
126         const char *attrs[] = { "objectGUID", NULL };
127         int ret;
128
129         /* TODO:  In the future, this needs to honour the partition boundaries */
130         struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
131
132         if (parent_dn == NULL){
133                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
134                                          ldb_dn_get_linearized(msg->dn)));
135                 return LDB_SUCCESS;
136         }
137
138         ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
139         talloc_free(parent_dn);
140         /* if there is no parentGUID for this object, then return */
141         if (ret == LDB_ERR_NO_SUCH_OBJECT){
142                 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
143                          ldb_dn_get_linearized(msg->dn)));
144                 return LDB_SUCCESS;
145         } else if (ret != LDB_SUCCESS) {
146                 return ret;
147         }
148
149         parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
150         if (!parent_guid) {
151                 talloc_free(res);
152                 return LDB_SUCCESS;
153         }
154
155         talloc_steal(msg->elements, parent_guid->data);
156         talloc_free(res);
157         return ldb_msg_add_value(msg, "parentGUID", parent_guid, 0);
158 }
159
160 /*
161   construct a subSchemaSubEntry
162 */
163 static int construct_subschema_subentry(struct ldb_module *module,
164                                         struct ldb_message *msg)
165 {
166         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
167         char *subSchemaSubEntry;
168         if (data && data->aggregate_dn) {
169                 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
170                 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
171         }
172         return LDB_SUCCESS;
173 }
174
175
176 /*
177   a list of attribute names that should be substituted in the parse
178   tree before the search is done
179 */
180 static const struct {
181         const char *attr;
182         const char *replace;
183 } parse_tree_sub[] = {
184         { "createTimestamp", "whenCreated" },
185         { "modifyTimestamp", "whenChanged" }
186 };
187
188
189 /*
190   a list of attribute names that are hidden, but can be searched for
191   using another (non-hidden) name to produce the correct result
192 */
193 static const struct {
194         const char *attr;
195         const char *replace;
196         int (*constructor)(struct ldb_module *, struct ldb_message *);
197 } search_sub[] = {
198         { "createTimestamp", "whenCreated", NULL },
199         { "modifyTimestamp", "whenChanged", NULL },
200         { "structuralObjectClass", "objectClass", NULL },
201         { "canonicalName", "distinguishedName", construct_canonical_name },
202         { "primaryGroupToken", "objectSid", construct_primary_group_token },
203         { "parentGUID", NULL, construct_parent_guid },
204         { "subSchemaSubEntry", NULL, construct_subschema_subentry }
205 };
206
207
208 enum op_remove {
209         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
210         OPERATIONAL_REMOVE_UNASKED /* remove if not requested */
211 };
212
213 /*
214   a list of attributes that may need to be removed from the
215   underlying db return
216 */
217 static const struct {
218         const char *attr;
219         enum op_remove op;
220 } operational_remove[] = {
221         { "nTSecurityDescriptor", OPERATIONAL_REMOVE_UNASKED },
222         { "parentGUID",           OPERATIONAL_REMOVE_ALWAYS },
223         { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
224         { "ntPwdHistory",         OPERATIONAL_REMOVE_UNASKED },
225         { "lmPwdHistory",         OPERATIONAL_REMOVE_UNASKED },
226         { "unicodePwd",           OPERATIONAL_REMOVE_UNASKED },
227         { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED },
228         { "dBCSPwd",              OPERATIONAL_REMOVE_UNASKED }
229 };
230
231
232 /*
233   post process a search result record. For any search_sub[] attributes that were
234   asked for, we need to call the appropriate copy routine to copy the result
235   into the message, then remove any attributes that we added to the search but
236   were not asked for by the user
237 */
238 static int operational_search_post_process(struct ldb_module *module,
239                                            struct ldb_message *msg,
240                                            const char * const *attrs)
241 {
242         struct ldb_context *ldb;
243         int i, a=0;
244
245         ldb = ldb_module_get_ctx(module);
246
247         /* removed any attrs that should not be shown to the user */
248         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
249                 struct ldb_message_element *el;
250
251                 switch (operational_remove[i].op) {
252                 case OPERATIONAL_REMOVE_UNASKED:
253                         if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
254                                 continue;
255                         }
256                 case OPERATIONAL_REMOVE_ALWAYS:
257                         el = ldb_msg_find_element(msg, operational_remove[i].attr);
258                         if (el) {
259                                 ldb_msg_remove_element(msg, el);
260                         }
261                         break;
262                 }
263         }
264
265         for (a=0;attrs && attrs[a];a++) {
266                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
267                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
268                                 continue;
269                         }
270
271                         /* construct the new attribute, using either a supplied
272                            constructor or a simple copy */
273                         if (search_sub[i].constructor != NULL) {
274                                 if (search_sub[i].constructor(module, msg) != LDB_SUCCESS) {
275                                         goto failed;
276                                 }
277                         } else if (ldb_msg_copy_attr(msg,
278                                                      search_sub[i].replace,
279                                                      search_sub[i].attr) != LDB_SUCCESS) {
280                                 goto failed;
281                         }
282
283                         /* remove the added search attribute, unless it was
284                            asked for by the user */
285                         if (search_sub[i].replace == NULL ||
286                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
287                             ldb_attr_in_list(attrs, "*")) {
288                                 continue;
289                         }
290
291                         ldb_msg_remove_attr(msg, search_sub[i].replace);
292                 }
293         }
294
295         return 0;
296
297 failed:
298         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
299                       "operational_search_post_process failed for attribute '%s'",
300                       attrs[a]);
301         return -1;
302 }
303
304
305 /*
306   hook search operations
307 */
308
309 struct operational_context {
310         struct ldb_module *module;
311         struct ldb_request *req;
312
313         const char * const *attrs;
314 };
315
316 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
317 {
318         struct operational_context *ac;
319         int ret;
320
321         ac = talloc_get_type(req->context, struct operational_context);
322
323         if (!ares) {
324                 return ldb_module_done(ac->req, NULL, NULL,
325                                         LDB_ERR_OPERATIONS_ERROR);
326         }
327         if (ares->error != LDB_SUCCESS) {
328                 return ldb_module_done(ac->req, ares->controls,
329                                         ares->response, ares->error);
330         }
331
332         switch (ares->type) {
333         case LDB_REPLY_ENTRY:
334                 /* for each record returned post-process to add any derived
335                    attributes that have been asked for */
336                 ret = operational_search_post_process(ac->module,
337                                                         ares->message,
338                                                         ac->attrs);
339                 if (ret != 0) {
340                         return ldb_module_done(ac->req, NULL, NULL,
341                                                 LDB_ERR_OPERATIONS_ERROR);
342                 }
343                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
344
345         case LDB_REPLY_REFERRAL:
346                 /* ignore referrals */
347                 break;
348
349         case LDB_REPLY_DONE:
350
351                 return ldb_module_done(ac->req, ares->controls,
352                                         ares->response, LDB_SUCCESS);
353         }
354
355         talloc_free(ares);
356         return LDB_SUCCESS;
357 }
358
359 static int operational_search(struct ldb_module *module, struct ldb_request *req)
360 {
361         struct ldb_context *ldb;
362         struct operational_context *ac;
363         struct ldb_request *down_req;
364         const char **search_attrs = NULL;
365         int i, a;
366         int ret;
367
368         ldb = ldb_module_get_ctx(module);
369
370         ac = talloc(req, struct operational_context);
371         if (ac == NULL) {
372                 return LDB_ERR_OPERATIONS_ERROR;
373         }
374
375         ac->module = module;
376         ac->req = req;
377         ac->attrs = req->op.search.attrs;
378
379         /*  FIXME: We must copy the tree and keep the original
380          *  unmodified. SSS */
381         /* replace any attributes in the parse tree that are
382            searchable, but are stored using a different name in the
383            backend */
384         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
385                 ldb_parse_tree_attr_replace(req->op.search.tree,
386                                             parse_tree_sub[i].attr,
387                                             parse_tree_sub[i].replace);
388         }
389
390         /* in the list of attributes we are looking for, rename any
391            attributes to the alias for any hidden attributes that can
392            be fetched directly using non-hidden names */
393         for (a=0;ac->attrs && ac->attrs[a];a++) {
394                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
395                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
396                             search_sub[i].replace) {
397                                 if (!search_attrs) {
398                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
399                                         if (search_attrs == NULL) {
400                                                 return LDB_ERR_OPERATIONS_ERROR;
401                                         }
402                                 }
403                                 search_attrs[a] = search_sub[i].replace;
404                         }
405                 }
406         }
407
408         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
409                                         req->op.search.base,
410                                         req->op.search.scope,
411                                         req->op.search.tree,
412                                         /* use new set of attrs if any */
413                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
414                                         req->controls,
415                                         ac, operational_callback,
416                                         req);
417         if (ret != LDB_SUCCESS) {
418                 return LDB_ERR_OPERATIONS_ERROR;
419         }
420
421         /* perform the search */
422         return ldb_next_request(module, down_req);
423 }
424
425 static int operational_init(struct ldb_module *ctx)
426 {
427         struct operational_data *data;
428         struct ldb_context *ldb = ldb_module_get_ctx(ctx);
429         int ret = ldb_next_init(ctx);
430
431         if (ret != LDB_SUCCESS) {
432                 return ret;
433         }
434
435         data = talloc(ctx, struct operational_data);
436         if (!data) {
437                 ldb_module_oom(ctx);
438                 return LDB_ERR_OPERATIONS_ERROR;
439         }
440
441         data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
442         if (!data->aggregate_dn) {
443                 ldb_set_errstring(ldb, "Could not build aggregate schema DN");
444                 return LDB_ERR_OPERATIONS_ERROR;
445         }
446
447         ldb_module_set_private(ctx, data);
448
449         return LDB_SUCCESS;
450 }
451
452 const struct ldb_module_ops ldb_operational_module_ops = {
453         .name              = "operational",
454         .search            = operational_search,
455         .init_context      = operational_init
456 };