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