4 Copyright (C) Andrew Tridgell 2005
5 Copyright (C) Simo Sorce 2006-2008
6 Copyright (C) Matthias Dieter Wallnöfer 2009
8 ** NOTE! The following LGPL license applies to the ldb
9 ** library. This does NOT imply that all of Samba is released
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Lesser General Public
14 License as published by the Free Software Foundation; either
15 version 3 of the License, or (at your option) any later version.
17 This library is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 Lesser General Public License for more details.
22 You should have received a copy of the GNU Lesser General Public
23 License along with this library; if not, see <http://www.gnu.org/licenses/>.
27 handle operational attributes
31 createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
32 modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
34 for the above two, we do the search as normal, and if
35 createTimestamp or modifyTimestamp is asked for, then do
36 additional searches for whenCreated and whenChanged and fill in
39 we also need to replace these with the whenCreated/whenChanged
40 equivalent in the search expression trees
42 whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
43 whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
45 on init we need to setup attribute handlers for these so
46 comparisons are done correctly. The resolution is 1 second.
48 on add we need to add both the above, for current time
50 on modify we need to change whenChanged
52 structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
54 for this one we do the search as normal, then if requested ask
55 for objectclass, change the attribute name, and add it
57 primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
59 contains the RID of a certain group object
62 attributeTypes: in schema only
63 objectClasses: in schema only
64 matchingRules: in schema only
65 matchingRuleUse: in schema only
66 creatorsName: not supported by w2k3?
67 modifiersName: not supported by w2k3?
70 #include "ldb_includes.h"
71 #include "ldb_module.h"
74 #include "dsdb/samdb/samdb.h"
77 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
81 construct a canonical name from a message
83 static int construct_canonical_name(struct ldb_module *module,
84 struct ldb_message *msg)
87 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
88 if (canonicalName == NULL) {
91 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
95 construct a primary group token for groups from a message
97 static int construct_primary_group_token(struct ldb_module *module,
98 struct ldb_message *msg)
100 struct ldb_context *ldb;
101 uint32_t primary_group_token;
103 ldb = ldb_module_get_ctx(module);
105 if (samdb_search_count(ldb, ldb, msg->dn, "(objectclass=group)") == 1) {
107 = samdb_result_rid_from_sid(ldb, msg, "objectSid", 0);
108 return samdb_msg_add_int(ldb, ldb, msg, "primaryGroupToken",
109 primary_group_token);
117 a list of attribute names that should be substituted in the parse
118 tree before the search is done
120 static const struct {
123 } parse_tree_sub[] = {
124 { "createTimestamp", "whenCreated" },
125 { "modifyTimestamp", "whenChanged" }
130 a list of attribute names that are hidden, but can be searched for
131 using another (non-hidden) name to produce the correct result
133 static const struct {
136 int (*constructor)(struct ldb_module *, struct ldb_message *);
138 { "createTimestamp", "whenCreated", NULL },
139 { "modifyTimestamp", "whenChanged", NULL },
140 { "structuralObjectClass", "objectClass", NULL },
141 { "canonicalName", "distinguishedName", construct_canonical_name },
142 { "primaryGroupToken", "objectSid", construct_primary_group_token }
146 post process a search result record. For any search_sub[] attributes that were
147 asked for, we need to call the appropriate copy routine to copy the result
148 into the message, then remove any attributes that we added to the search but
149 were not asked for by the user
151 static int operational_search_post_process(struct ldb_module *module,
152 struct ldb_message *msg,
153 const char * const *attrs)
155 struct ldb_context *ldb;
158 ldb = ldb_module_get_ctx(module);
160 for (a=0;attrs && attrs[a];a++) {
161 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
162 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
166 /* construct the new attribute, using either a supplied
167 constructor or a simple copy */
168 if (search_sub[i].constructor) {
169 if (search_sub[i].constructor(module, msg) != 0) {
172 } else if (ldb_msg_copy_attr(msg,
173 search_sub[i].replace,
174 search_sub[i].attr) != 0) {
178 /* remove the added search attribute, unless it was
179 asked for by the user */
180 if (search_sub[i].replace == NULL ||
181 ldb_attr_in_list(attrs, search_sub[i].replace) ||
182 ldb_attr_in_list(attrs, "*")) {
186 ldb_msg_remove_attr(msg, search_sub[i].replace);
193 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
194 "operational_search_post_process failed for attribute '%s'",
201 hook search operations
204 struct operational_context {
205 struct ldb_module *module;
206 struct ldb_request *req;
208 const char * const *attrs;
211 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
213 struct operational_context *ac;
216 ac = talloc_get_type(req->context, struct operational_context);
219 return ldb_module_done(ac->req, NULL, NULL,
220 LDB_ERR_OPERATIONS_ERROR);
222 if (ares->error != LDB_SUCCESS) {
223 return ldb_module_done(ac->req, ares->controls,
224 ares->response, ares->error);
227 switch (ares->type) {
228 case LDB_REPLY_ENTRY:
229 /* for each record returned post-process to add any derived
230 attributes that have been asked for */
231 ret = operational_search_post_process(ac->module,
235 return ldb_module_done(ac->req, NULL, NULL,
236 LDB_ERR_OPERATIONS_ERROR);
238 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
240 case LDB_REPLY_REFERRAL:
241 /* ignore referrals */
246 return ldb_module_done(ac->req, ares->controls,
247 ares->response, LDB_SUCCESS);
254 static int operational_search(struct ldb_module *module, struct ldb_request *req)
256 struct ldb_context *ldb;
257 struct operational_context *ac;
258 struct ldb_request *down_req;
259 const char **search_attrs = NULL;
263 ldb = ldb_module_get_ctx(module);
265 ac = talloc(req, struct operational_context);
267 return LDB_ERR_OPERATIONS_ERROR;
272 ac->attrs = req->op.search.attrs;
274 /* FIXME: We must copy the tree and keep the original
276 /* replace any attributes in the parse tree that are
277 searchable, but are stored using a different name in the
279 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
280 ldb_parse_tree_attr_replace(req->op.search.tree,
281 parse_tree_sub[i].attr,
282 parse_tree_sub[i].replace);
285 /* in the list of attributes we are looking for, rename any
286 attributes to the alias for any hidden attributes that can
287 be fetched directly using non-hidden names */
288 for (a=0;ac->attrs && ac->attrs[a];a++) {
289 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
290 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
291 search_sub[i].replace) {
293 search_attrs = ldb_attr_list_copy(req, ac->attrs);
294 if (search_attrs == NULL) {
295 return LDB_ERR_OPERATIONS_ERROR;
298 search_attrs[a] = search_sub[i].replace;
303 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
305 req->op.search.scope,
307 /* use new set of attrs if any */
308 search_attrs == NULL?req->op.search.attrs:search_attrs,
310 ac, operational_callback,
312 if (ret != LDB_SUCCESS) {
313 return LDB_ERR_OPERATIONS_ERROR;
316 /* perform the search */
317 return ldb_next_request(module, down_req);
320 static int operational_init(struct ldb_module *ctx)
328 return ldb_next_init(ctx);
331 const struct ldb_module_ops ldb_operational_module_ops = {
332 .name = "operational",
333 .search = operational_search,
334 .init_context = operational_init