s4:dsdb/password_hash: implement DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID
[Samba/ita.git] / source4 / dsdb / samdb / ldb_modules / operational.c
blobf249b357f87fac0a49ddc5dd988149f74f98afef
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);
221 /* if there is no parent for this object, then return */
222 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
223 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
224 ldb_dn_get_linearized(msg->dn)));
225 return LDB_SUCCESS;
226 } else if (ret != LDB_SUCCESS) {
227 return ret;
230 parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
231 if (!parent_guid) {
232 talloc_free(res);
233 return LDB_SUCCESS;
236 v = data_blob_dup_talloc(res, parent_guid);
237 if (!v.data) {
238 talloc_free(res);
239 return LDB_ERR_OPERATIONS_ERROR;
241 ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
242 talloc_free(res);
243 return ret;
247 construct a subSchemaSubEntry
249 static int construct_subschema_subentry(struct ldb_module *module,
250 struct ldb_message *msg, enum ldb_scope scope)
252 struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
253 char *subSchemaSubEntry;
255 /* We may be being called before the init function has finished */
256 if (!data) {
257 return LDB_SUCCESS;
260 /* Try and set this value up, if possible. Don't worry if it
261 * fails, we may not have the DB set up yet, and it's not
262 * really vital anyway */
263 if (!data->aggregate_dn) {
264 struct ldb_context *ldb = ldb_module_get_ctx(module);
265 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
268 if (data->aggregate_dn) {
269 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
270 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
272 return LDB_SUCCESS;
276 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
277 struct ldb_message *msg,
278 struct ldb_message_element *object_category)
280 struct ldb_context *ldb;
281 struct ldb_dn *dn;
282 const struct ldb_val *val;
284 ldb = ldb_module_get_ctx(module);
285 if (!ldb) {
286 DEBUG(4, (__location__ ": Failed to get ldb \n"));
287 return LDB_ERR_OPERATIONS_ERROR;
290 dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
291 if (!dn) {
292 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
293 (const char *)object_category->values[0].data));
294 return LDB_ERR_OPERATIONS_ERROR;
297 val = ldb_dn_get_rdn_val(dn);
298 if (!val) {
299 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
300 ldb_dn_get_linearized(dn)));
301 return LDB_ERR_OPERATIONS_ERROR;
304 if (strequal((const char *)val->data, "NTDS-DSA")) {
305 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
306 } else {
307 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
309 return LDB_SUCCESS;
312 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
313 struct ldb_message *msg,
314 struct ldb_dn *dn)
316 struct ldb_dn *server_dn;
317 const char *attr_obj_cat[] = { "objectCategory", NULL };
318 struct ldb_result *res;
319 struct ldb_message_element *object_category;
320 int ret;
322 server_dn = ldb_dn_copy(msg, dn);
323 if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
324 DEBUG(4, (__location__ ": Failed to add child to %s \n",
325 ldb_dn_get_linearized(server_dn)));
326 return LDB_ERR_OPERATIONS_ERROR;
329 ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat, 0);
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, 0);
357 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
358 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
359 ldb_dn_get_linearized(msg->dn)));
360 return LDB_SUCCESS;
361 } else if (ret != LDB_SUCCESS) {
362 return ret;
365 ldb = ldb_module_get_ctx(module);
366 if (!ldb) {
367 return LDB_SUCCESS;
370 server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
371 if (!server_dn) {
372 DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
373 ldb_dn_get_linearized(res->msgs[0]->dn)));
374 return LDB_SUCCESS;
376 return construct_msds_isrodc_with_server_dn(module, msg, server_dn);
380 construct msDS-isRODC attr
382 static int construct_msds_isrodc(struct ldb_module *module,
383 struct ldb_message *msg, enum ldb_scope scope)
385 struct ldb_message_element * object_class;
386 struct ldb_message_element * object_category;
387 unsigned int i;
389 object_class = ldb_msg_find_element(msg, "objectClass");
390 if (!object_class) {
391 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
392 ldb_dn_get_linearized(msg->dn)));
393 return LDB_ERR_OPERATIONS_ERROR;
396 for (i=0; i<object_class->num_values; i++) {
397 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
398 /* If TO!objectCategory equals the DN of the classSchema object for the nTDSDSA
399 * object class, then TO!msDS-isRODC is false. Otherwise, TO!msDS-isRODC is true.
401 object_category = ldb_msg_find_element(msg, "objectCategory");
402 if (!object_category) {
403 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
404 ldb_dn_get_linearized(msg->dn)));
405 return LDB_SUCCESS;
407 return construct_msds_isrodc_with_dn(module, msg, object_category);
409 if (strequal((const char*)object_class->values[i].data, "server")) {
410 /* Let TN be the nTDSDSA object whose DN is "CN=NTDS Settings," prepended to
411 * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA object" case,
412 * substituting TN for TO.
414 return construct_msds_isrodc_with_server_dn(module, msg, msg->dn);
416 if (strequal((const char*)object_class->values[i].data, "computer")) {
417 /* Let TS be the server object named by TO!serverReferenceBL. Apply the previous
418 * rule for the "TO is a server object" case, substituting TS for TO.
420 return construct_msds_isrodc_with_computer_dn(module, msg);
424 return LDB_SUCCESS;
429 construct msDS-keyVersionNumber attr
431 TODO: Make this based on the 'win2k' DS huristics bit...
434 static int construct_msds_keyversionnumber(struct ldb_module *module,
435 struct ldb_message *msg,
436 enum ldb_scope scope)
438 uint32_t i;
439 enum ndr_err_code ndr_err;
440 const struct ldb_val *omd_value;
441 struct replPropertyMetaDataBlob *omd;
443 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
444 if (!omd_value) {
445 /* We can't make up a key version number without meta data */
446 return LDB_SUCCESS;
448 if (!omd_value) {
449 return LDB_SUCCESS;
452 omd = talloc(msg, struct replPropertyMetaDataBlob);
453 if (!omd) {
454 ldb_module_oom(module);
455 return LDB_SUCCESS;
458 ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
459 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
460 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
461 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
462 ldb_dn_get_linearized(msg->dn)));
463 return LDB_ERR_OPERATIONS_ERROR;
466 if (omd->version != 1) {
467 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
468 omd->version, ldb_dn_get_linearized(msg->dn)));
469 talloc_free(omd);
470 return LDB_SUCCESS;
472 for (i=0; i<omd->ctr.ctr1.count; i++) {
473 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTRIBUTE_unicodePwd) {
474 ldb_msg_add_fmt(msg, "msDS-KeyVersionNumber", "%u", omd->ctr.ctr1.array[i].version);
475 break;
478 return LDB_SUCCESS;
482 struct op_controls_flags {
483 bool sd;
484 bool bypassoperational;
487 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
488 if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
489 return true;
491 return false;
495 a list of attribute names that should be substituted in the parse
496 tree before the search is done
498 static const struct {
499 const char *attr;
500 const char *replace;
501 } parse_tree_sub[] = {
502 { "createTimestamp", "whenCreated" },
503 { "modifyTimestamp", "whenChanged" }
508 a list of attribute names that are hidden, but can be searched for
509 using another (non-hidden) name to produce the correct result
511 static const struct {
512 const char *attr;
513 const char *replace;
514 const char *extra_attr;
515 int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
516 } search_sub[] = {
517 { "createTimestamp", "whenCreated", NULL , NULL },
518 { "modifyTimestamp", "whenChanged", NULL , NULL },
519 { "structuralObjectClass", "objectClass", NULL , NULL },
520 { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
521 { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
522 { "tokenGroups", "objectClass", NULL, construct_token_groups },
523 { "parentGUID", NULL, NULL, construct_parent_guid },
524 { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
525 { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
526 { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
530 enum op_remove {
531 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
532 OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
533 OPERATIONAL_SD_FLAGS, /* show if SD_FLAGS_OID set, or asked for */
534 OPERATIONAL_REMOVE_UNLESS_CONTROL /* remove always unless an adhoc control has been specified */
538 a list of attributes that may need to be removed from the
539 underlying db return
541 Some of these are attributes that were once stored, but are now calculated
543 static const struct {
544 const char *attr;
545 enum op_remove op;
546 } operational_remove[] = {
547 { "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
548 { "msDS-KeyVersionNumber", OPERATIONAL_REMOVE_UNLESS_CONTROL },
549 { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
550 { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
551 { "unicodePwd", OPERATIONAL_REMOVE_UNASKED },
552 { "dBCSPwd", OPERATIONAL_REMOVE_UNASKED },
553 { "ntPwdHistory", OPERATIONAL_REMOVE_UNASKED },
554 { "lmPwdHistory", OPERATIONAL_REMOVE_UNASKED },
555 { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
560 post process a search result record. For any search_sub[] attributes that were
561 asked for, we need to call the appropriate copy routine to copy the result
562 into the message, then remove any attributes that we added to the search but
563 were not asked for by the user
565 static int operational_search_post_process(struct ldb_module *module,
566 struct ldb_message *msg,
567 enum ldb_scope scope,
568 const char * const *attrs_from_user,
569 const char * const *attrs_searched_for,
570 struct op_controls_flags* controls_flags)
572 struct ldb_context *ldb;
573 unsigned int i, a = 0;
574 bool constructed_attributes = false;
576 ldb = ldb_module_get_ctx(module);
578 /* removed any attrs that should not be shown to the user */
579 for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
580 switch (operational_remove[i].op) {
581 case OPERATIONAL_REMOVE_UNASKED:
582 if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
583 continue;
585 if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
586 continue;
588 case OPERATIONAL_REMOVE_ALWAYS:
589 ldb_msg_remove_attr(msg, operational_remove[i].attr);
590 break;
591 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
592 if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
593 ldb_msg_remove_attr(msg, operational_remove[i].attr);
594 break;
595 } else {
596 continue;
598 case OPERATIONAL_SD_FLAGS:
599 if (controls_flags->sd ||
600 ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
601 continue;
603 ldb_msg_remove_attr(msg, operational_remove[i].attr);
604 break;
608 for (a=0;attrs_from_user && attrs_from_user[a];a++) {
609 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
610 continue;
612 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
613 if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
614 continue;
617 /* construct the new attribute, using either a supplied
618 constructor or a simple copy */
619 constructed_attributes = true;
620 if (search_sub[i].constructor != NULL) {
621 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
622 goto failed;
624 } else if (ldb_msg_copy_attr(msg,
625 search_sub[i].replace,
626 search_sub[i].attr) != LDB_SUCCESS) {
627 goto failed;
632 /* Deletion of the search helper attributes are needed if:
633 * - we generated constructed attributes and
634 * - we aren't requesting all attributes
636 if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
637 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
638 /* remove the added search helper attributes, unless
639 * they were asked for by the user */
640 if (search_sub[i].replace != NULL &&
641 !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
642 ldb_msg_remove_attr(msg, search_sub[i].replace);
644 if (search_sub[i].extra_attr != NULL &&
645 !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
646 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
651 return 0;
653 failed:
654 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
655 "operational_search_post_process failed for attribute '%s'",
656 attrs_from_user[a]);
657 return -1;
661 hook search operations
664 struct operational_context {
665 struct ldb_module *module;
666 struct ldb_request *req;
667 enum ldb_scope scope;
668 const char * const *attrs;
669 struct op_controls_flags* controls_flags;
672 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
674 struct operational_context *ac;
675 int ret;
677 ac = talloc_get_type(req->context, struct operational_context);
679 if (!ares) {
680 return ldb_module_done(ac->req, NULL, NULL,
681 LDB_ERR_OPERATIONS_ERROR);
683 if (ares->error != LDB_SUCCESS) {
684 return ldb_module_done(ac->req, ares->controls,
685 ares->response, ares->error);
688 switch (ares->type) {
689 case LDB_REPLY_ENTRY:
690 /* for each record returned post-process to add any derived
691 attributes that have been asked for */
692 ret = operational_search_post_process(ac->module,
693 ares->message,
694 ac->scope,
695 ac->attrs,
696 req->op.search.attrs,
697 ac->controls_flags);
698 if (ret != 0) {
699 return ldb_module_done(ac->req, NULL, NULL,
700 LDB_ERR_OPERATIONS_ERROR);
702 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
704 case LDB_REPLY_REFERRAL:
705 return ldb_module_send_referral(ac->req, ares->referral);
707 case LDB_REPLY_DONE:
709 return ldb_module_done(ac->req, ares->controls,
710 ares->response, LDB_SUCCESS);
713 talloc_free(ares);
714 return LDB_SUCCESS;
717 static int operational_search(struct ldb_module *module, struct ldb_request *req)
719 struct ldb_context *ldb;
720 struct operational_context *ac;
721 struct ldb_request *down_req;
722 const char **search_attrs = NULL;
723 unsigned int i, a;
724 int ret;
726 /* There are no operational attributes on special DNs */
727 if (ldb_dn_is_special(req->op.search.base)) {
728 return ldb_next_request(module, req);
731 ldb = ldb_module_get_ctx(module);
733 ac = talloc(req, struct operational_context);
734 if (ac == NULL) {
735 return LDB_ERR_OPERATIONS_ERROR;
738 ac->module = module;
739 ac->req = req;
740 ac->scope = req->op.search.scope;
741 ac->attrs = req->op.search.attrs;
743 /* FIXME: We must copy the tree and keep the original
744 * unmodified. SSS */
745 /* replace any attributes in the parse tree that are
746 searchable, but are stored using a different name in the
747 backend */
748 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
749 ldb_parse_tree_attr_replace(req->op.search.tree,
750 parse_tree_sub[i].attr,
751 parse_tree_sub[i].replace);
754 ac->controls_flags = talloc(ac, struct op_controls_flags);
755 /* remember if the SD_FLAGS_OID was set */
756 ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
757 /* remember if the LDB_CONTROL_BYPASSOPERATIONAL_OID */
758 ac->controls_flags->bypassoperational = (ldb_request_get_control(req,
759 LDB_CONTROL_BYPASSOPERATIONAL_OID) != NULL);
761 /* in the list of attributes we are looking for, rename any
762 attributes to the alias for any hidden attributes that can
763 be fetched directly using non-hidden names */
764 for (a=0;ac->attrs && ac->attrs[a];a++) {
765 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
766 continue;
768 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
769 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
770 search_sub[i].replace) {
772 if (search_sub[i].extra_attr) {
773 const char **search_attrs2;
774 /* Only adds to the end of the list */
775 search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
776 ? search_attrs
777 : ac->attrs,
778 search_sub[i].extra_attr);
779 if (search_attrs2 == NULL) {
780 return LDB_ERR_OPERATIONS_ERROR;
782 /* may be NULL, talloc_free() doesn't mind */
783 talloc_free(search_attrs);
784 search_attrs = search_attrs2;
787 if (!search_attrs) {
788 search_attrs = ldb_attr_list_copy(req, ac->attrs);
789 if (search_attrs == NULL) {
790 return LDB_ERR_OPERATIONS_ERROR;
793 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
794 search_attrs[a] = search_sub[i].replace;
799 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
800 req->op.search.base,
801 req->op.search.scope,
802 req->op.search.tree,
803 /* use new set of attrs if any */
804 search_attrs == NULL?req->op.search.attrs:search_attrs,
805 req->controls,
806 ac, operational_callback,
807 req);
808 if (ret != LDB_SUCCESS) {
809 return LDB_ERR_OPERATIONS_ERROR;
812 /* perform the search */
813 return ldb_next_request(module, down_req);
816 static int operational_init(struct ldb_module *ctx)
818 struct operational_data *data;
819 int ret;
820 auth_init();
822 ret = ldb_next_init(ctx);
824 if (ret != LDB_SUCCESS) {
825 return ret;
828 data = talloc_zero(ctx, struct operational_data);
829 if (!data) {
830 ldb_module_oom(ctx);
831 return LDB_ERR_OPERATIONS_ERROR;
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