4 Copyright (C) Andrew Tridgell 2005
5 Copyright (C) Simo Sorce 2006-2008
6 Copyright (C) Matthias Dieter Wallnöfer 2009
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.
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.
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/>.
23 handle operational attributes
27 createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
28 modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
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
35 we also need to replace these with the whenCreated/whenChanged
36 equivalent in the search expression trees
38 whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
39 whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
41 on init we need to setup attribute handlers for these so
42 comparisons are done correctly. The resolution is 1 second.
44 on add we need to add both the above, for current time
46 on modify we need to change whenChanged
48 structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
50 for this one we do the search as normal, then if requested ask
51 for objectclass, change the attribute name, and add it
53 primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
55 contains the RID of a certain group object
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?
67 #include "ldb_includes.h"
68 #include "ldb_module.h"
70 #include "librpc/gen_ndr/ndr_misc.h"
71 #include "param/param.h"
72 #include "dsdb/samdb/samdb.h"
75 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
78 struct operational_data {
79 struct ldb_dn *aggregate_dn;
83 construct a canonical name from a message
85 static int construct_canonical_name(struct ldb_module *module,
86 struct ldb_message *msg)
89 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
90 if (canonicalName == NULL) {
91 return LDB_ERR_OPERATIONS_ERROR;
93 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
97 construct a primary group token for groups from a message
99 static int construct_primary_group_token(struct ldb_module *module,
100 struct ldb_message *msg)
102 struct ldb_context *ldb;
103 uint32_t primary_group_token;
105 ldb = ldb_module_get_ctx(module);
107 if (samdb_search_count(ldb, ldb, msg->dn, "(objectclass=group)") == 1) {
109 = samdb_result_rid_from_sid(ldb, msg, "objectSid", 0);
110 return samdb_msg_add_int(ldb, ldb, msg, "primaryGroupToken",
111 primary_group_token);
117 static int construct_parent_guid(struct ldb_module *module,
118 struct ldb_message *msg)
120 struct ldb_context *ldb;
121 struct GUID parent_guid;
124 ldb = ldb_module_get_ctx(module);
126 ret = dsdb_find_parentguid_by_dn(ldb, msg->dn, &parent_guid);
129 if (ret != LDB_SUCCESS){
131 /* if there is no parentGUID for this object, then return */
132 if (ret == LDB_ERR_NO_SUCH_OBJECT){
140 ret = dsdb_msg_add_guid(msg, &parent_guid, "parentGUID");
147 construct a subSchemaSubEntry
149 static int construct_subschema_subentry(struct ldb_module *module,
150 struct ldb_message *msg)
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);
163 a list of attribute names that should be substituted in the parse
164 tree before the search is done
166 static const struct {
169 } parse_tree_sub[] = {
170 { "createTimestamp", "whenCreated" },
171 { "modifyTimestamp", "whenChanged" }
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
179 static const struct {
182 int (*constructor)(struct ldb_module *, struct ldb_message *);
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 }
195 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
196 OPERATIONAL_REMOVE_UNASKED /* remove if not requested */
200 a list of attributes that may need to be removed from the
203 static const struct {
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 }
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
223 static int operational_search_post_process(struct ldb_module *module,
224 struct ldb_message *msg,
225 const char * const *attrs)
227 struct ldb_context *ldb;
230 ldb = ldb_module_get_ctx(module);
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;
236 switch (operational_remove[i].op) {
237 case OPERATIONAL_REMOVE_UNASKED:
238 if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
241 case OPERATIONAL_REMOVE_ALWAYS:
242 el = ldb_msg_find_element(msg, operational_remove[i].attr);
244 ldb_msg_remove_element(msg, el);
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) {
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) {
262 } else if (ldb_msg_copy_attr(msg,
263 search_sub[i].replace,
264 search_sub[i].attr) != LDB_SUCCESS) {
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, "*")) {
276 ldb_msg_remove_attr(msg, search_sub[i].replace);
283 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
284 "operational_search_post_process failed for attribute '%s'",
291 hook search operations
294 struct operational_context {
295 struct ldb_module *module;
296 struct ldb_request *req;
298 const char * const *attrs;
301 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
303 struct operational_context *ac;
306 ac = talloc_get_type(req->context, struct operational_context);
309 return ldb_module_done(ac->req, NULL, NULL,
310 LDB_ERR_OPERATIONS_ERROR);
312 if (ares->error != LDB_SUCCESS) {
313 return ldb_module_done(ac->req, ares->controls,
314 ares->response, ares->error);
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,
325 return ldb_module_done(ac->req, NULL, NULL,
326 LDB_ERR_OPERATIONS_ERROR);
328 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
330 case LDB_REPLY_REFERRAL:
331 /* ignore referrals */
336 return ldb_module_done(ac->req, ares->controls,
337 ares->response, LDB_SUCCESS);
344 static int operational_search(struct ldb_module *module, struct ldb_request *req)
346 struct ldb_context *ldb;
347 struct operational_context *ac;
348 struct ldb_request *down_req;
349 const char **search_attrs = NULL;
353 ldb = ldb_module_get_ctx(module);
355 ac = talloc(req, struct operational_context);
357 return LDB_ERR_OPERATIONS_ERROR;
362 ac->attrs = req->op.search.attrs;
364 /* FIXME: We must copy the tree and keep the original
366 /* replace any attributes in the parse tree that are
367 searchable, but are stored using a different name in the
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);
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) {
383 search_attrs = ldb_attr_list_copy(req, ac->attrs);
384 if (search_attrs == NULL) {
385 return LDB_ERR_OPERATIONS_ERROR;
388 search_attrs[a] = search_sub[i].replace;
393 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
395 req->op.search.scope,
397 /* use new set of attrs if any */
398 search_attrs == NULL?req->op.search.attrs:search_attrs,
400 ac, operational_callback,
402 if (ret != LDB_SUCCESS) {
403 return LDB_ERR_OPERATIONS_ERROR;
406 /* perform the search */
407 return ldb_next_request(module, down_req);
410 static int operational_init(struct ldb_module *ctx)
412 struct operational_data *data;
413 struct ldb_context *ldb = ldb_module_get_ctx(ctx);
414 int ret = ldb_next_init(ctx);
416 if (ret != LDB_SUCCESS) {
420 data = talloc(ctx, struct operational_data);
423 return LDB_ERR_OPERATIONS_ERROR;
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;
432 ldb_module_set_private(ctx, data);
437 const struct ldb_module_ops ldb_operational_module_ops = {
438 .name = "operational",
439 .search = operational_search,
440 .init_context = operational_init