s4: Using control bypassoperational allow the logic of this module to be bypassed...
[Samba.git] / source4 / dsdb / samdb / ldb_modules / operational.c
blobe5aa516fbd29bf0b8eef74ff723a70a250d256da
1 /*
2 ldb database library
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
33 the resulting values
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?
66 #include "includes.h"
67 #include <ldb.h>
68 #include <ldb_module.h>
70 #include "librpc/gen_ndr/ndr_misc.h"
71 #include "librpc/gen_ndr/ndr_drsblobs.h"
72 #include "param/param.h"
73 #include "dsdb/samdb/samdb.h"
74 #include "dsdb/samdb/ldb_modules/util.h"
76 #include "auth/auth.h"
77 #include "libcli/security/dom_sid.h"
79 #ifndef ARRAY_SIZE
80 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
81 #endif
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)
93 char *canonicalName;
94 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
95 if (canonicalName == NULL) {
96 return LDB_ERR_OPERATIONS_ERROR;
98 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
102 construct a primary group token for groups from a message
104 static int construct_primary_group_token(struct ldb_module *module,
105 struct ldb_message *msg, enum ldb_scope scope)
107 struct ldb_context *ldb;
108 uint32_t primary_group_token;
110 ldb = ldb_module_get_ctx(module);
111 if (ldb_match_msg_objectclass(msg, "group") == 1) {
112 primary_group_token
113 = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
114 if (primary_group_token == 0) {
115 return LDB_SUCCESS;
118 return samdb_msg_add_int(ldb, msg, msg, "primaryGroupToken",
119 primary_group_token);
120 } else {
121 return LDB_SUCCESS;
126 construct the token groups for SAM objects from a message
128 static int construct_token_groups(struct ldb_module *module,
129 struct ldb_message *msg, enum ldb_scope scope)
131 struct ldb_context *ldb = ldb_module_get_ctx(module);;
132 struct auth_context *auth_context;
133 struct auth_serversupplied_info *server_info;
134 struct auth_session_info *session_info;
135 TALLOC_CTX *tmp_ctx = talloc_new(msg);
136 uint32_t i;
137 int ret;
139 NTSTATUS status;
141 if (scope != LDB_SCOPE_BASE) {
142 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
143 return LDB_ERR_OPERATIONS_ERROR;
146 status = auth_context_create_from_ldb(tmp_ctx, ldb, &auth_context);
147 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
148 talloc_free(tmp_ctx);
149 ldb_module_oom(module);
150 return LDB_ERR_OPERATIONS_ERROR;
151 } else if (!NT_STATUS_IS_OK(status)) {
152 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, could not create authContext");
153 talloc_free(tmp_ctx);
154 return LDB_ERR_OPERATIONS_ERROR;
157 status = auth_get_server_info_principal(tmp_ctx, auth_context, NULL, msg->dn, &server_info);
158 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
159 talloc_free(tmp_ctx);
160 ldb_module_oom(module);
161 return LDB_ERR_OPERATIONS_ERROR;
162 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
163 /* Not a user, we have no tokenGroups */
164 talloc_free(tmp_ctx);
165 return LDB_SUCCESS;
166 } else if (!NT_STATUS_IS_OK(status)) {
167 talloc_free(tmp_ctx);
168 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_get_server_info_principal failed: %s", nt_errstr(status));
169 return LDB_ERR_OPERATIONS_ERROR;
172 status = auth_generate_session_info(tmp_ctx, auth_context, server_info, 0, &session_info);
173 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
174 talloc_free(tmp_ctx);
175 ldb_module_oom(module);
176 return LDB_ERR_OPERATIONS_ERROR;
177 } else if (!NT_STATUS_IS_OK(status)) {
178 talloc_free(tmp_ctx);
179 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_generate_session_info failed: %s", nt_errstr(status));
180 return LDB_ERR_OPERATIONS_ERROR;
183 /* We start at 1, as the first SID is the user's SID, not included in the tokenGroups */
184 for (i = 1; i < session_info->security_token->num_sids; i++) {
185 ret = samdb_msg_add_dom_sid(ldb, msg, msg,
186 "tokenGroups",
187 session_info->security_token->sids[i]);
188 if (ret != LDB_SUCCESS) {
189 talloc_free(tmp_ctx);
190 return ret;
194 return LDB_SUCCESS;
198 construct the parent GUID for an entry from a message
200 static int construct_parent_guid(struct ldb_module *module,
201 struct ldb_message *msg, enum ldb_scope scope)
203 struct ldb_result *res;
204 const struct ldb_val *parent_guid;
205 const char *attrs[] = { "objectGUID", NULL };
206 int ret;
207 struct ldb_val v;
209 /* TODO: In the future, this needs to honour the partition boundaries */
210 struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
212 if (parent_dn == NULL) {
213 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
214 ldb_dn_get_linearized(msg->dn)));
215 return LDB_SUCCESS;
218 ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
219 talloc_free(parent_dn);
220 /* if there is no parentGUID for this object, then return */
221 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
222 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
223 ldb_dn_get_linearized(msg->dn)));
224 return LDB_SUCCESS;
225 } else if (ret != LDB_SUCCESS) {
226 return ret;
229 parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
230 if (!parent_guid) {
231 talloc_free(res);
232 return LDB_SUCCESS;
235 v = data_blob_dup_talloc(res, parent_guid);
236 if (!v.data) {
237 talloc_free(res);
238 return LDB_ERR_OPERATIONS_ERROR;
240 ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
241 talloc_free(res);
242 return ret;
246 construct a subSchemaSubEntry
248 static int construct_subschema_subentry(struct ldb_module *module,
249 struct ldb_message *msg, enum ldb_scope scope)
251 struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
252 char *subSchemaSubEntry;
254 /* We may be being called before the init function has finished */
255 if (!data) {
256 return LDB_SUCCESS;
259 /* Try and set this value up, if possible. Don't worry if it
260 * fails, we may not have the DB set up yet, and it's not
261 * really vital anyway */
262 if (!data->aggregate_dn) {
263 struct ldb_context *ldb = ldb_module_get_ctx(module);
264 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
267 if (data->aggregate_dn) {
268 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
269 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
271 return LDB_SUCCESS;
275 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
276 struct ldb_message *msg,
277 struct ldb_message_element *object_category)
279 struct ldb_context *ldb;
280 struct ldb_dn *dn;
281 const struct ldb_val *val;
283 ldb = ldb_module_get_ctx(module);
284 if (!ldb) {
285 DEBUG(4, (__location__ ": Failed to get ldb \n"));
286 return LDB_ERR_OPERATIONS_ERROR;
289 dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
290 if (!dn) {
291 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
292 (const char *)object_category->values[0].data));
293 return LDB_ERR_OPERATIONS_ERROR;
296 val = ldb_dn_get_rdn_val(dn);
297 if (!val) {
298 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
299 ldb_dn_get_linearized(dn)));
300 return LDB_ERR_OPERATIONS_ERROR;
303 if (strequal((const char *)val->data, "NTDS-DSA")) {
304 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
305 } else {
306 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
308 return LDB_SUCCESS;
311 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
312 struct ldb_message *msg,
313 struct ldb_dn *dn)
315 struct ldb_dn *server_dn;
316 const char *attr_obj_cat[] = { "objectCategory", NULL };
317 struct ldb_result *res;
318 struct ldb_message_element *object_category;
319 int ret;
321 server_dn = ldb_dn_copy(msg, dn);
322 if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
323 DEBUG(4, (__location__ ": Failed to add child to %s \n",
324 ldb_dn_get_linearized(server_dn)));
325 return LDB_ERR_OPERATIONS_ERROR;
328 ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat, 0);
329 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
330 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
331 ldb_dn_get_linearized(server_dn)));
332 return LDB_SUCCESS;
333 } else if (ret != LDB_SUCCESS) {
334 return ret;
337 object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
338 if (!object_category) {
339 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
340 ldb_dn_get_linearized(res->msgs[0]->dn)));
341 return LDB_SUCCESS;
343 return construct_msds_isrodc_with_dn(module, msg, object_category);
346 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
347 struct ldb_message *msg)
349 struct ldb_context *ldb;
350 const char *attr[] = { "serverReferenceBL", NULL };
351 struct ldb_result *res;
352 int ret;
353 struct ldb_dn *server_dn;
355 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attr, 0);
356 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
357 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
358 ldb_dn_get_linearized(msg->dn)));
359 return LDB_SUCCESS;
360 } else if (ret != LDB_SUCCESS) {
361 return ret;
364 ldb = ldb_module_get_ctx(module);
365 if (!ldb) {
366 return LDB_SUCCESS;
369 server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
370 if (!server_dn) {
371 DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
372 ldb_dn_get_linearized(res->msgs[0]->dn)));
373 return LDB_SUCCESS;
375 return construct_msds_isrodc_with_server_dn(module, msg, server_dn);
379 construct msDS-isRODC attr
381 static int construct_msds_isrodc(struct ldb_module *module,
382 struct ldb_message *msg, enum ldb_scope scope)
384 struct ldb_message_element * object_class;
385 struct ldb_message_element * object_category;
386 unsigned int i;
388 object_class = ldb_msg_find_element(msg, "objectClass");
389 if (!object_class) {
390 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
391 ldb_dn_get_linearized(msg->dn)));
392 return LDB_ERR_OPERATIONS_ERROR;
395 for (i=0; i<object_class->num_values; i++) {
396 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
397 /* If TO!objectCategory equals the DN of the classSchema object for the nTDSDSA
398 * object class, then TO!msDS-isRODC is false. Otherwise, TO!msDS-isRODC is true.
400 object_category = ldb_msg_find_element(msg, "objectCategory");
401 if (!object_category) {
402 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
403 ldb_dn_get_linearized(msg->dn)));
404 return LDB_SUCCESS;
406 return construct_msds_isrodc_with_dn(module, msg, object_category);
408 if (strequal((const char*)object_class->values[i].data, "server")) {
409 /* Let TN be the nTDSDSA object whose DN is "CN=NTDS Settings," prepended to
410 * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA object" case,
411 * substituting TN for TO.
413 return construct_msds_isrodc_with_server_dn(module, msg, msg->dn);
415 if (strequal((const char*)object_class->values[i].data, "computer")) {
416 /* Let TS be the server object named by TO!serverReferenceBL. Apply the previous
417 * rule for the "TO is a server object" case, substituting TS for TO.
419 return construct_msds_isrodc_with_computer_dn(module, msg);
423 return LDB_SUCCESS;
428 construct msDS-keyVersionNumber attr
430 TODO: Make this based on the 'win2k' DS huristics bit...
433 static int construct_msds_keyversionnumber(struct ldb_module *module,
434 struct ldb_message *msg,
435 enum ldb_scope scope)
437 uint32_t i;
438 enum ndr_err_code ndr_err;
439 const struct ldb_val *omd_value;
440 struct replPropertyMetaDataBlob *omd;
442 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
443 if (!omd_value) {
444 /* We can't make up a key version number without meta data */
445 return LDB_SUCCESS;
447 if (!omd_value) {
448 return LDB_SUCCESS;
451 omd = talloc(msg, struct replPropertyMetaDataBlob);
452 if (!omd) {
453 ldb_module_oom(module);
454 return LDB_SUCCESS;
457 ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
458 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
459 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
460 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
461 ldb_dn_get_linearized(msg->dn)));
462 return LDB_ERR_OPERATIONS_ERROR;
465 if (omd->version != 1) {
466 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
467 omd->version, ldb_dn_get_linearized(msg->dn)));
468 talloc_free(omd);
469 return LDB_SUCCESS;
471 for (i=0; i<omd->ctr.ctr1.count; i++) {
472 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTRIBUTE_unicodePwd) {
473 ldb_msg_add_fmt(msg, "msDS-KeyVersionNumber", "%u", omd->ctr.ctr1.array[i].version);
474 break;
477 return LDB_SUCCESS;
481 struct op_controls_flags {
482 bool sd;
483 bool bypassoperational;
486 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
487 if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
488 return true;
490 return false;
494 a list of attribute names that should be substituted in the parse
495 tree before the search is done
497 static const struct {
498 const char *attr;
499 const char *replace;
500 } parse_tree_sub[] = {
501 { "createTimestamp", "whenCreated" },
502 { "modifyTimestamp", "whenChanged" }
507 a list of attribute names that are hidden, but can be searched for
508 using another (non-hidden) name to produce the correct result
510 static const struct {
511 const char *attr;
512 const char *replace;
513 const char *extra_attr;
514 int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
515 } search_sub[] = {
516 { "createTimestamp", "whenCreated", NULL , NULL },
517 { "modifyTimestamp", "whenChanged", NULL , NULL },
518 { "structuralObjectClass", "objectClass", NULL , NULL },
519 { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
520 { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
521 { "tokenGroups", "objectClass", NULL, construct_token_groups },
522 { "parentGUID", NULL, NULL, construct_parent_guid },
523 { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
524 { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
525 { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
529 enum op_remove {
530 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
531 OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
532 OPERATIONAL_SD_FLAGS, /* show if SD_FLAGS_OID set, or asked for */
533 OPERATIONAL_REMOVE_UNLESS_CONTROL /* remove always unless an adhoc control has been specified */
537 a list of attributes that may need to be removed from the
538 underlying db return
540 Some of these are attributes that were once stored, but are now calculated
542 static const struct {
543 const char *attr;
544 enum op_remove op;
545 } operational_remove[] = {
546 { "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
547 { "msDS-KeyVersionNumber", OPERATIONAL_REMOVE_UNLESS_CONTROL },
548 { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
549 { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
550 { "unicodePwd", OPERATIONAL_REMOVE_UNASKED },
551 { "dBCSPwd", OPERATIONAL_REMOVE_UNASKED },
552 { "ntPwdHistory", OPERATIONAL_REMOVE_UNASKED },
553 { "lmPwdHistory", OPERATIONAL_REMOVE_UNASKED },
554 { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
559 post process a search result record. For any search_sub[] attributes that were
560 asked for, we need to call the appropriate copy routine to copy the result
561 into the message, then remove any attributes that we added to the search but
562 were not asked for by the user
564 static int operational_search_post_process(struct ldb_module *module,
565 struct ldb_message *msg,
566 enum ldb_scope scope,
567 const char * const *attrs_from_user,
568 const char * const *attrs_searched_for,
569 struct op_controls_flags* controls_flags)
571 struct ldb_context *ldb;
572 unsigned int i, a = 0;
573 bool constructed_attributes = false;
575 ldb = ldb_module_get_ctx(module);
577 /* removed any attrs that should not be shown to the user */
578 for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
579 switch (operational_remove[i].op) {
580 case OPERATIONAL_REMOVE_UNASKED:
581 if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
582 continue;
584 if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
585 continue;
587 case OPERATIONAL_REMOVE_ALWAYS:
588 ldb_msg_remove_attr(msg, operational_remove[i].attr);
589 break;
590 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
591 if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
592 ldb_msg_remove_attr(msg, operational_remove[i].attr);
593 break;
594 } else {
595 continue;
597 case OPERATIONAL_SD_FLAGS:
598 if (controls_flags->sd ||
599 ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
600 continue;
602 ldb_msg_remove_attr(msg, operational_remove[i].attr);
603 break;
607 for (a=0;attrs_from_user && attrs_from_user[a];a++) {
608 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
609 continue;
611 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
612 if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
613 continue;
616 /* construct the new attribute, using either a supplied
617 constructor or a simple copy */
618 constructed_attributes = true;
619 if (search_sub[i].constructor != NULL) {
620 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
621 goto failed;
623 } else if (ldb_msg_copy_attr(msg,
624 search_sub[i].replace,
625 search_sub[i].attr) != LDB_SUCCESS) {
626 goto failed;
631 /* Deletion of the search helper attributes are needed if:
632 * - we generated constructed attributes and
633 * - we aren't requesting all attributes
635 if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
636 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
637 /* remove the added search helper attributes, unless
638 * they were asked for by the user */
639 if (search_sub[i].replace != NULL &&
640 !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
641 ldb_msg_remove_attr(msg, search_sub[i].replace);
643 if (search_sub[i].extra_attr != NULL &&
644 !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
645 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
650 return 0;
652 failed:
653 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
654 "operational_search_post_process failed for attribute '%s'",
655 attrs_from_user[a]);
656 return -1;
660 hook search operations
663 struct operational_context {
664 struct ldb_module *module;
665 struct ldb_request *req;
666 enum ldb_scope scope;
667 const char * const *attrs;
668 struct op_controls_flags* controls_flags;
671 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
673 struct operational_context *ac;
674 int ret;
676 ac = talloc_get_type(req->context, struct operational_context);
678 if (!ares) {
679 return ldb_module_done(ac->req, NULL, NULL,
680 LDB_ERR_OPERATIONS_ERROR);
682 if (ares->error != LDB_SUCCESS) {
683 return ldb_module_done(ac->req, ares->controls,
684 ares->response, ares->error);
687 switch (ares->type) {
688 case LDB_REPLY_ENTRY:
689 /* for each record returned post-process to add any derived
690 attributes that have been asked for */
691 ret = operational_search_post_process(ac->module,
692 ares->message,
693 ac->scope,
694 ac->attrs,
695 req->op.search.attrs,
696 ac->controls_flags);
697 if (ret != 0) {
698 return ldb_module_done(ac->req, NULL, NULL,
699 LDB_ERR_OPERATIONS_ERROR);
701 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
703 case LDB_REPLY_REFERRAL:
704 return ldb_module_send_referral(ac->req, ares->referral);
706 case LDB_REPLY_DONE:
708 return ldb_module_done(ac->req, ares->controls,
709 ares->response, LDB_SUCCESS);
712 talloc_free(ares);
713 return LDB_SUCCESS;
716 static int operational_search(struct ldb_module *module, struct ldb_request *req)
718 struct ldb_context *ldb;
719 struct operational_context *ac;
720 struct ldb_request *down_req;
721 const char **search_attrs = NULL;
722 unsigned int i, a;
723 int ret;
725 /* There are no operational attributes on special DNs */
726 if (ldb_dn_is_special(req->op.search.base)) {
727 return ldb_next_request(module, req);
730 ldb = ldb_module_get_ctx(module);
732 ac = talloc(req, struct operational_context);
733 if (ac == NULL) {
734 return LDB_ERR_OPERATIONS_ERROR;
737 ac->module = module;
738 ac->req = req;
739 ac->scope = req->op.search.scope;
740 ac->attrs = req->op.search.attrs;
742 /* FIXME: We must copy the tree and keep the original
743 * unmodified. SSS */
744 /* replace any attributes in the parse tree that are
745 searchable, but are stored using a different name in the
746 backend */
747 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
748 ldb_parse_tree_attr_replace(req->op.search.tree,
749 parse_tree_sub[i].attr,
750 parse_tree_sub[i].replace);
753 ac->controls_flags = talloc(ac, struct op_controls_flags);
754 /* remember if the SD_FLAGS_OID was set */
755 ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
756 /* remember if the LDB_CONTROL_BYPASSOPERATIONAL_OID */
757 ac->controls_flags->bypassoperational = (ldb_request_get_control(req,
758 LDB_CONTROL_BYPASSOPERATIONAL_OID) != NULL);
760 /* in the list of attributes we are looking for, rename any
761 attributes to the alias for any hidden attributes that can
762 be fetched directly using non-hidden names */
763 for (a=0;ac->attrs && ac->attrs[a];a++) {
764 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
765 continue;
767 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
768 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
769 search_sub[i].replace) {
771 if (search_sub[i].extra_attr) {
772 const char **search_attrs2;
773 /* Only adds to the end of the list */
774 search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
775 ? search_attrs
776 : ac->attrs,
777 search_sub[i].extra_attr);
778 if (search_attrs2 == NULL) {
779 return LDB_ERR_OPERATIONS_ERROR;
781 /* may be NULL, talloc_free() doesn't mind */
782 talloc_free(search_attrs);
783 search_attrs = search_attrs2;
786 if (!search_attrs) {
787 search_attrs = ldb_attr_list_copy(req, ac->attrs);
788 if (search_attrs == NULL) {
789 return LDB_ERR_OPERATIONS_ERROR;
792 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
793 search_attrs[a] = search_sub[i].replace;
798 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
799 req->op.search.base,
800 req->op.search.scope,
801 req->op.search.tree,
802 /* use new set of attrs if any */
803 search_attrs == NULL?req->op.search.attrs:search_attrs,
804 req->controls,
805 ac, operational_callback,
806 req);
807 if (ret != LDB_SUCCESS) {
808 return LDB_ERR_OPERATIONS_ERROR;
811 /* perform the search */
812 return ldb_next_request(module, down_req);
815 static int operational_init(struct ldb_module *ctx)
817 struct operational_data *data;
818 int ret;
819 auth_init();
821 ret = ldb_next_init(ctx);
823 if (ret != LDB_SUCCESS) {
824 return ret;
827 data = talloc_zero(ctx, struct operational_data);
828 if (!data) {
829 ldb_module_oom(ctx);
830 return LDB_ERR_OPERATIONS_ERROR;
833 ldb_module_set_private(ctx, data);
835 return LDB_SUCCESS;
838 _PUBLIC_ const struct ldb_module_ops ldb_operational_module_ops = {
839 .name = "operational",
840 .search = operational_search,
841 .init_context = operational_init