4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2010
5 Copyright (C) Andrew Tridgell 2005
6 Copyright (C) Simo Sorce 2006-2008
7 Copyright (C) Matthias Dieter Wallnöfer 2009
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 handle operational attributes
28 createTimeStamp: HIDDEN, searchable, ldaptime, alias for whenCreated
29 modifyTimeStamp: HIDDEN, searchable, ldaptime, alias for whenChanged
31 for the above two, we do the search as normal, and if
32 createTimeStamp or modifyTimeStamp is asked for, then do
33 additional searches for whenCreated and whenChanged and fill in
36 we also need to replace these with the whenCreated/whenChanged
37 equivalent in the search expression trees
39 whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
40 whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
42 on init we need to setup attribute handlers for these so
43 comparisons are done correctly. The resolution is 1 second.
45 on add we need to add both the above, for current time
47 on modify we need to change whenChanged
49 structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
51 for this one we do the search as normal, then if requested ask
52 for objectclass, change the attribute name, and add it
54 primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
56 contains the RID of a certain group object
59 attributeTypes: in schema only
60 objectClasses: in schema only
61 matchingRules: in schema only
62 matchingRuleUse: in schema only
63 creatorsName: not supported by w2k3?
64 modifiersName: not supported by w2k3?
69 #include <ldb_module.h>
71 #include "librpc/gen_ndr/ndr_misc.h"
72 #include "librpc/gen_ndr/ndr_drsblobs.h"
73 #include "param/param.h"
74 #include "dsdb/samdb/samdb.h"
75 #include "dsdb/samdb/ldb_modules/util.h"
77 #include "libcli/security/security.h"
80 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
83 struct operational_data {
84 struct ldb_dn *aggregate_dn;
88 construct a canonical name from a message
90 static int construct_canonical_name(struct ldb_module *module,
91 struct ldb_message *msg, enum ldb_scope scope,
92 struct ldb_request *parent)
95 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
96 if (canonicalName == NULL) {
97 return ldb_operr(ldb_module_get_ctx(module));
99 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
103 construct a primary group token for groups from a message
105 static int construct_primary_group_token(struct ldb_module *module,
106 struct ldb_message *msg, enum ldb_scope scope,
107 struct ldb_request *parent)
109 struct ldb_context *ldb;
110 uint32_t primary_group_token;
112 ldb = ldb_module_get_ctx(module);
113 if (ldb_match_msg_objectclass(msg, "group") == 1) {
115 = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
116 if (primary_group_token == 0) {
120 return samdb_msg_add_uint(ldb, msg, msg, "primaryGroupToken",
121 primary_group_token);
128 construct the token groups for SAM objects from a message
130 static int construct_token_groups(struct ldb_module *module,
131 struct ldb_message *msg, enum ldb_scope scope,
132 struct ldb_request *parent)
134 struct ldb_context *ldb = ldb_module_get_ctx(module);;
135 TALLOC_CTX *tmp_ctx = talloc_new(msg);
142 struct dom_sid *primary_group_sid;
143 const char *primary_group_string;
144 const char *primary_group_dn;
145 DATA_BLOB primary_group_blob;
147 struct dom_sid *account_sid;
148 const char *account_sid_string;
149 const char *account_sid_dn;
150 DATA_BLOB account_sid_blob;
151 struct dom_sid *groupSIDs = NULL;
152 unsigned int num_groupSIDs = 0;
154 struct dom_sid *domain_sid;
156 if (scope != LDB_SCOPE_BASE) {
157 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
158 return LDB_ERR_OPERATIONS_ERROR;
161 /* If it's not a user, it won't have a primaryGroupID */
162 if (ldb_msg_find_element(msg, "primaryGroupID") == NULL) {
163 talloc_free(tmp_ctx);
167 /* Ensure it has an objectSID too */
168 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
169 if (account_sid == NULL) {
170 talloc_free(tmp_ctx);
174 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
175 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
176 talloc_free(tmp_ctx);
177 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
178 } else if (!NT_STATUS_IS_OK(status)) {
179 talloc_free(tmp_ctx);
180 return LDB_ERR_OPERATIONS_ERROR;
183 primary_group_sid = dom_sid_add_rid(tmp_ctx,
185 ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
186 if (!primary_group_sid) {
187 talloc_free(tmp_ctx);
191 /* only return security groups */
192 filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u))",
193 GROUP_TYPE_SECURITY_ENABLED);
195 talloc_free(tmp_ctx);
199 primary_group_string = dom_sid_string(tmp_ctx, primary_group_sid);
200 if (!primary_group_string) {
201 talloc_free(tmp_ctx);
205 primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string);
206 if (!primary_group_dn) {
207 talloc_free(tmp_ctx);
211 primary_group_blob = data_blob_string_const(primary_group_dn);
213 account_sid_string = dom_sid_string(tmp_ctx, account_sid);
214 if (!account_sid_string) {
215 talloc_free(tmp_ctx);
219 account_sid_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", account_sid_string);
220 if (!account_sid_dn) {
221 talloc_free(tmp_ctx);
225 account_sid_blob = data_blob_string_const(account_sid_dn);
227 status = dsdb_expand_nested_groups(ldb, &account_sid_blob,
228 true, /* We don't want to add the object's SID itself,
229 it's not returend in this attribute */
231 tmp_ctx, &groupSIDs, &num_groupSIDs);
233 if (!NT_STATUS_IS_OK(status)) {
234 ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s",
235 account_sid_string, nt_errstr(status));
236 talloc_free(tmp_ctx);
237 return LDB_ERR_OPERATIONS_ERROR;
240 /* Expands the primary group - this function takes in
241 * memberOf-like values, so we fake one up with the
242 * <SID=S-...> format of DN and then let it expand
243 * them, as long as they meet the filter - so only
244 * domain groups, not builtin groups
246 status = dsdb_expand_nested_groups(ldb, &primary_group_blob, false, filter,
247 tmp_ctx, &groupSIDs, &num_groupSIDs);
248 if (!NT_STATUS_IS_OK(status)) {
249 ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s",
250 account_sid_string, nt_errstr(status));
251 talloc_free(tmp_ctx);
252 return LDB_ERR_OPERATIONS_ERROR;
255 for (i=0; i < num_groupSIDs; i++) {
256 ret = samdb_msg_add_dom_sid(ldb, msg, msg, "tokenGroups", &groupSIDs[i]);
258 talloc_free(tmp_ctx);
267 construct the parent GUID for an entry from a message
269 static int construct_parent_guid(struct ldb_module *module,
270 struct ldb_message *msg, enum ldb_scope scope,
271 struct ldb_request *parent)
273 struct ldb_result *res, *parent_res;
274 const struct ldb_val *parent_guid;
275 const char *attrs[] = { "instanceType", NULL };
276 const char *attrs2[] = { "objectGUID", NULL };
277 uint32_t instanceType;
279 struct ldb_dn *parent_dn;
282 /* determine if the object is NC by instance type */
283 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
284 DSDB_FLAG_NEXT_MODULE |
285 DSDB_SEARCH_SHOW_RECYCLED, parent);
286 if (ret != LDB_SUCCESS) {
290 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
293 if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
294 DEBUG(4,(__location__ ": Object %s is NC\n",
295 ldb_dn_get_linearized(msg->dn)));
298 parent_dn = ldb_dn_get_parent(msg, msg->dn);
300 if (parent_dn == NULL) {
301 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
302 ldb_dn_get_linearized(msg->dn)));
305 ret = dsdb_module_search_dn(module, msg, &parent_res, parent_dn, attrs2,
306 DSDB_FLAG_NEXT_MODULE |
307 DSDB_SEARCH_SHOW_RECYCLED, parent);
308 talloc_free(parent_dn);
310 /* not NC, so the object should have a parent*/
311 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
312 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
313 ldb_dn_get_linearized(msg->dn)));
314 return ldb_operr(ldb_module_get_ctx(module));
315 } else if (ret != LDB_SUCCESS) {
319 parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
321 talloc_free(parent_res);
325 v = data_blob_dup_talloc(parent_res, *parent_guid);
327 talloc_free(parent_res);
328 return ldb_oom(ldb_module_get_ctx(module));
330 ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
331 talloc_free(parent_res);
336 construct a subSchemaSubEntry
338 static int construct_subschema_subentry(struct ldb_module *module,
339 struct ldb_message *msg, enum ldb_scope scope,
340 struct ldb_request *parent)
342 struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
343 char *subSchemaSubEntry;
345 /* We may be being called before the init function has finished */
350 /* Try and set this value up, if possible. Don't worry if it
351 * fails, we may not have the DB set up yet, and it's not
352 * really vital anyway */
353 if (!data->aggregate_dn) {
354 struct ldb_context *ldb = ldb_module_get_ctx(module);
355 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
358 if (data->aggregate_dn) {
359 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
360 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
366 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
367 struct ldb_message *msg,
368 struct ldb_message_element *object_category)
370 struct ldb_context *ldb;
372 const struct ldb_val *val;
374 ldb = ldb_module_get_ctx(module);
376 DEBUG(4, (__location__ ": Failed to get ldb \n"));
377 return ldb_operr(ldb);
380 dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
382 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
383 (const char *)object_category->values[0].data));
384 return ldb_operr(ldb);
387 val = ldb_dn_get_rdn_val(dn);
389 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
390 ldb_dn_get_linearized(dn)));
391 return ldb_operr(ldb);
394 if (strequal((const char *)val->data, "NTDS-DSA")) {
395 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
397 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
402 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
403 struct ldb_message *msg,
405 struct ldb_request *parent)
407 struct ldb_dn *server_dn;
408 const char *attr_obj_cat[] = { "objectCategory", NULL };
409 struct ldb_result *res;
410 struct ldb_message_element *object_category;
413 server_dn = ldb_dn_copy(msg, dn);
414 if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
415 DEBUG(4, (__location__ ": Failed to add child to %s \n",
416 ldb_dn_get_linearized(server_dn)));
417 return ldb_operr(ldb_module_get_ctx(module));
420 ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
421 DSDB_FLAG_NEXT_MODULE, parent);
422 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
423 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
424 ldb_dn_get_linearized(server_dn)));
426 } else if (ret != LDB_SUCCESS) {
430 object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
431 if (!object_category) {
432 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
433 ldb_dn_get_linearized(res->msgs[0]->dn)));
436 return construct_msds_isrodc_with_dn(module, msg, object_category);
439 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
440 struct ldb_message *msg,
441 struct ldb_request *parent)
444 struct ldb_dn *server_dn;
446 ret = dsdb_module_reference_dn(module, msg, msg->dn, "serverReferenceBL",
448 if (ret == LDB_ERR_NO_SUCH_OBJECT || ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
449 /* it's OK if we can't find serverReferenceBL attribute */
450 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
451 ldb_dn_get_linearized(msg->dn)));
453 } else if (ret != LDB_SUCCESS) {
457 return construct_msds_isrodc_with_server_dn(module, msg, server_dn, parent);
461 construct msDS-isRODC attr
463 static int construct_msds_isrodc(struct ldb_module *module,
464 struct ldb_message *msg, enum ldb_scope scope,
465 struct ldb_request *parent)
467 struct ldb_message_element * object_class;
468 struct ldb_message_element * object_category;
471 object_class = ldb_msg_find_element(msg, "objectClass");
473 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
474 ldb_dn_get_linearized(msg->dn)));
475 return ldb_operr(ldb_module_get_ctx(module));
478 for (i=0; i<object_class->num_values; i++) {
479 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
480 /* If TO!objectCategory equals the DN of the classSchema object for the nTDSDSA
481 * object class, then TO!msDS-isRODC is false. Otherwise, TO!msDS-isRODC is true.
483 object_category = ldb_msg_find_element(msg, "objectCategory");
484 if (!object_category) {
485 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
486 ldb_dn_get_linearized(msg->dn)));
489 return construct_msds_isrodc_with_dn(module, msg, object_category);
491 if (strequal((const char*)object_class->values[i].data, "server")) {
492 /* Let TN be the nTDSDSA object whose DN is "CN=NTDS Settings," prepended to
493 * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA object" case,
494 * substituting TN for TO.
496 return construct_msds_isrodc_with_server_dn(module, msg, msg->dn, parent);
498 if (strequal((const char*)object_class->values[i].data, "computer")) {
499 /* Let TS be the server object named by TO!serverReferenceBL. Apply the previous
500 * rule for the "TO is a server object" case, substituting TS for TO.
502 return construct_msds_isrodc_with_computer_dn(module, msg, parent);
511 construct msDS-keyVersionNumber attr
513 TODO: Make this based on the 'win2k' DS huristics bit...
516 static int construct_msds_keyversionnumber(struct ldb_module *module,
517 struct ldb_message *msg,
518 enum ldb_scope scope,
519 struct ldb_request *parent)
522 enum ndr_err_code ndr_err;
523 const struct ldb_val *omd_value;
524 struct replPropertyMetaDataBlob *omd;
527 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
529 /* We can't make up a key version number without meta data */
536 omd = talloc(msg, struct replPropertyMetaDataBlob);
538 ldb_module_oom(module);
542 ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
543 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
544 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
545 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
546 ldb_dn_get_linearized(msg->dn)));
547 return ldb_operr(ldb_module_get_ctx(module));
550 if (omd->version != 1) {
551 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
552 omd->version, ldb_dn_get_linearized(msg->dn)));
556 for (i=0; i<omd->ctr.ctr1.count; i++) {
557 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
558 ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
560 "msDS-KeyVersionNumber",
561 omd->ctr.ctr1.array[i].version);
562 if (ret != LDB_SUCCESS) {
573 struct op_controls_flags {
575 bool bypassoperational;
578 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
579 if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
586 a list of attribute names that should be substituted in the parse
587 tree before the search is done
589 static const struct {
592 } parse_tree_sub[] = {
593 { "createTimeStamp", "whenCreated" },
594 { "modifyTimeStamp", "whenChanged" }
599 a list of attribute names that are hidden, but can be searched for
600 using another (non-hidden) name to produce the correct result
602 static const struct {
605 const char *extra_attr;
606 int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope, struct ldb_request *);
608 { "createTimeStamp", "whenCreated", NULL , NULL },
609 { "modifyTimeStamp", "whenChanged", NULL , NULL },
610 { "structuralObjectClass", "objectClass", NULL , NULL },
611 { "canonicalName", NULL, NULL , construct_canonical_name },
612 { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
613 { "tokenGroups", "primaryGroupID", "objectSid", construct_token_groups },
614 { "parentGUID", NULL, NULL, construct_parent_guid },
615 { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
616 { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
617 { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
622 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
623 OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
624 OPERATIONAL_SD_FLAGS, /* show if SD_FLAGS_OID set, or asked for */
625 OPERATIONAL_REMOVE_UNLESS_CONTROL /* remove always unless an adhoc control has been specified */
629 a list of attributes that may need to be removed from the
632 Some of these are attributes that were once stored, but are now calculated
634 static const struct {
637 } operational_remove[] = {
638 { "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
639 { "msDS-KeyVersionNumber", OPERATIONAL_REMOVE_UNLESS_CONTROL },
640 { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
641 { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
642 #define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
643 { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
648 post process a search result record. For any search_sub[] attributes that were
649 asked for, we need to call the appropriate copy routine to copy the result
650 into the message, then remove any attributes that we added to the search but
651 were not asked for by the user
653 static int operational_search_post_process(struct ldb_module *module,
654 struct ldb_message *msg,
655 enum ldb_scope scope,
656 const char * const *attrs_from_user,
657 const char * const *attrs_searched_for,
658 struct op_controls_flags* controls_flags,
659 struct ldb_request *parent)
661 struct ldb_context *ldb;
662 unsigned int i, a = 0;
663 bool constructed_attributes = false;
665 ldb = ldb_module_get_ctx(module);
667 /* removed any attrs that should not be shown to the user */
668 for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
669 switch (operational_remove[i].op) {
670 case OPERATIONAL_REMOVE_UNASKED:
671 if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
674 if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
677 case OPERATIONAL_REMOVE_ALWAYS:
678 ldb_msg_remove_attr(msg, operational_remove[i].attr);
680 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
681 if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
682 ldb_msg_remove_attr(msg, operational_remove[i].attr);
687 case OPERATIONAL_SD_FLAGS:
688 if (controls_flags->sd ||
689 ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
692 ldb_msg_remove_attr(msg, operational_remove[i].attr);
697 for (a=0;attrs_from_user && attrs_from_user[a];a++) {
698 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
701 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
702 if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
706 /* construct the new attribute, using either a supplied
707 constructor or a simple copy */
708 constructed_attributes = true;
709 if (search_sub[i].constructor != NULL) {
710 if (search_sub[i].constructor(module, msg, scope, parent) != LDB_SUCCESS) {
713 } else if (ldb_msg_copy_attr(msg,
714 search_sub[i].replace,
715 search_sub[i].attr) != LDB_SUCCESS) {
721 /* Deletion of the search helper attributes are needed if:
722 * - we generated constructed attributes and
723 * - we aren't requesting all attributes
725 if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
726 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
727 /* remove the added search helper attributes, unless
728 * they were asked for by the user */
729 if (search_sub[i].replace != NULL &&
730 !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
731 ldb_msg_remove_attr(msg, search_sub[i].replace);
733 if (search_sub[i].extra_attr != NULL &&
734 !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
735 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
743 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
744 "operational_search_post_process failed for attribute '%s' - %s",
745 attrs_from_user[a], ldb_errstring(ldb));
750 hook search operations
753 struct operational_context {
754 struct ldb_module *module;
755 struct ldb_request *req;
756 enum ldb_scope scope;
757 const char * const *attrs;
758 struct op_controls_flags* controls_flags;
761 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
763 struct operational_context *ac;
766 ac = talloc_get_type(req->context, struct operational_context);
769 return ldb_module_done(ac->req, NULL, NULL,
770 LDB_ERR_OPERATIONS_ERROR);
772 if (ares->error != LDB_SUCCESS) {
773 return ldb_module_done(ac->req, ares->controls,
774 ares->response, ares->error);
777 switch (ares->type) {
778 case LDB_REPLY_ENTRY:
779 /* for each record returned post-process to add any derived
780 attributes that have been asked for */
781 ret = operational_search_post_process(ac->module,
785 req->op.search.attrs,
786 ac->controls_flags, req);
788 return ldb_module_done(ac->req, NULL, NULL,
789 LDB_ERR_OPERATIONS_ERROR);
791 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
793 case LDB_REPLY_REFERRAL:
794 return ldb_module_send_referral(ac->req, ares->referral);
798 return ldb_module_done(ac->req, ares->controls,
799 ares->response, LDB_SUCCESS);
806 static int operational_search(struct ldb_module *module, struct ldb_request *req)
808 struct ldb_context *ldb;
809 struct operational_context *ac;
810 struct ldb_request *down_req;
811 const char **search_attrs = NULL;
815 /* There are no operational attributes on special DNs */
816 if (ldb_dn_is_special(req->op.search.base)) {
817 return ldb_next_request(module, req);
820 ldb = ldb_module_get_ctx(module);
822 ac = talloc(req, struct operational_context);
829 ac->scope = req->op.search.scope;
830 ac->attrs = req->op.search.attrs;
832 /* FIXME: We must copy the tree and keep the original
834 /* replace any attributes in the parse tree that are
835 searchable, but are stored using a different name in the
837 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
838 ldb_parse_tree_attr_replace(req->op.search.tree,
839 parse_tree_sub[i].attr,
840 parse_tree_sub[i].replace);
843 ac->controls_flags = talloc(ac, struct op_controls_flags);
844 /* remember if the SD_FLAGS_OID was set */
845 ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
846 /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
847 ac->controls_flags->bypassoperational =
848 (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
850 /* in the list of attributes we are looking for, rename any
851 attributes to the alias for any hidden attributes that can
852 be fetched directly using non-hidden names */
853 for (a=0;ac->attrs && ac->attrs[a];a++) {
854 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
857 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
858 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
859 search_sub[i].replace) {
861 if (search_sub[i].extra_attr) {
862 const char **search_attrs2;
863 /* Only adds to the end of the list */
864 search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
867 search_sub[i].extra_attr);
868 if (search_attrs2 == NULL) {
869 return ldb_operr(ldb);
871 /* may be NULL, talloc_free() doesn't mind */
872 talloc_free(search_attrs);
873 search_attrs = search_attrs2;
877 search_attrs = ldb_attr_list_copy(req, ac->attrs);
878 if (search_attrs == NULL) {
879 return ldb_operr(ldb);
882 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
883 search_attrs[a] = search_sub[i].replace;
888 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
890 req->op.search.scope,
892 /* use new set of attrs if any */
893 search_attrs == NULL?req->op.search.attrs:search_attrs,
895 ac, operational_callback,
897 LDB_REQ_SET_LOCATION(down_req);
898 if (ret != LDB_SUCCESS) {
899 return ldb_operr(ldb);
902 /* perform the search */
903 return ldb_next_request(module, down_req);
906 static int operational_init(struct ldb_module *ctx)
908 struct operational_data *data;
911 ret = ldb_next_init(ctx);
913 if (ret != LDB_SUCCESS) {
917 data = talloc_zero(ctx, struct operational_data);
919 return ldb_module_oom(ctx);
922 ldb_module_set_private(ctx, data);
927 static const struct ldb_module_ops ldb_operational_module_ops = {
928 .name = "operational",
929 .search = operational_search,
930 .init_context = operational_init
933 int ldb_operational_module_init(const char *version)
935 LDB_MODULE_CHECK_VERSION(version);
936 return ldb_register_module(&ldb_operational_module_ops);