r17185: Oh, I wanted to do this for sooo long time.
[jelmer/samba4-debian.git] / source / lib / ldb / modules / operational.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell 2005
5    Copyright (C) Simo Sorce 2006
6
7      ** NOTE! The following LGPL license applies to the ldb
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10    
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 2 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20
21    You should have received a copy of the GNU Lesser General Public
22    License along with this library; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 */
25 /*
26   handle operational attributes
27  */
28
29 /*
30   createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
31   modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
32
33      for the above two, we do the search as normal, and if
34      createTimestamp or modifyTimestamp is asked for, then do
35      additional searches for whenCreated and whenChanged and fill in
36      the resulting values
37
38      we also need to replace these with the whenCreated/whenChanged
39      equivalent in the search expression trees
40
41   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
42   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
43
44      on init we need to setup attribute handlers for these so
45      comparisons are done correctly. The resolution is 1 second.
46
47      on add we need to add both the above, for current time
48
49      on modify we need to change whenChanged
50
51
52   subschemaSubentry: HIDDEN, not-searchable, 
53                      points at DN CN=Aggregate,CN=Schema,CN=Configuration,$BASEDN
54
55      for this one we do the search as normal, then add the static
56      value if requested. How do we work out the $BASEDN from inside a
57      module?
58      
59
60   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
61
62      for this one we do the search as normal, then if requested ask
63      for objectclass, change the attribute name, and add it
64
65   allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable, 
66      list of attributes that can be modified - requires schema lookup
67
68
69   attributeTypes: in schema only
70   objectClasses: in schema only
71   matchingRules: in schema only
72   matchingRuleUse: in schema only
73   creatorsName: not supported by w2k3?
74   modifiersName: not supported by w2k3?
75 */
76
77 #include "includes.h"
78 #include "ldb/include/includes.h"
79
80 /*
81   construct a canonical name from a message
82 */
83 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
84 {
85         char *canonicalName;
86         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
87         if (canonicalName == NULL) {
88                 return -1;
89         }
90         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
91 }
92
93 /*
94   a list of attribute names that should be substituted in the parse
95   tree before the search is done
96 */
97 static const struct {
98         const char *attr;
99         const char *replace;
100 } parse_tree_sub[] = {
101         { "createTimestamp", "whenCreated" },
102         { "modifyTimestamp", "whenChanged" }
103 };
104
105
106 /*
107   a list of attribute names that are hidden, but can be searched for
108   using another (non-hidden) name to produce the correct result
109 */
110 static const struct {
111         const char *attr;
112         const char *replace;
113         int (*constructor)(struct ldb_module *, struct ldb_message *);
114 } search_sub[] = {
115         { "createTimestamp", "whenCreated", NULL },
116         { "modifyTimestamp", "whenChanged", NULL },
117         { "structuralObjectClass", "objectClass", NULL },
118         { "canonicalName", "distinguishedName", construct_canonical_name }
119 };
120
121 /*
122   post process a search result record. For any search_sub[] attributes that were
123   asked for, we need to call the appropriate copy routine to copy the result
124   into the message, then remove any attributes that we added to the search but were
125   not asked for by the user
126 */
127 static int operational_search_post_process(struct ldb_module *module,
128                                            struct ldb_message *msg, 
129                                            const char * const *attrs)
130 {
131         int i, a=0;
132
133         for (a=0;attrs && attrs[a];a++) {
134                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
135                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
136                                 continue;
137                         }
138
139                         /* construct the new attribute, using either a supplied 
140                            constructor or a simple copy */
141                         if (search_sub[i].constructor) {
142                                 if (search_sub[i].constructor(module, msg) != 0) {
143                                         goto failed;
144                                 }
145                         } else if (ldb_msg_copy_attr(msg,
146                                                      search_sub[i].replace,
147                                                      search_sub[i].attr) != 0) {
148                                 goto failed;
149                         }
150
151                         /* remove the added search attribute, unless it was asked for 
152                            by the user */
153                         if (search_sub[i].replace == NULL ||
154                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
155                             ldb_attr_in_list(attrs, "*")) {
156                                 continue;
157                         }
158
159                         ldb_msg_remove_attr(msg, search_sub[i].replace);
160                 }
161         }
162
163         return 0;
164
165 failed:
166         ldb_debug_set(module->ldb, LDB_DEBUG_WARNING, 
167                       "operational_search_post_process failed for attribute '%s'\n", 
168                       attrs[a]);
169         return -1;
170 }
171
172 /*
173   add a time element to a record
174 */
175 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
176 {
177         struct ldb_message_element *el;
178         char *s;
179
180         if (ldb_msg_find_element(msg, attr) != NULL) {
181                 return 0;
182         }
183
184         s = ldb_timestring(msg, t);
185         if (s == NULL) {
186                 return -1;
187         }
188
189         if (ldb_msg_add_string(msg, attr, s) != 0) {
190                 return -1;
191         }
192
193         el = ldb_msg_find_element(msg, attr);
194         /* always set as replace. This works because on add ops, the flag
195            is ignored */
196         el->flags = LDB_FLAG_MOD_REPLACE;
197
198         return 0;
199 }
200
201 /*
202   add a uint64_t element to a record
203 */
204 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
205 {
206         struct ldb_message_element *el;
207
208         if (ldb_msg_find_element(msg, attr) != NULL) {
209                 return 0;
210         }
211
212         if (ldb_msg_add_fmt(msg, attr, "%llu", v) != 0) {
213                 return -1;
214         }
215
216         el = ldb_msg_find_element(msg, attr);
217         /* always set as replace. This works because on add ops, the flag
218            is ignored */
219         el->flags = LDB_FLAG_MOD_REPLACE;
220
221         return 0;
222 }
223
224
225 /*
226   hook search operations
227 */
228
229 struct operational_async_context {
230
231         struct ldb_module *module;
232         void *up_context;
233         int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
234
235         const char * const *attrs;
236 };
237
238 static int operational_async_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
239 {
240         struct operational_async_context *ac;
241
242         if (!context || !ares) {
243                 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
244                 goto error;
245         }
246
247         ac = talloc_get_type(context, struct operational_async_context);
248
249         if (ares->type == LDB_REPLY_ENTRY) {
250                 /* for each record returned post-process to add any derived
251                    attributes that have been asked for */
252                 if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
253                         goto error;
254                 }
255         }
256
257         return ac->up_callback(ldb, ac->up_context, ares);
258
259 error:
260         talloc_free(ares);
261         return LDB_ERR_OPERATIONS_ERROR;
262 }
263
264 static int operational_search(struct ldb_module *module, struct ldb_request *req)
265 {
266         struct operational_async_context *ac;
267         struct ldb_request *down_req;
268         const char **search_attrs = NULL;
269         int i, a, ret;
270
271         req->async.handle = NULL;
272
273         ac = talloc(req, struct operational_async_context);
274         if (ac == NULL) {
275                 return LDB_ERR_OPERATIONS_ERROR;
276         }
277
278         ac->module = module;
279         ac->up_context = req->async.context;
280         ac->up_callback = req->async.callback;
281         ac->attrs = req->op.search.attrs;
282
283         down_req = talloc_zero(req, struct ldb_request);
284         if (down_req == NULL) {
285                 return LDB_ERR_OPERATIONS_ERROR;
286         }
287
288         down_req->operation = req->operation;
289         down_req->op.search.base = req->op.search.base;
290         down_req->op.search.scope = req->op.search.scope;
291         down_req->op.search.tree = req->op.search.tree;
292
293         /*  FIXME: I hink we should copy the tree and keep the original
294          *  unmodified. SSS */
295         /* replace any attributes in the parse tree that are
296            searchable, but are stored using a different name in the
297            backend */
298         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
299                 ldb_parse_tree_attr_replace(req->op.search.tree, 
300                                             parse_tree_sub[i].attr, 
301                                             parse_tree_sub[i].replace);
302         }
303
304         /* in the list of attributes we are looking for, rename any
305            attributes to the alias for any hidden attributes that can
306            be fetched directly using non-hidden names */
307         for (a=0;ac->attrs && ac->attrs[a];a++) {
308                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
309                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
310                             search_sub[i].replace) {
311                                 if (!search_attrs) {
312                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
313                                         if (search_attrs == NULL) {
314                                                 return LDB_ERR_OPERATIONS_ERROR;
315                                         }
316                                 }
317                                 search_attrs[a] = search_sub[i].replace;
318                         }
319                 }
320         }
321         
322         /* use new set of attrs if any */
323         if (search_attrs) down_req->op.search.attrs = search_attrs;
324         else down_req->op.search.attrs = req->op.search.attrs;
325         
326         down_req->controls = req->controls;
327
328         down_req->async.context = ac;
329         down_req->async.callback = operational_async_callback;
330         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
331
332         /* perform the search */
333         ret = ldb_next_request(module, down_req);
334
335         /* do not free down_req as the call results may be linked to it,
336          * it will be freed when the upper level request get freed */
337         if (ret == LDB_SUCCESS) {
338                 req->async.handle = down_req->async.handle;
339         }
340
341         return ret;
342 }
343
344 /*
345   hook add record ops
346 */
347 static int operational_add(struct ldb_module *module, struct ldb_request *req)
348 {
349         struct ldb_request *down_req;
350         struct ldb_message *msg;
351         time_t t = time(NULL);
352         uint64_t seq_num;
353         int ret;
354
355         if (ldb_dn_is_special(req->op.add.message->dn)) {
356                 return ldb_next_request(module, req);
357         }
358
359         down_req = talloc(req, struct ldb_request);
360         if (down_req == NULL) {
361                 return LDB_ERR_OPERATIONS_ERROR;
362         }
363
364         *down_req = *req;
365
366         /* we have to copy the message as the caller might have it as a const */
367         down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
368         if (msg == NULL) {
369                 return LDB_ERR_OPERATIONS_ERROR;
370         }
371         if (add_time_element(msg, "whenCreated", t) != 0 ||
372             add_time_element(msg, "whenChanged", t) != 0) {
373                 talloc_free(down_req);
374                 return LDB_ERR_OPERATIONS_ERROR;
375         }
376
377         /* Get a sequence number from the backend */
378         ret = ldb_sequence_number(module->ldb, &seq_num);
379         if (ret == LDB_SUCCESS) {
380                 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
381                     add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
382                         talloc_free(down_req);
383                         return LDB_ERR_OPERATIONS_ERROR;
384                 }
385         }
386
387         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
388
389         /* go on with the call chain */
390         ret = ldb_next_request(module, down_req);
391
392         /* do not free down_req as the call results may be linked to it,
393          * it will be freed when the upper level request get freed */
394         if (ret == LDB_SUCCESS) {
395                 req->async.handle = down_req->async.handle;
396         }
397
398         return ret;
399 }
400
401 /*
402   hook modify record ops
403 */
404 static int operational_modify(struct ldb_module *module, struct ldb_request *req)
405 {
406         struct ldb_request *down_req;
407         struct ldb_message *msg;
408         time_t t = time(NULL);
409         uint64_t seq_num;
410         int ret;
411
412         if (ldb_dn_is_special(req->op.mod.message->dn)) {
413                 return ldb_next_request(module, req);
414         }
415
416         down_req = talloc(req, struct ldb_request);
417         if (down_req == NULL) {
418                 return LDB_ERR_OPERATIONS_ERROR;
419         }
420
421         *down_req = *req;
422
423         /* we have to copy the message as the caller might have it as a const */
424         down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
425         if (msg == NULL) {
426                 return LDB_ERR_OPERATIONS_ERROR;
427         }
428         if (add_time_element(msg, "whenChanged", t) != 0) {
429                 talloc_free(down_req);
430                 return LDB_ERR_OPERATIONS_ERROR;
431         }
432
433         /* Get a sequence number from the backend */
434         ret = ldb_sequence_number(module->ldb, &seq_num);
435         if (ret == LDB_SUCCESS) {
436                 /* update the records USN if possible */
437                 if (add_uint64_element(msg, "uSNChanged", 
438                                        seq_num) != 0) {
439                         talloc_free(down_req);
440                         return -1;
441                 }
442         }
443         
444         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
445
446         /* go on with the call chain */
447         ret = ldb_next_request(module, down_req);
448
449         /* do not free down_req as the call results may be linked to it,
450          * it will be freed when the upper level request get freed */
451         if (ret == LDB_SUCCESS) {
452                 req->async.handle = down_req->async.handle;
453         }
454
455         return ret;
456 }
457
458 static int operational_init(struct ldb_module *ctx)
459 {
460         /* setup some standard attribute handlers */
461         ldb_set_attrib_handler_syntax(ctx->ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
462         ldb_set_attrib_handler_syntax(ctx->ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
463         ldb_set_attrib_handler_syntax(ctx->ldb, "subschemaSubentry", LDB_SYNTAX_DN);
464         ldb_set_attrib_handler_syntax(ctx->ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
465
466         return ldb_next_init(ctx);
467 }
468
469 static const struct ldb_module_ops operational_ops = {
470         .name              = "operational",
471         .search            = operational_search,
472         .add               = operational_add,
473         .modify            = operational_modify,
474         .init_context      = operational_init
475 };
476
477 int ldb_operational_init(void)
478 {
479         return ldb_register_module(&operational_ops);
480 }