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"
73 #include "dsdb/samdb/ldb_modules/util.h"
76 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
79 struct operational_data {
80 struct ldb_dn *aggregate_dn;
84 construct a canonical name from a message
86 static int construct_canonical_name(struct ldb_module *module,
87 struct ldb_message *msg)
90 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
91 if (canonicalName == NULL) {
92 return LDB_ERR_OPERATIONS_ERROR;
94 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
98 construct a primary group token for groups from a message
100 static int construct_primary_group_token(struct ldb_module *module,
101 struct ldb_message *msg)
103 struct ldb_context *ldb;
104 uint32_t primary_group_token;
106 ldb = ldb_module_get_ctx(module);
108 /* this is horrendously inefficient! we're doing a subtree
109 * search for every DN we return. So that's N^2 in the
110 * total number of objects! */
111 if (samdb_search_count(ldb, msg->dn, "(objectclass=group)") == 1) {
113 = samdb_result_rid_from_sid(ldb, msg, "objectSid", 0);
114 return samdb_msg_add_int(ldb, ldb, msg, "primaryGroupToken",
115 primary_group_token);
121 static int construct_parent_guid(struct ldb_module *module,
122 struct ldb_message *msg)
124 struct ldb_result *res;
125 const struct ldb_val *parent_guid;
126 const char *attrs[] = { "objectGUID", NULL };
129 /* TODO: In the future, this needs to honour the partition boundaries */
130 struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
132 if (parent_dn == NULL){
133 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
134 ldb_dn_get_linearized(msg->dn)));
138 ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
139 talloc_free(parent_dn);
140 /* if there is no parentGUID for this object, then return */
141 if (ret == LDB_ERR_NO_SUCH_OBJECT){
142 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
143 ldb_dn_get_linearized(msg->dn)));
145 } else if (ret != LDB_SUCCESS) {
149 parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
155 talloc_steal(msg->elements, parent_guid->data);
157 return ldb_msg_add_value(msg, "parentGUID", parent_guid, 0);
161 construct a subSchemaSubEntry
163 static int construct_subschema_subentry(struct ldb_module *module,
164 struct ldb_message *msg)
166 struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
167 char *subSchemaSubEntry;
168 if (data && data->aggregate_dn) {
169 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
170 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
177 a list of attribute names that should be substituted in the parse
178 tree before the search is done
180 static const struct {
183 } parse_tree_sub[] = {
184 { "createTimestamp", "whenCreated" },
185 { "modifyTimestamp", "whenChanged" }
190 a list of attribute names that are hidden, but can be searched for
191 using another (non-hidden) name to produce the correct result
193 static const struct {
196 int (*constructor)(struct ldb_module *, struct ldb_message *);
198 { "createTimestamp", "whenCreated", NULL },
199 { "modifyTimestamp", "whenChanged", NULL },
200 { "structuralObjectClass", "objectClass", NULL },
201 { "canonicalName", "distinguishedName", construct_canonical_name },
202 { "primaryGroupToken", "objectSid", construct_primary_group_token },
203 { "parentGUID", NULL, construct_parent_guid },
204 { "subSchemaSubEntry", NULL, construct_subschema_subentry }
209 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
210 OPERATIONAL_REMOVE_UNASKED /* remove if not requested */
214 a list of attributes that may need to be removed from the
217 static const struct {
220 } operational_remove[] = {
221 { "nTSecurityDescriptor", OPERATIONAL_REMOVE_UNASKED },
222 { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
223 { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
224 { "ntPwdHistory", OPERATIONAL_REMOVE_UNASKED },
225 { "lmPwdHistory", OPERATIONAL_REMOVE_UNASKED },
226 { "unicodePwd", OPERATIONAL_REMOVE_UNASKED },
227 { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED },
228 { "dBCSPwd", OPERATIONAL_REMOVE_UNASKED }
233 post process a search result record. For any search_sub[] attributes that were
234 asked for, we need to call the appropriate copy routine to copy the result
235 into the message, then remove any attributes that we added to the search but
236 were not asked for by the user
238 static int operational_search_post_process(struct ldb_module *module,
239 struct ldb_message *msg,
240 const char * const *attrs)
242 struct ldb_context *ldb;
245 ldb = ldb_module_get_ctx(module);
247 /* removed any attrs that should not be shown to the user */
248 for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
249 struct ldb_message_element *el;
251 switch (operational_remove[i].op) {
252 case OPERATIONAL_REMOVE_UNASKED:
253 if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
256 case OPERATIONAL_REMOVE_ALWAYS:
257 el = ldb_msg_find_element(msg, operational_remove[i].attr);
259 ldb_msg_remove_element(msg, el);
265 for (a=0;attrs && attrs[a];a++) {
266 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
267 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
271 /* construct the new attribute, using either a supplied
272 constructor or a simple copy */
273 if (search_sub[i].constructor != NULL) {
274 if (search_sub[i].constructor(module, msg) != LDB_SUCCESS) {
277 } else if (ldb_msg_copy_attr(msg,
278 search_sub[i].replace,
279 search_sub[i].attr) != LDB_SUCCESS) {
283 /* remove the added search attribute, unless it was
284 asked for by the user */
285 if (search_sub[i].replace == NULL ||
286 ldb_attr_in_list(attrs, search_sub[i].replace) ||
287 ldb_attr_in_list(attrs, "*")) {
291 ldb_msg_remove_attr(msg, search_sub[i].replace);
298 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
299 "operational_search_post_process failed for attribute '%s'",
306 hook search operations
309 struct operational_context {
310 struct ldb_module *module;
311 struct ldb_request *req;
313 const char * const *attrs;
316 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
318 struct operational_context *ac;
321 ac = talloc_get_type(req->context, struct operational_context);
324 return ldb_module_done(ac->req, NULL, NULL,
325 LDB_ERR_OPERATIONS_ERROR);
327 if (ares->error != LDB_SUCCESS) {
328 return ldb_module_done(ac->req, ares->controls,
329 ares->response, ares->error);
332 switch (ares->type) {
333 case LDB_REPLY_ENTRY:
334 /* for each record returned post-process to add any derived
335 attributes that have been asked for */
336 ret = operational_search_post_process(ac->module,
340 return ldb_module_done(ac->req, NULL, NULL,
341 LDB_ERR_OPERATIONS_ERROR);
343 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
345 case LDB_REPLY_REFERRAL:
346 /* ignore referrals */
351 return ldb_module_done(ac->req, ares->controls,
352 ares->response, LDB_SUCCESS);
359 static int operational_search(struct ldb_module *module, struct ldb_request *req)
361 struct ldb_context *ldb;
362 struct operational_context *ac;
363 struct ldb_request *down_req;
364 const char **search_attrs = NULL;
368 ldb = ldb_module_get_ctx(module);
370 ac = talloc(req, struct operational_context);
372 return LDB_ERR_OPERATIONS_ERROR;
377 ac->attrs = req->op.search.attrs;
379 /* FIXME: We must copy the tree and keep the original
381 /* replace any attributes in the parse tree that are
382 searchable, but are stored using a different name in the
384 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
385 ldb_parse_tree_attr_replace(req->op.search.tree,
386 parse_tree_sub[i].attr,
387 parse_tree_sub[i].replace);
390 /* in the list of attributes we are looking for, rename any
391 attributes to the alias for any hidden attributes that can
392 be fetched directly using non-hidden names */
393 for (a=0;ac->attrs && ac->attrs[a];a++) {
394 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
395 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
396 search_sub[i].replace) {
398 search_attrs = ldb_attr_list_copy(req, ac->attrs);
399 if (search_attrs == NULL) {
400 return LDB_ERR_OPERATIONS_ERROR;
403 search_attrs[a] = search_sub[i].replace;
408 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
410 req->op.search.scope,
412 /* use new set of attrs if any */
413 search_attrs == NULL?req->op.search.attrs:search_attrs,
415 ac, operational_callback,
417 if (ret != LDB_SUCCESS) {
418 return LDB_ERR_OPERATIONS_ERROR;
421 /* perform the search */
422 return ldb_next_request(module, down_req);
425 static int operational_init(struct ldb_module *ctx)
427 struct operational_data *data;
428 struct ldb_context *ldb = ldb_module_get_ctx(ctx);
429 int ret = ldb_next_init(ctx);
431 if (ret != LDB_SUCCESS) {
435 data = talloc(ctx, struct operational_data);
438 return LDB_ERR_OPERATIONS_ERROR;
441 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
442 if (!data->aggregate_dn) {
443 ldb_set_errstring(ldb, "Could not build aggregate schema DN");
444 return LDB_ERR_OPERATIONS_ERROR;
447 ldb_module_set_private(ctx, data);
452 const struct ldb_module_ops ldb_operational_module_ops = {
453 .name = "operational",
454 .search = operational_search,
455 .init_context = operational_init