s4-source4/dsdb/samdb/ldb_modules/operational.c Use DSDB_FLAG_NEXT_MODULE flag
[Samba.git] / source4 / dsdb / samdb / ldb_modules / operational.c
blobc1da400b8d0a383acbd0bf14922f492c84a40669
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_operr(ldb_module_get_ctx(module));
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 return ldb_module_oom(module);
150 } else if (!NT_STATUS_IS_OK(status)) {
151 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, could not create authContext");
152 talloc_free(tmp_ctx);
153 return LDB_ERR_OPERATIONS_ERROR;
156 status = auth_get_server_info_principal(tmp_ctx, auth_context, NULL, msg->dn, &server_info);
157 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
158 talloc_free(tmp_ctx);
159 return ldb_module_oom(module);
160 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
161 /* Not a user, we have no tokenGroups */
162 talloc_free(tmp_ctx);
163 return LDB_SUCCESS;
164 } else if (!NT_STATUS_IS_OK(status)) {
165 talloc_free(tmp_ctx);
166 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_get_server_info_principal failed: %s", nt_errstr(status));
167 return LDB_ERR_OPERATIONS_ERROR;
170 status = auth_generate_session_info(tmp_ctx, auth_context, server_info, 0, &session_info);
171 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
172 talloc_free(tmp_ctx);
173 return ldb_module_oom(module);
174 } else if (!NT_STATUS_IS_OK(status)) {
175 talloc_free(tmp_ctx);
176 ldb_asprintf_errstring(ldb, "Cannot provide tokenGroups attribute: auth_generate_session_info failed: %s", nt_errstr(status));
177 return LDB_ERR_OPERATIONS_ERROR;
180 /* We start at 1, as the first SID is the user's SID, not included in the tokenGroups */
181 for (i = 1; i < session_info->security_token->num_sids; i++) {
182 ret = samdb_msg_add_dom_sid(ldb, msg, msg,
183 "tokenGroups",
184 session_info->security_token->sids[i]);
185 if (ret != LDB_SUCCESS) {
186 talloc_free(tmp_ctx);
187 return ret;
191 return LDB_SUCCESS;
195 construct the parent GUID for an entry from a message
197 static int construct_parent_guid(struct ldb_module *module,
198 struct ldb_message *msg, enum ldb_scope scope)
200 struct ldb_result *res;
201 const struct ldb_val *parent_guid;
202 const char *attrs[] = { "objectGUID", NULL };
203 int ret;
204 struct ldb_val v;
206 /* TODO: In the future, this needs to honour the partition boundaries */
207 struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
209 if (parent_dn == NULL) {
210 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
211 ldb_dn_get_linearized(msg->dn)));
212 return LDB_SUCCESS;
215 ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs,
216 DSDB_FLAG_NEXT_MODULE |
217 DSDB_SEARCH_SHOW_DELETED);
218 talloc_free(parent_dn);
220 /* if there is no parent 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_oom(ldb_module_get_ctx(module));
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_operr(ldb);
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_operr(ldb);
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_operr(ldb);
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_operr(ldb_module_get_ctx(module));
328 ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
329 DSDB_FLAG_NEXT_MODULE);
330 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
331 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
332 ldb_dn_get_linearized(server_dn)));
333 return LDB_SUCCESS;
334 } else if (ret != LDB_SUCCESS) {
335 return ret;
338 object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
339 if (!object_category) {
340 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
341 ldb_dn_get_linearized(res->msgs[0]->dn)));
342 return LDB_SUCCESS;
344 return construct_msds_isrodc_with_dn(module, msg, object_category);
347 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
348 struct ldb_message *msg)
350 struct ldb_context *ldb;
351 const char *attr[] = { "serverReferenceBL", NULL };
352 struct ldb_result *res;
353 int ret;
354 struct ldb_dn *server_dn;
356 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attr,
357 DSDB_FLAG_NEXT_MODULE);
358 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
359 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
360 ldb_dn_get_linearized(msg->dn)));
361 return LDB_SUCCESS;
362 } else if (ret != LDB_SUCCESS) {
363 return ret;
366 ldb = ldb_module_get_ctx(module);
367 if (!ldb) {
368 return LDB_SUCCESS;
371 server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
372 if (!server_dn) {
373 DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
374 ldb_dn_get_linearized(res->msgs[0]->dn)));
375 return LDB_SUCCESS;
377 return construct_msds_isrodc_with_server_dn(module, msg, server_dn);
381 construct msDS-isRODC attr
383 static int construct_msds_isrodc(struct ldb_module *module,
384 struct ldb_message *msg, enum ldb_scope scope)
386 struct ldb_message_element * object_class;
387 struct ldb_message_element * object_category;
388 unsigned int i;
390 object_class = ldb_msg_find_element(msg, "objectClass");
391 if (!object_class) {
392 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
393 ldb_dn_get_linearized(msg->dn)));
394 return ldb_operr(ldb_module_get_ctx(module));
397 for (i=0; i<object_class->num_values; i++) {
398 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
399 /* If TO!objectCategory equals the DN of the classSchema object for the nTDSDSA
400 * object class, then TO!msDS-isRODC is false. Otherwise, TO!msDS-isRODC is true.
402 object_category = ldb_msg_find_element(msg, "objectCategory");
403 if (!object_category) {
404 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
405 ldb_dn_get_linearized(msg->dn)));
406 return LDB_SUCCESS;
408 return construct_msds_isrodc_with_dn(module, msg, object_category);
410 if (strequal((const char*)object_class->values[i].data, "server")) {
411 /* Let TN be the nTDSDSA object whose DN is "CN=NTDS Settings," prepended to
412 * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA object" case,
413 * substituting TN for TO.
415 return construct_msds_isrodc_with_server_dn(module, msg, msg->dn);
417 if (strequal((const char*)object_class->values[i].data, "computer")) {
418 /* Let TS be the server object named by TO!serverReferenceBL. Apply the previous
419 * rule for the "TO is a server object" case, substituting TS for TO.
421 return construct_msds_isrodc_with_computer_dn(module, msg);
425 return LDB_SUCCESS;
430 construct msDS-keyVersionNumber attr
432 TODO: Make this based on the 'win2k' DS huristics bit...
435 static int construct_msds_keyversionnumber(struct ldb_module *module,
436 struct ldb_message *msg,
437 enum ldb_scope scope)
439 uint32_t i;
440 enum ndr_err_code ndr_err;
441 const struct ldb_val *omd_value;
442 struct replPropertyMetaDataBlob *omd;
444 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
445 if (!omd_value) {
446 /* We can't make up a key version number without meta data */
447 return LDB_SUCCESS;
449 if (!omd_value) {
450 return LDB_SUCCESS;
453 omd = talloc(msg, struct replPropertyMetaDataBlob);
454 if (!omd) {
455 ldb_module_oom(module);
456 return LDB_SUCCESS;
459 ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
460 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
461 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
462 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
463 ldb_dn_get_linearized(msg->dn)));
464 return ldb_operr(ldb_module_get_ctx(module));
467 if (omd->version != 1) {
468 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
469 omd->version, ldb_dn_get_linearized(msg->dn)));
470 talloc_free(omd);
471 return LDB_SUCCESS;
473 for (i=0; i<omd->ctr.ctr1.count; i++) {
474 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTRIBUTE_unicodePwd) {
475 ldb_msg_add_fmt(msg, "msDS-KeyVersionNumber", "%u", omd->ctr.ctr1.array[i].version);
476 break;
479 return LDB_SUCCESS;
483 struct op_controls_flags {
484 bool sd;
485 bool bypassoperational;
488 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
489 if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
490 return true;
492 return false;
496 a list of attribute names that should be substituted in the parse
497 tree before the search is done
499 static const struct {
500 const char *attr;
501 const char *replace;
502 } parse_tree_sub[] = {
503 { "createTimestamp", "whenCreated" },
504 { "modifyTimestamp", "whenChanged" }
509 a list of attribute names that are hidden, but can be searched for
510 using another (non-hidden) name to produce the correct result
512 static const struct {
513 const char *attr;
514 const char *replace;
515 const char *extra_attr;
516 int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
517 } search_sub[] = {
518 { "createTimestamp", "whenCreated", NULL , NULL },
519 { "modifyTimestamp", "whenChanged", NULL , NULL },
520 { "structuralObjectClass", "objectClass", NULL , NULL },
521 { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
522 { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
523 { "tokenGroups", "objectClass", NULL, construct_token_groups },
524 { "parentGUID", NULL, NULL, construct_parent_guid },
525 { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
526 { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
527 { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
531 enum op_remove {
532 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
533 OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
534 OPERATIONAL_SD_FLAGS, /* show if SD_FLAGS_OID set, or asked for */
535 OPERATIONAL_REMOVE_UNLESS_CONTROL /* remove always unless an adhoc control has been specified */
539 a list of attributes that may need to be removed from the
540 underlying db return
542 Some of these are attributes that were once stored, but are now calculated
544 static const struct {
545 const char *attr;
546 enum op_remove op;
547 } operational_remove[] = {
548 { "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
549 { "msDS-KeyVersionNumber", OPERATIONAL_REMOVE_UNLESS_CONTROL },
550 { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
551 { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
552 { "unicodePwd", OPERATIONAL_REMOVE_UNASKED },
553 { "dBCSPwd", OPERATIONAL_REMOVE_UNASKED },
554 { "ntPwdHistory", OPERATIONAL_REMOVE_UNASKED },
555 { "lmPwdHistory", OPERATIONAL_REMOVE_UNASKED },
556 { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
561 post process a search result record. For any search_sub[] attributes that were
562 asked for, we need to call the appropriate copy routine to copy the result
563 into the message, then remove any attributes that we added to the search but
564 were not asked for by the user
566 static int operational_search_post_process(struct ldb_module *module,
567 struct ldb_message *msg,
568 enum ldb_scope scope,
569 const char * const *attrs_from_user,
570 const char * const *attrs_searched_for,
571 struct op_controls_flags* controls_flags)
573 struct ldb_context *ldb;
574 unsigned int i, a = 0;
575 bool constructed_attributes = false;
577 ldb = ldb_module_get_ctx(module);
579 /* removed any attrs that should not be shown to the user */
580 for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
581 switch (operational_remove[i].op) {
582 case OPERATIONAL_REMOVE_UNASKED:
583 if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
584 continue;
586 if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
587 continue;
589 case OPERATIONAL_REMOVE_ALWAYS:
590 ldb_msg_remove_attr(msg, operational_remove[i].attr);
591 break;
592 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
593 if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
594 ldb_msg_remove_attr(msg, operational_remove[i].attr);
595 break;
596 } else {
597 continue;
599 case OPERATIONAL_SD_FLAGS:
600 if (controls_flags->sd ||
601 ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
602 continue;
604 ldb_msg_remove_attr(msg, operational_remove[i].attr);
605 break;
609 for (a=0;attrs_from_user && attrs_from_user[a];a++) {
610 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
611 continue;
613 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
614 if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
615 continue;
618 /* construct the new attribute, using either a supplied
619 constructor or a simple copy */
620 constructed_attributes = true;
621 if (search_sub[i].constructor != NULL) {
622 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
623 goto failed;
625 } else if (ldb_msg_copy_attr(msg,
626 search_sub[i].replace,
627 search_sub[i].attr) != LDB_SUCCESS) {
628 goto failed;
633 /* Deletion of the search helper attributes are needed if:
634 * - we generated constructed attributes and
635 * - we aren't requesting all attributes
637 if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
638 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
639 /* remove the added search helper attributes, unless
640 * they were asked for by the user */
641 if (search_sub[i].replace != NULL &&
642 !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
643 ldb_msg_remove_attr(msg, search_sub[i].replace);
645 if (search_sub[i].extra_attr != NULL &&
646 !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
647 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
652 return 0;
654 failed:
655 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
656 "operational_search_post_process failed for attribute '%s'",
657 attrs_from_user[a]);
658 return -1;
662 hook search operations
665 struct operational_context {
666 struct ldb_module *module;
667 struct ldb_request *req;
668 enum ldb_scope scope;
669 const char * const *attrs;
670 struct op_controls_flags* controls_flags;
673 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
675 struct operational_context *ac;
676 int ret;
678 ac = talloc_get_type(req->context, struct operational_context);
680 if (!ares) {
681 return ldb_module_done(ac->req, NULL, NULL,
682 LDB_ERR_OPERATIONS_ERROR);
684 if (ares->error != LDB_SUCCESS) {
685 return ldb_module_done(ac->req, ares->controls,
686 ares->response, ares->error);
689 switch (ares->type) {
690 case LDB_REPLY_ENTRY:
691 /* for each record returned post-process to add any derived
692 attributes that have been asked for */
693 ret = operational_search_post_process(ac->module,
694 ares->message,
695 ac->scope,
696 ac->attrs,
697 req->op.search.attrs,
698 ac->controls_flags);
699 if (ret != 0) {
700 return ldb_module_done(ac->req, NULL, NULL,
701 LDB_ERR_OPERATIONS_ERROR);
703 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
705 case LDB_REPLY_REFERRAL:
706 return ldb_module_send_referral(ac->req, ares->referral);
708 case LDB_REPLY_DONE:
710 return ldb_module_done(ac->req, ares->controls,
711 ares->response, LDB_SUCCESS);
714 talloc_free(ares);
715 return LDB_SUCCESS;
718 static int operational_search(struct ldb_module *module, struct ldb_request *req)
720 struct ldb_context *ldb;
721 struct operational_context *ac;
722 struct ldb_request *down_req;
723 const char **search_attrs = NULL;
724 unsigned int i, a;
725 int ret;
727 /* There are no operational attributes on special DNs */
728 if (ldb_dn_is_special(req->op.search.base)) {
729 return ldb_next_request(module, req);
732 ldb = ldb_module_get_ctx(module);
734 ac = talloc(req, struct operational_context);
735 if (ac == NULL) {
736 return ldb_oom(ldb);
739 ac->module = module;
740 ac->req = req;
741 ac->scope = req->op.search.scope;
742 ac->attrs = req->op.search.attrs;
744 /* FIXME: We must copy the tree and keep the original
745 * unmodified. SSS */
746 /* replace any attributes in the parse tree that are
747 searchable, but are stored using a different name in the
748 backend */
749 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
750 ldb_parse_tree_attr_replace(req->op.search.tree,
751 parse_tree_sub[i].attr,
752 parse_tree_sub[i].replace);
755 ac->controls_flags = talloc(ac, struct op_controls_flags);
756 /* remember if the SD_FLAGS_OID was set */
757 ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
758 /* remember if the LDB_CONTROL_BYPASSOPERATIONAL_OID */
759 ac->controls_flags->bypassoperational = (ldb_request_get_control(req,
760 LDB_CONTROL_BYPASSOPERATIONAL_OID) != NULL);
762 /* in the list of attributes we are looking for, rename any
763 attributes to the alias for any hidden attributes that can
764 be fetched directly using non-hidden names */
765 for (a=0;ac->attrs && ac->attrs[a];a++) {
766 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
767 continue;
769 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
770 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
771 search_sub[i].replace) {
773 if (search_sub[i].extra_attr) {
774 const char **search_attrs2;
775 /* Only adds to the end of the list */
776 search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
777 ? search_attrs
778 : ac->attrs,
779 search_sub[i].extra_attr);
780 if (search_attrs2 == NULL) {
781 return ldb_operr(ldb);
783 /* may be NULL, talloc_free() doesn't mind */
784 talloc_free(search_attrs);
785 search_attrs = search_attrs2;
788 if (!search_attrs) {
789 search_attrs = ldb_attr_list_copy(req, ac->attrs);
790 if (search_attrs == NULL) {
791 return ldb_operr(ldb);
794 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
795 search_attrs[a] = search_sub[i].replace;
800 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
801 req->op.search.base,
802 req->op.search.scope,
803 req->op.search.tree,
804 /* use new set of attrs if any */
805 search_attrs == NULL?req->op.search.attrs:search_attrs,
806 req->controls,
807 ac, operational_callback,
808 req);
809 if (ret != LDB_SUCCESS) {
810 return ldb_operr(ldb);
813 /* perform the search */
814 return ldb_next_request(module, down_req);
817 static int operational_init(struct ldb_module *ctx)
819 struct operational_data *data;
820 int ret;
821 auth_init();
823 ret = ldb_next_init(ctx);
825 if (ret != LDB_SUCCESS) {
826 return ret;
829 data = talloc_zero(ctx, struct operational_data);
830 if (!data) {
831 return ldb_module_oom(ctx);
834 ldb_module_set_private(ctx, data);
836 return LDB_SUCCESS;
839 _PUBLIC_ const struct ldb_module_ops ldb_operational_module_ops = {
840 .name = "operational",
841 .search = operational_search,
842 .init_context = operational_init