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 add a time element to a record
175 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
177 struct ldb_message_element *el;
180 if (ldb_msg_find_element(msg, attr) != NULL) {
184 s = ldb_timestring(msg, t);
189 if (ldb_msg_add_string(msg, attr, s) != 0) {
193 el = ldb_msg_find_element(msg, attr);
194 /* always set as replace. This works because on add ops, the flag
196 el->flags = LDB_FLAG_MOD_REPLACE;
202 add a uint64_t element to a record
204 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
206 struct ldb_message_element *el;
208 if (ldb_msg_find_element(msg, attr) != NULL) {
212 if (ldb_msg_add_fmt(msg, attr, "%llu", v) != 0) {
216 el = ldb_msg_find_element(msg, attr);
217 /* always set as replace. This works because on add ops, the flag
219 el->flags = LDB_FLAG_MOD_REPLACE;
226 hook search operations
229 struct operational_async_context {
231 struct ldb_module *module;
233 int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
235 const char * const *attrs;
238 static int operational_async_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
240 struct operational_async_context *ac;
242 if (!context || !ares) {
243 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
247 ac = talloc_get_type(context, struct operational_async_context);
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) {
257 return ac->up_callback(ldb, ac->up_context, ares);
261 return LDB_ERR_OPERATIONS_ERROR;
264 static int operational_search(struct ldb_module *module, struct ldb_request *req)
266 struct operational_async_context *ac;
267 struct ldb_request *down_req;
268 const char **search_attrs = NULL;
271 req->async.handle = NULL;
273 ac = talloc(req, struct operational_async_context);
275 return LDB_ERR_OPERATIONS_ERROR;
279 ac->up_context = req->async.context;
280 ac->up_callback = req->async.callback;
281 ac->attrs = req->op.search.attrs;
283 down_req = talloc_zero(req, struct ldb_request);
284 if (down_req == NULL) {
285 return LDB_ERR_OPERATIONS_ERROR;
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;
293 /* FIXME: I hink we should copy the tree and keep the original
295 /* replace any attributes in the parse tree that are
296 searchable, but are stored using a different name in the
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);
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) {
312 search_attrs = ldb_attr_list_copy(req, ac->attrs);
313 if (search_attrs == NULL) {
314 return LDB_ERR_OPERATIONS_ERROR;
317 search_attrs[a] = search_sub[i].replace;
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;
326 down_req->controls = req->controls;
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);
332 /* perform the search */
333 ret = ldb_next_request(module, down_req);
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;
347 static int operational_add(struct ldb_module *module, struct ldb_request *req)
349 struct ldb_request *down_req;
350 struct ldb_message *msg;
351 time_t t = time(NULL);
355 if (ldb_dn_is_special(req->op.add.message->dn)) {
356 return ldb_next_request(module, req);
359 down_req = talloc(req, struct ldb_request);
360 if (down_req == NULL) {
361 return LDB_ERR_OPERATIONS_ERROR;
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);
369 return LDB_ERR_OPERATIONS_ERROR;
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;
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;
387 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
389 /* go on with the call chain */
390 ret = ldb_next_request(module, down_req);
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;
402 hook modify record ops
404 static int operational_modify(struct ldb_module *module, struct ldb_request *req)
406 struct ldb_request *down_req;
407 struct ldb_message *msg;
408 time_t t = time(NULL);
412 if (ldb_dn_is_special(req->op.mod.message->dn)) {
413 return ldb_next_request(module, req);
416 down_req = talloc(req, struct ldb_request);
417 if (down_req == NULL) {
418 return LDB_ERR_OPERATIONS_ERROR;
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);
426 return LDB_ERR_OPERATIONS_ERROR;
428 if (add_time_element(msg, "whenChanged", t) != 0) {
429 talloc_free(down_req);
430 return LDB_ERR_OPERATIONS_ERROR;
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",
439 talloc_free(down_req);
444 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
446 /* go on with the call chain */
447 ret = ldb_next_request(module, down_req);
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;
458 static int operational_init(struct ldb_module *ctx)
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);
466 return ldb_next_init(ctx);
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
477 int ldb_operational_init(void)
479 return ldb_register_module(&operational_ops);