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 "auth/auth.h"
78 #include "libcli/security/security.h"
81 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
84 struct operational_data {
85 struct ldb_dn *aggregate_dn;
89 construct a canonical name from a message
91 static int construct_canonical_name(struct ldb_module *module,
92 struct ldb_message *msg, enum ldb_scope scope)
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)
108 struct ldb_context *ldb;
109 uint32_t primary_group_token;
111 ldb = ldb_module_get_ctx(module);
112 if (ldb_match_msg_objectclass(msg, "group") == 1) {
114 = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
115 if (primary_group_token == 0) {
119 return samdb_msg_add_int(ldb, msg, msg, "primaryGroupToken",
120 primary_group_token);
127 construct the token groups for SAM objects from a message
129 static int construct_token_groups(struct ldb_module *module,
130 struct ldb_message *msg, enum ldb_scope scope)
132 struct ldb_context *ldb = ldb_module_get_ctx(module);;
133 TALLOC_CTX *tmp_ctx = talloc_new(msg);
140 struct dom_sid *primary_group_sid;
141 const char *primary_group_string;
142 const char *primary_group_dn;
143 DATA_BLOB primary_group_blob;
145 struct dom_sid *account_sid;
146 const char *account_sid_string;
147 const char *account_sid_dn;
148 DATA_BLOB account_sid_blob;
149 struct dom_sid **groupSIDs = NULL;
150 unsigned int num_groupSIDs = 0;
152 struct dom_sid *domain_sid;
154 if (scope != LDB_SCOPE_BASE) {
155 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
156 return LDB_ERR_OPERATIONS_ERROR;
159 /* If it's not a user, it won't have a primaryGroupID */
160 if (ldb_msg_find_element(msg, "primaryGroupID") == NULL) {
161 talloc_free(tmp_ctx);
165 /* Ensure it has an objectSID too */
166 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
167 if (account_sid == NULL) {
168 talloc_free(tmp_ctx);
172 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
173 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
174 talloc_free(tmp_ctx);
175 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
176 } else if (!NT_STATUS_IS_OK(status)) {
177 talloc_free(tmp_ctx);
178 return LDB_ERR_OPERATIONS_ERROR;
181 primary_group_sid = dom_sid_add_rid(tmp_ctx,
183 ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
184 if (!primary_group_sid) {
185 talloc_free(tmp_ctx);
189 /* only return security groups */
190 filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u))",
191 GROUP_TYPE_SECURITY_ENABLED);
193 talloc_free(tmp_ctx);
197 primary_group_string = dom_sid_string(tmp_ctx, primary_group_sid);
198 if (!primary_group_string) {
199 talloc_free(tmp_ctx);
203 primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string);
204 if (!primary_group_dn) {
205 talloc_free(tmp_ctx);
209 primary_group_blob = data_blob_string_const(primary_group_dn);
211 account_sid_string = dom_sid_string(tmp_ctx, account_sid);
212 if (!account_sid_string) {
213 talloc_free(tmp_ctx);
217 account_sid_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", account_sid_string);
218 if (!account_sid_dn) {
219 talloc_free(tmp_ctx);
223 account_sid_blob = data_blob_string_const(account_sid_dn);
225 status = dsdb_expand_nested_groups(ldb, &account_sid_blob,
226 true, /* We don't want to add the object's SID itself,
227 it's not returend in this attribute */
229 tmp_ctx, &groupSIDs, &num_groupSIDs);
231 if (!NT_STATUS_IS_OK(status)) {
232 ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s",
233 account_sid_string, nt_errstr(status));
234 talloc_free(tmp_ctx);
235 return LDB_ERR_OPERATIONS_ERROR;
238 /* Expands the primary group - this function takes in
239 * memberOf-like values, so we fake one up with the
240 * <SID=S-...> format of DN and then let it expand
241 * them, as long as they meet the filter - so only
242 * domain groups, not builtin groups
244 status = dsdb_expand_nested_groups(ldb, &primary_group_blob, false, filter,
245 tmp_ctx, &groupSIDs, &num_groupSIDs);
246 if (!NT_STATUS_IS_OK(status)) {
247 ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s",
248 account_sid_string, nt_errstr(status));
249 talloc_free(tmp_ctx);
250 return LDB_ERR_OPERATIONS_ERROR;
253 for (i=0; i < num_groupSIDs; i++) {
254 ret = samdb_msg_add_dom_sid(ldb, msg, msg, "tokenGroups", groupSIDs[i]);
256 talloc_free(tmp_ctx);
265 construct the parent GUID for an entry from a message
267 static int construct_parent_guid(struct ldb_module *module,
268 struct ldb_message *msg, enum ldb_scope scope)
270 struct ldb_result *res, *parent_res;
271 const struct ldb_val *parent_guid;
272 const char *attrs[] = { "instanceType", NULL };
273 const char *attrs2[] = { "objectGUID", NULL };
274 uint32_t instanceType;
276 struct ldb_dn *parent_dn;
279 /* determine if the object is NC by instance type */
280 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
281 DSDB_FLAG_NEXT_MODULE |
282 DSDB_SEARCH_SHOW_RECYCLED);
284 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
287 if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
288 DEBUG(4,(__location__ ": Object %s is NC\n",
289 ldb_dn_get_linearized(msg->dn)));
292 parent_dn = ldb_dn_get_parent(msg, msg->dn);
294 if (parent_dn == NULL) {
295 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
296 ldb_dn_get_linearized(msg->dn)));
299 ret = dsdb_module_search_dn(module, msg, &parent_res, parent_dn, attrs2,
300 DSDB_FLAG_NEXT_MODULE |
301 DSDB_SEARCH_SHOW_RECYCLED);
302 talloc_free(parent_dn);
304 /* not NC, so the object should have a parent*/
305 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
306 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
307 ldb_dn_get_linearized(msg->dn)));
308 return ldb_operr(ldb_module_get_ctx(module));
309 } else if (ret != LDB_SUCCESS) {
313 parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
315 talloc_free(parent_res);
319 v = data_blob_dup_talloc(parent_res, parent_guid);
321 talloc_free(parent_res);
322 return ldb_oom(ldb_module_get_ctx(module));
324 ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
325 talloc_free(parent_res);
330 construct a subSchemaSubEntry
332 static int construct_subschema_subentry(struct ldb_module *module,
333 struct ldb_message *msg, enum ldb_scope scope)
335 struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
336 char *subSchemaSubEntry;
338 /* We may be being called before the init function has finished */
343 /* Try and set this value up, if possible. Don't worry if it
344 * fails, we may not have the DB set up yet, and it's not
345 * really vital anyway */
346 if (!data->aggregate_dn) {
347 struct ldb_context *ldb = ldb_module_get_ctx(module);
348 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
351 if (data->aggregate_dn) {
352 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
353 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
359 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
360 struct ldb_message *msg,
361 struct ldb_message_element *object_category)
363 struct ldb_context *ldb;
365 const struct ldb_val *val;
367 ldb = ldb_module_get_ctx(module);
369 DEBUG(4, (__location__ ": Failed to get ldb \n"));
370 return ldb_operr(ldb);
373 dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
375 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
376 (const char *)object_category->values[0].data));
377 return ldb_operr(ldb);
380 val = ldb_dn_get_rdn_val(dn);
382 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
383 ldb_dn_get_linearized(dn)));
384 return ldb_operr(ldb);
387 if (strequal((const char *)val->data, "NTDS-DSA")) {
388 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
390 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
395 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
396 struct ldb_message *msg,
399 struct ldb_dn *server_dn;
400 const char *attr_obj_cat[] = { "objectCategory", NULL };
401 struct ldb_result *res;
402 struct ldb_message_element *object_category;
405 server_dn = ldb_dn_copy(msg, dn);
406 if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
407 DEBUG(4, (__location__ ": Failed to add child to %s \n",
408 ldb_dn_get_linearized(server_dn)));
409 return ldb_operr(ldb_module_get_ctx(module));
412 ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
413 DSDB_FLAG_NEXT_MODULE);
414 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
415 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
416 ldb_dn_get_linearized(server_dn)));
418 } else if (ret != LDB_SUCCESS) {
422 object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
423 if (!object_category) {
424 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
425 ldb_dn_get_linearized(res->msgs[0]->dn)));
428 return construct_msds_isrodc_with_dn(module, msg, object_category);
431 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
432 struct ldb_message *msg)
434 struct ldb_context *ldb;
435 const char *attr[] = { "serverReferenceBL", NULL };
436 struct ldb_result *res;
438 struct ldb_dn *server_dn;
440 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attr,
441 DSDB_FLAG_NEXT_MODULE);
442 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
443 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
444 ldb_dn_get_linearized(msg->dn)));
446 } else if (ret != LDB_SUCCESS) {
450 ldb = ldb_module_get_ctx(module);
455 server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
457 DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
458 ldb_dn_get_linearized(res->msgs[0]->dn)));
461 return construct_msds_isrodc_with_server_dn(module, msg, server_dn);
465 construct msDS-isRODC attr
467 static int construct_msds_isrodc(struct ldb_module *module,
468 struct ldb_message *msg, enum ldb_scope scope)
470 struct ldb_message_element * object_class;
471 struct ldb_message_element * object_category;
474 object_class = ldb_msg_find_element(msg, "objectClass");
476 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
477 ldb_dn_get_linearized(msg->dn)));
478 return ldb_operr(ldb_module_get_ctx(module));
481 for (i=0; i<object_class->num_values; i++) {
482 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
483 /* If TO!objectCategory equals the DN of the classSchema object for the nTDSDSA
484 * object class, then TO!msDS-isRODC is false. Otherwise, TO!msDS-isRODC is true.
486 object_category = ldb_msg_find_element(msg, "objectCategory");
487 if (!object_category) {
488 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
489 ldb_dn_get_linearized(msg->dn)));
492 return construct_msds_isrodc_with_dn(module, msg, object_category);
494 if (strequal((const char*)object_class->values[i].data, "server")) {
495 /* Let TN be the nTDSDSA object whose DN is "CN=NTDS Settings," prepended to
496 * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA object" case,
497 * substituting TN for TO.
499 return construct_msds_isrodc_with_server_dn(module, msg, msg->dn);
501 if (strequal((const char*)object_class->values[i].data, "computer")) {
502 /* Let TS be the server object named by TO!serverReferenceBL. Apply the previous
503 * rule for the "TO is a server object" case, substituting TS for TO.
505 return construct_msds_isrodc_with_computer_dn(module, msg);
514 construct msDS-keyVersionNumber attr
516 TODO: Make this based on the 'win2k' DS huristics bit...
519 static int construct_msds_keyversionnumber(struct ldb_module *module,
520 struct ldb_message *msg,
521 enum ldb_scope scope)
524 enum ndr_err_code ndr_err;
525 const struct ldb_val *omd_value;
526 struct replPropertyMetaDataBlob *omd;
529 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
531 /* We can't make up a key version number without meta data */
538 omd = talloc(msg, struct replPropertyMetaDataBlob);
540 ldb_module_oom(module);
544 ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
545 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
546 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
547 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
548 ldb_dn_get_linearized(msg->dn)));
549 return ldb_operr(ldb_module_get_ctx(module));
552 if (omd->version != 1) {
553 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
554 omd->version, ldb_dn_get_linearized(msg->dn)));
558 for (i=0; i<omd->ctr.ctr1.count; i++) {
559 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
560 ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
562 "msDS-KeyVersionNumber",
563 omd->ctr.ctr1.array[i].version);
564 if (ret != LDB_SUCCESS) {
575 struct op_controls_flags {
577 bool bypassoperational;
580 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
581 if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
588 a list of attribute names that should be substituted in the parse
589 tree before the search is done
591 static const struct {
594 } parse_tree_sub[] = {
595 { "createTimestamp", "whenCreated" },
596 { "modifyTimestamp", "whenChanged" }
601 a list of attribute names that are hidden, but can be searched for
602 using another (non-hidden) name to produce the correct result
604 static const struct {
607 const char *extra_attr;
608 int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
610 { "createTimestamp", "whenCreated", NULL , NULL },
611 { "modifyTimestamp", "whenChanged", NULL , NULL },
612 { "structuralObjectClass", NULL, NULL , NULL },
613 { "canonicalName", NULL, NULL , construct_canonical_name },
614 { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
615 { "tokenGroups", "primaryGroupID", "objectSid", construct_token_groups },
616 { "parentGUID", NULL, NULL, construct_parent_guid },
617 { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
618 { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
619 { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
624 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
625 OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
626 OPERATIONAL_SD_FLAGS, /* show if SD_FLAGS_OID set, or asked for */
627 OPERATIONAL_REMOVE_UNLESS_CONTROL /* remove always unless an adhoc control has been specified */
631 a list of attributes that may need to be removed from the
634 Some of these are attributes that were once stored, but are now calculated
636 static const struct {
639 } operational_remove[] = {
640 { "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
641 { "msDS-KeyVersionNumber", OPERATIONAL_REMOVE_UNLESS_CONTROL },
642 { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
643 { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
644 #define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
645 { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
650 post process a search result record. For any search_sub[] attributes that were
651 asked for, we need to call the appropriate copy routine to copy the result
652 into the message, then remove any attributes that we added to the search but
653 were not asked for by the user
655 static int operational_search_post_process(struct ldb_module *module,
656 struct ldb_message *msg,
657 enum ldb_scope scope,
658 const char * const *attrs_from_user,
659 const char * const *attrs_searched_for,
660 struct op_controls_flags* controls_flags)
662 struct ldb_context *ldb;
663 unsigned int i, a = 0;
664 bool constructed_attributes = false;
666 ldb = ldb_module_get_ctx(module);
668 /* removed any attrs that should not be shown to the user */
669 for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
670 switch (operational_remove[i].op) {
671 case OPERATIONAL_REMOVE_UNASKED:
672 if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
675 if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
678 case OPERATIONAL_REMOVE_ALWAYS:
679 ldb_msg_remove_attr(msg, operational_remove[i].attr);
681 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
682 if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
683 ldb_msg_remove_attr(msg, operational_remove[i].attr);
688 case OPERATIONAL_SD_FLAGS:
689 if (controls_flags->sd ||
690 ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
693 ldb_msg_remove_attr(msg, operational_remove[i].attr);
698 for (a=0;attrs_from_user && attrs_from_user[a];a++) {
699 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
702 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
703 if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
707 /* construct the new attribute, using either a supplied
708 constructor or a simple copy */
709 constructed_attributes = true;
710 if (search_sub[i].constructor != NULL) {
711 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
714 } else if (ldb_msg_copy_attr(msg,
716 search_sub[i].replace) != LDB_SUCCESS) {
722 /* Deletion of the search helper attributes are needed if:
723 * - we generated constructed attributes and
724 * - we aren't requesting all attributes
726 if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
727 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
728 /* remove the added search helper attributes, unless
729 * they were asked for by the user */
730 if (search_sub[i].replace != NULL &&
731 !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
732 ldb_msg_remove_attr(msg, search_sub[i].replace);
734 if (search_sub[i].extra_attr != NULL &&
735 !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
736 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
744 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
745 "operational_search_post_process failed for attribute '%s' - %s",
746 attrs_from_user[a], ldb_errstring(ldb));
751 hook search operations
754 struct operational_context {
755 struct ldb_module *module;
756 struct ldb_request *req;
757 enum ldb_scope scope;
758 const char * const *attrs;
759 struct op_controls_flags* controls_flags;
762 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
764 struct operational_context *ac;
767 ac = talloc_get_type(req->context, struct operational_context);
770 return ldb_module_done(ac->req, NULL, NULL,
771 LDB_ERR_OPERATIONS_ERROR);
773 if (ares->error != LDB_SUCCESS) {
774 return ldb_module_done(ac->req, ares->controls,
775 ares->response, ares->error);
778 switch (ares->type) {
779 case LDB_REPLY_ENTRY:
780 /* for each record returned post-process to add any derived
781 attributes that have been asked for */
782 ret = operational_search_post_process(ac->module,
786 req->op.search.attrs,
789 return ldb_module_done(ac->req, NULL, NULL,
790 LDB_ERR_OPERATIONS_ERROR);
792 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
794 case LDB_REPLY_REFERRAL:
795 return ldb_module_send_referral(ac->req, ares->referral);
799 return ldb_module_done(ac->req, ares->controls,
800 ares->response, LDB_SUCCESS);
807 static int operational_search(struct ldb_module *module, struct ldb_request *req)
809 struct ldb_context *ldb;
810 struct operational_context *ac;
811 struct ldb_request *down_req;
812 const char **search_attrs = NULL;
816 /* There are no operational attributes on special DNs */
817 if (ldb_dn_is_special(req->op.search.base)) {
818 return ldb_next_request(module, req);
821 ldb = ldb_module_get_ctx(module);
823 ac = talloc(req, struct operational_context);
830 ac->scope = req->op.search.scope;
831 ac->attrs = req->op.search.attrs;
833 /* FIXME: We must copy the tree and keep the original
835 /* replace any attributes in the parse tree that are
836 searchable, but are stored using a different name in the
838 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
839 ldb_parse_tree_attr_replace(req->op.search.tree,
840 parse_tree_sub[i].attr,
841 parse_tree_sub[i].replace);
844 ac->controls_flags = talloc(ac, struct op_controls_flags);
845 /* remember if the SD_FLAGS_OID was set */
846 ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
847 /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
848 ac->controls_flags->bypassoperational =
849 (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
851 /* in the list of attributes we are looking for, rename any
852 attributes to the alias for any hidden attributes that can
853 be fetched directly using non-hidden names */
854 for (a=0;ac->attrs && ac->attrs[a];a++) {
855 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
858 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
859 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
860 search_sub[i].replace) {
862 if (search_sub[i].extra_attr) {
863 const char **search_attrs2;
864 /* Only adds to the end of the list */
865 search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
868 search_sub[i].extra_attr);
869 if (search_attrs2 == NULL) {
870 return ldb_operr(ldb);
872 /* may be NULL, talloc_free() doesn't mind */
873 talloc_free(search_attrs);
874 search_attrs = search_attrs2;
878 search_attrs = ldb_attr_list_copy(req, ac->attrs);
879 if (search_attrs == NULL) {
880 return ldb_operr(ldb);
883 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
884 search_attrs[a] = search_sub[i].replace;
889 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
891 req->op.search.scope,
893 /* use new set of attrs if any */
894 search_attrs == NULL?req->op.search.attrs:search_attrs,
896 ac, operational_callback,
898 LDB_REQ_SET_LOCATION(down_req);
899 if (ret != LDB_SUCCESS) {
900 return ldb_operr(ldb);
903 /* perform the search */
904 return ldb_next_request(module, down_req);
907 static int operational_init(struct ldb_module *ctx)
909 struct operational_data *data;
913 ret = ldb_next_init(ctx);
915 if (ret != LDB_SUCCESS) {
919 data = talloc_zero(ctx, struct operational_data);
921 return ldb_module_oom(ctx);
924 ldb_module_set_private(ctx, data);
929 static const struct ldb_module_ops ldb_operational_module_ops = {
930 .name = "operational",
931 .search = operational_search,
932 .init_context = operational_init
935 int ldb_operational_module_init(const char *version)
937 LDB_MODULE_CHECK_VERSION(version);
938 return ldb_register_module(&ldb_operational_module_ops);