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);
107 if (ldb_match_msg_objectclass(msg, "group") == 1) {
109 = samdb_result_rid_from_sid(ldb, msg, "objectSid", 0);
110 if (primary_group_token == 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 };
130 /* TODO: In the future, this needs to honour the partition boundaries */
131 struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
133 if (parent_dn == NULL) {
134 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
135 ldb_dn_get_linearized(msg->dn)));
139 ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
140 talloc_free(parent_dn);
141 /* if there is no parentGUID for this object, then return */
142 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
143 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
144 ldb_dn_get_linearized(msg->dn)));
146 } else if (ret != LDB_SUCCESS) {
150 parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
156 v = data_blob_dup_talloc(res, parent_guid);
159 return LDB_ERR_OPERATIONS_ERROR;
161 ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
167 construct a subSchemaSubEntry
169 static int construct_subschema_subentry(struct ldb_module *module,
170 struct ldb_message *msg)
172 struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
173 char *subSchemaSubEntry;
174 if (data && data->aggregate_dn) {
175 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
176 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
183 a list of attribute names that should be substituted in the parse
184 tree before the search is done
186 static const struct {
189 } parse_tree_sub[] = {
190 { "createTimestamp", "whenCreated" },
191 { "modifyTimestamp", "whenChanged" }
196 a list of attribute names that are hidden, but can be searched for
197 using another (non-hidden) name to produce the correct result
199 static const struct {
202 const char *extra_attr;
203 int (*constructor)(struct ldb_module *, struct ldb_message *);
205 { "createTimestamp", "whenCreated", NULL , NULL },
206 { "modifyTimestamp", "whenChanged", NULL , NULL },
207 { "structuralObjectClass", "objectClass", NULL , NULL },
208 { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
209 { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
210 { "parentGUID", NULL, NULL, construct_parent_guid },
211 { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry }
216 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
217 OPERATIONAL_REMOVE_UNASKED /* remove if not requested */
221 a list of attributes that may need to be removed from the
224 static const struct {
227 } operational_remove[] = {
228 { "nTSecurityDescriptor", OPERATIONAL_REMOVE_UNASKED },
229 { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
230 { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
231 { "unicodePwd", OPERATIONAL_REMOVE_UNASKED },
232 { "dBCSPwd", OPERATIONAL_REMOVE_UNASKED },
233 { "ntPwdHistory", OPERATIONAL_REMOVE_UNASKED },
234 { "lmPwdHistory", OPERATIONAL_REMOVE_UNASKED },
235 { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
240 post process a search result record. For any search_sub[] attributes that were
241 asked for, we need to call the appropriate copy routine to copy the result
242 into the message, then remove any attributes that we added to the search but
243 were not asked for by the user
245 static int operational_search_post_process(struct ldb_module *module,
246 struct ldb_message *msg,
247 const char * const *attrs)
249 struct ldb_context *ldb;
252 ldb = ldb_module_get_ctx(module);
254 /* removed any attrs that should not be shown to the user */
255 for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
256 struct ldb_message_element *el;
258 switch (operational_remove[i].op) {
259 case OPERATIONAL_REMOVE_UNASKED:
260 if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
263 case OPERATIONAL_REMOVE_ALWAYS:
264 el = ldb_msg_find_element(msg, operational_remove[i].attr);
266 ldb_msg_remove_element(msg, el);
272 for (a=0;attrs && attrs[a];a++) {
273 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
274 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
278 /* construct the new attribute, using either a supplied
279 constructor or a simple copy */
280 if (search_sub[i].constructor != NULL) {
281 if (search_sub[i].constructor(module, msg) != LDB_SUCCESS) {
284 } else if (ldb_msg_copy_attr(msg,
285 search_sub[i].replace,
286 search_sub[i].attr) != LDB_SUCCESS) {
290 /* remove the added search attribute, unless it was
291 asked for by the user */
292 if (search_sub[i].replace != NULL &&
293 !ldb_attr_in_list(attrs, search_sub[i].replace) &&
294 !ldb_attr_in_list(attrs, "*")) {
295 ldb_msg_remove_attr(msg, search_sub[i].replace);
297 if (search_sub[i].extra_attr != NULL &&
298 !ldb_attr_in_list(attrs, search_sub[i].extra_attr) &&
299 !ldb_attr_in_list(attrs, "*")) {
300 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
308 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
309 "operational_search_post_process failed for attribute '%s'",
316 hook search operations
319 struct operational_context {
320 struct ldb_module *module;
321 struct ldb_request *req;
323 const char * const *attrs;
326 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
328 struct operational_context *ac;
331 ac = talloc_get_type(req->context, struct operational_context);
334 return ldb_module_done(ac->req, NULL, NULL,
335 LDB_ERR_OPERATIONS_ERROR);
337 if (ares->error != LDB_SUCCESS) {
338 return ldb_module_done(ac->req, ares->controls,
339 ares->response, ares->error);
342 switch (ares->type) {
343 case LDB_REPLY_ENTRY:
344 /* for each record returned post-process to add any derived
345 attributes that have been asked for */
346 ret = operational_search_post_process(ac->module,
350 return ldb_module_done(ac->req, NULL, NULL,
351 LDB_ERR_OPERATIONS_ERROR);
353 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
355 case LDB_REPLY_REFERRAL:
356 /* ignore referrals */
361 return ldb_module_done(ac->req, ares->controls,
362 ares->response, LDB_SUCCESS);
369 static int operational_search(struct ldb_module *module, struct ldb_request *req)
371 struct ldb_context *ldb;
372 struct operational_context *ac;
373 struct ldb_request *down_req;
374 const char **search_attrs = NULL;
378 ldb = ldb_module_get_ctx(module);
380 ac = talloc(req, struct operational_context);
382 return LDB_ERR_OPERATIONS_ERROR;
387 ac->attrs = req->op.search.attrs;
389 /* FIXME: We must copy the tree and keep the original
391 /* replace any attributes in the parse tree that are
392 searchable, but are stored using a different name in the
394 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
395 ldb_parse_tree_attr_replace(req->op.search.tree,
396 parse_tree_sub[i].attr,
397 parse_tree_sub[i].replace);
400 /* in the list of attributes we are looking for, rename any
401 attributes to the alias for any hidden attributes that can
402 be fetched directly using non-hidden names */
403 for (a=0;ac->attrs && ac->attrs[a];a++) {
404 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
405 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
406 search_sub[i].replace) {
408 if (search_sub[i].extra_attr) {
409 const char **search_attrs2;
410 /* Only adds to the end of the list */
411 search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
414 search_sub[i].extra_attr);
415 if (search_attrs2 == NULL) {
416 return LDB_ERR_OPERATIONS_ERROR;
418 /* may be NULL, talloc_free() doesn't mind */
419 talloc_free(search_attrs);
420 search_attrs = search_attrs2;
424 search_attrs = ldb_attr_list_copy(req, ac->attrs);
425 if (search_attrs == NULL) {
426 return LDB_ERR_OPERATIONS_ERROR;
429 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
430 search_attrs[a] = search_sub[i].replace;
435 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
437 req->op.search.scope,
439 /* use new set of attrs if any */
440 search_attrs == NULL?req->op.search.attrs:search_attrs,
442 ac, operational_callback,
444 if (ret != LDB_SUCCESS) {
445 return LDB_ERR_OPERATIONS_ERROR;
448 /* perform the search */
449 return ldb_next_request(module, down_req);
452 static int operational_init(struct ldb_module *ctx)
454 struct operational_data *data;
455 struct ldb_context *ldb = ldb_module_get_ctx(ctx);
456 int ret = ldb_next_init(ctx);
458 if (ret != LDB_SUCCESS) {
462 data = talloc(ctx, struct operational_data);
465 return LDB_ERR_OPERATIONS_ERROR;
468 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
469 if (!data->aggregate_dn) {
470 ldb_set_errstring(ldb, "Could not build aggregate schema DN");
471 return LDB_ERR_OPERATIONS_ERROR;
474 ldb_module_set_private(ctx, data);
479 const struct ldb_module_ops ldb_operational_module_ops = {
480 .name = "operational",
481 .search = operational_search,
482 .init_context = operational_init