4 Copyright (C) Andrew Tridgell 2005
5 Copyright (C) Simo Sorce 2006
7 ** NOTE! The following LGPL license applies to the ldb
8 ** library. This does NOT imply that all of Samba is released
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.
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.
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
26 handle operational attributes
30 createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
31 modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
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
38 we also need to replace these with the whenCreated/whenChanged
39 equivalent in the search expression trees
41 whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
42 whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
44 on init we need to setup attribute handlers for these so
45 comparisons are done correctly. The resolution is 1 second.
47 on add we need to add both the above, for current time
49 on modify we need to change whenChanged
52 subschemaSubentry: HIDDEN, not-searchable,
53 points at DN CN=Aggregate,CN=Schema,CN=Configuration,$BASEDN
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
60 structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
62 for this one we do the search as normal, then if requested ask
63 for objectclass, change the attribute name, and add it
65 allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable,
66 list of attributes that can be modified - requires schema lookup
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?
78 #include "ldb/include/includes.h"
81 construct a canonical name from a message
83 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
86 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
87 if (canonicalName == NULL) {
90 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
94 a list of attribute names that should be substituted in the parse
95 tree before the search is done
100 } parse_tree_sub[] = {
101 { "createTimestamp", "whenCreated" },
102 { "modifyTimestamp", "whenChanged" }
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
110 static const struct {
113 int (*constructor)(struct ldb_module *, struct ldb_message *);
115 { "createTimestamp", "whenCreated", NULL },
116 { "modifyTimestamp", "whenChanged", NULL },
117 { "structuralObjectClass", "objectClass", NULL },
118 { "canonicalName", "distinguishedName", construct_canonical_name }
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
127 static int operational_search_post_process(struct ldb_module *module,
128 struct ldb_message *msg,
129 const char * const *attrs)
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) {
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) {
145 } else if (ldb_msg_copy_attr(msg,
146 search_sub[i].replace,
147 search_sub[i].attr) != 0) {
151 /* remove the added search attribute, unless it was asked for
153 if (search_sub[i].replace == NULL ||
154 ldb_attr_in_list(attrs, search_sub[i].replace) ||
155 ldb_attr_in_list(attrs, "*")) {
159 ldb_msg_remove_attr(msg, search_sub[i].replace);
166 ldb_debug_set(module->ldb, LDB_DEBUG_WARNING,
167 "operational_search_post_process failed for attribute '%s'\n",
173 hook search operations
175 static int operational_search_bytree(struct ldb_module *module, struct ldb_request *req)
179 const char * const *attrs = req->op.search.attrs;
180 const char **search_attrs = NULL;
182 req->op.search.res = NULL;
184 /* replace any attributes in the parse tree that are
185 searchable, but are stored using a different name in the
187 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
188 ldb_parse_tree_attr_replace(req->op.search.tree,
189 parse_tree_sub[i].attr,
190 parse_tree_sub[i].replace);
193 /* in the list of attributes we are looking for, rename any
194 attributes to the alias for any hidden attributes that can
195 be fetched directly using non-hidden names */
196 for (a=0;attrs && attrs[a];a++) {
197 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
198 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) == 0 &&
199 search_sub[i].replace) {
201 search_attrs = ldb_attr_list_copy(req, attrs);
202 if (search_attrs == NULL) {
206 search_attrs[a] = search_sub[i].replace;
211 /* use new set of attrs if any */
212 if (search_attrs) req->op.search.attrs = search_attrs;
213 /* perform the search */
214 ret = ldb_next_request(module, req);
215 /* set back saved attrs if needed */
216 if (search_attrs) req->op.search.attrs = attrs;
218 /* check operation result */
219 if (ret != LDB_SUCCESS) {
223 /* for each record returned post-process to add any derived
224 attributes that have been asked for */
225 for (r = 0; r < req->op.search.res->count; r++) {
226 if (operational_search_post_process(module, req->op.search.res->msgs[r], attrs) != 0) {
232 talloc_free(search_attrs);
236 talloc_free(search_attrs);
237 talloc_free(req->op.search.res);
238 ldb_oom(module->ldb);
239 return LDB_ERR_OTHER;
243 add a time element to a record
245 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
247 struct ldb_message_element *el;
250 if (ldb_msg_find_element(msg, attr) != NULL) {
254 s = ldb_timestring(msg, t);
259 if (ldb_msg_add_string(msg, attr, s) != 0) {
263 el = ldb_msg_find_element(msg, attr);
264 /* always set as replace. This works because on add ops, the flag
266 el->flags = LDB_FLAG_MOD_REPLACE;
272 add a uint64_t element to a record
274 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
276 struct ldb_message_element *el;
278 if (ldb_msg_find_element(msg, attr) != NULL) {
282 if (ldb_msg_add_fmt(msg, attr, "%llu", v) != 0) {
286 el = ldb_msg_find_element(msg, attr);
287 /* always set as replace. This works because on add ops, the flag
289 el->flags = LDB_FLAG_MOD_REPLACE;
298 static int operational_add(struct ldb_module *module, struct ldb_request *req)
300 const struct ldb_message *msg = req->op.add.message;
301 time_t t = time(NULL);
302 struct ldb_message *msg2;
305 if (ldb_dn_is_special(msg->dn)) {
306 return ldb_next_request(module, req);
309 /* we have to copy the message as the caller might have it as a const */
310 msg2 = ldb_msg_copy_shallow(module, msg);
314 if (add_time_element(msg2, "whenCreated", t) != 0 ||
315 add_time_element(msg2, "whenChanged", t) != 0) {
320 /* see if the backend can give us the USN */
321 if (module->ldb->sequence_number != NULL) {
322 uint64_t seq_num = module->ldb->sequence_number(module->ldb);
323 if (add_uint64_element(msg2, "uSNCreated", seq_num) != 0 ||
324 add_uint64_element(msg2, "uSNChanged", seq_num) != 0) {
330 /* use the new structure for the call chain below this point */
331 req->op.add.message = msg2;
332 /* go on with the call chain */
333 ret = ldb_next_request(module, req);
334 /* put back saved message */
335 req->op.add.message = msg;
336 /* free temproary compy */
342 hook modify record ops
344 static int operational_modify(struct ldb_module *module, struct ldb_request *req)
346 const struct ldb_message *msg = req->op.mod.message;
347 time_t t = time(NULL);
348 struct ldb_message *msg2;
351 if (ldb_dn_is_special(msg->dn)) {
352 return ldb_next_request(module, req);
355 /* we have to copy the message as the caller might have it as a const */
356 msg2 = ldb_msg_copy_shallow(module, msg);
360 if (add_time_element(msg2, "whenChanged", t) != 0) {
365 /* update the records USN if possible */
366 if (module->ldb->sequence_number != NULL &&
367 add_uint64_element(msg2, "uSNChanged",
368 module->ldb->sequence_number(module->ldb)) != 0) {
373 /* use the new structure for the call chain below this point */
374 req->op.mod.message = msg2;
375 /* go on with the call chain */
376 ret = ldb_next_request(module, req);
377 /* put back saved message */
378 req->op.mod.message = msg;
379 /* free temproary compy */
385 hook search operations
388 struct operational_async_context {
390 struct ldb_module *module;
392 int (*up_callback)(struct ldb_context *, void *, struct ldb_async_result *);
395 const char * const *attrs;
398 static int operational_async_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares) {
400 struct operational_async_context *ac;
402 if (!context || !ares) {
403 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
407 ac = talloc_get_type(context, struct operational_async_context);
409 if (ares->type == LDB_REPLY_ENTRY) {
410 /* for each record returned post-process to add any derived
411 attributes that have been asked for */
412 if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
417 return ac->up_callback(ldb, ac->up_context, ares);
421 return LDB_ERR_OPERATIONS_ERROR;
424 static int operational_search_async(struct ldb_module *module, struct ldb_request *req)
426 struct operational_async_context *ac;
427 struct ldb_request *down_req;
428 const char **search_attrs = NULL;
431 req->async.handle = NULL;
433 ac = talloc(req, struct operational_async_context);
435 return LDB_ERR_OPERATIONS_ERROR;
439 ac->up_context = req->async.context;
440 ac->up_callback = req->async.callback;
441 ac->timeout = req->async.timeout;
442 ac->attrs = req->op.search.attrs;
444 down_req = talloc_zero(req, struct ldb_request);
445 if (down_req == NULL) {
446 return LDB_ERR_OPERATIONS_ERROR;
449 down_req->operation = req->operation;
450 down_req->op.search.base = req->op.search.base;
451 down_req->op.search.scope = req->op.search.scope;
452 down_req->op.search.tree = req->op.search.tree;
454 /* FIXME: I hink we should copy the tree and keep the original
456 /* replace any attributes in the parse tree that are
457 searchable, but are stored using a different name in the
459 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
460 ldb_parse_tree_attr_replace(req->op.search.tree,
461 parse_tree_sub[i].attr,
462 parse_tree_sub[i].replace);
465 /* in the list of attributes we are looking for, rename any
466 attributes to the alias for any hidden attributes that can
467 be fetched directly using non-hidden names */
468 for (a=0;ac->attrs && ac->attrs[a];a++) {
469 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
470 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
471 search_sub[i].replace) {
473 search_attrs = ldb_attr_list_copy(req, ac->attrs);
474 if (search_attrs == NULL) {
475 return LDB_ERR_OPERATIONS_ERROR;
478 search_attrs[a] = search_sub[i].replace;
483 /* use new set of attrs if any */
484 if (search_attrs) down_req->op.search.attrs = search_attrs;
485 else down_req->op.search.attrs = req->op.search.attrs;
487 down_req->controls = req->controls;
488 down_req->creds = req->creds;
490 down_req->async.context = ac;
491 down_req->async.callback = operational_async_callback;
492 down_req->async.timeout = req->async.timeout;
494 /* perform the search */
495 ret = ldb_next_request(module, down_req);
497 /* do not free down_req as the call results may be linked to it,
498 * it will be freed when the upper level request get freed */
499 if (ret == LDB_SUCCESS) {
500 req->async.handle = down_req->async.handle;
509 static int operational_add_async(struct ldb_module *module, struct ldb_request *req)
511 struct ldb_request *down_req;
512 struct ldb_message *msg;
513 time_t t = time(NULL);
516 if (ldb_dn_is_special(req->op.add.message->dn)) {
517 return ldb_next_request(module, req);
520 down_req = talloc(req, struct ldb_request);
521 if (down_req == NULL) {
522 return LDB_ERR_OPERATIONS_ERROR;
525 /* we have to copy the message as the caller might have it as a const */
526 msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
528 return LDB_ERR_OPERATIONS_ERROR;
530 if (add_time_element(msg, "whenCreated", t) != 0 ||
531 add_time_element(msg, "whenChanged", t) != 0) {
532 talloc_free(down_req);
533 return LDB_ERR_OPERATIONS_ERROR;
536 /* see if the backend can give us the USN */
537 if (module->ldb->sequence_number != NULL) {
538 uint64_t seq_num = module->ldb->sequence_number(module->ldb);
539 if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
540 add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
541 talloc_free(down_req);
542 return LDB_ERR_OPERATIONS_ERROR;
546 down_req->op.add.message = msg;
548 down_req->controls = req->controls;
549 down_req->creds = req->creds;
551 down_req->async.context = req->async.context;
552 down_req->async.callback = req->async.callback;
553 down_req->async.timeout = req->async.timeout;
555 /* go on with the call chain */
556 ret = ldb_next_request(module, down_req);
558 /* do not free down_req as the call results may be linked to it,
559 * it will be freed when the upper level request get freed */
560 if (ret == LDB_SUCCESS) {
561 req->async.handle = down_req->async.handle;
568 hook modify record ops
570 static int operational_modify_async(struct ldb_module *module, struct ldb_request *req)
572 struct ldb_request *down_req;
573 struct ldb_message *msg;
574 time_t t = time(NULL);
577 if (ldb_dn_is_special(req->op.mod.message->dn)) {
578 return ldb_next_request(module, req);
581 down_req = talloc(req, struct ldb_request);
582 if (down_req == NULL) {
583 return LDB_ERR_OPERATIONS_ERROR;
586 /* we have to copy the message as the caller might have it as a const */
587 msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
589 return LDB_ERR_OPERATIONS_ERROR;
591 if (add_time_element(msg, "whenChanged", t) != 0) {
592 talloc_free(down_req);
593 return LDB_ERR_OPERATIONS_ERROR;
596 /* update the records USN if possible */
597 if (module->ldb->sequence_number != NULL &&
598 add_uint64_element(msg, "uSNChanged",
599 module->ldb->sequence_number(module->ldb)) != 0) {
600 talloc_free(down_req);
604 down_req->op.mod.message = msg;
606 down_req->controls = req->controls;
607 down_req->creds = req->creds;
609 down_req->async.context = req->async.context;
610 down_req->async.callback = req->async.callback;
611 down_req->async.timeout = req->async.timeout;
613 /* go on with the call chain */
614 ret = ldb_next_request(module, down_req);
616 /* do not free down_req as the call results may be linked to it,
617 * it will be freed when the upper level request get freed */
618 if (ret == LDB_SUCCESS) {
619 req->async.handle = down_req->async.handle;
626 static int operational_request(struct ldb_module *module, struct ldb_request *req)
628 switch (req->operation) {
631 return operational_search_bytree(module, req);
634 return operational_add(module, req);
637 return operational_modify(module, req);
639 case LDB_ASYNC_SEARCH:
640 return operational_search_async(module, req);
643 return operational_add_async(module, req);
645 case LDB_ASYNC_MODIFY:
646 return operational_modify_async(module, req);
649 return ldb_next_request(module, req);
654 static int operational_init(struct ldb_module *ctx)
656 /* setup some standard attribute handlers */
657 ldb_set_attrib_handler_syntax(ctx->ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
658 ldb_set_attrib_handler_syntax(ctx->ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
659 ldb_set_attrib_handler_syntax(ctx->ldb, "subschemaSubentry", LDB_SYNTAX_DN);
660 ldb_set_attrib_handler_syntax(ctx->ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
662 return ldb_next_init(ctx);
665 static const struct ldb_module_ops operational_ops = {
666 .name = "operational",
667 .request = operational_request,
668 .init_context = operational_init
671 int ldb_operational_init(void)
673 return ldb_register_module(&operational_ops);