idl: Use DRSUAPI_ATTID_ prefix instead of DRSUAPI_ATTRIBUTE_ for ATTID values
[Samba.git] / source4 / dsdb / samdb / ldb_modules / operational.c
blob65ac36a97aec812c23c0556a0273b4f2f6fcb337
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/security.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, *parent_res;
201 const struct ldb_val *parent_guid;
202 const char *attrs[] = { "instanceType", NULL };
203 const char *attrs2[] = { "objectGUID", NULL };
204 uint32_t instanceType;
205 int ret;
206 struct ldb_dn *parent_dn;
207 struct ldb_val v;
209 /* determine if the object is NC by instance type */
210 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
211 DSDB_FLAG_NEXT_MODULE |
212 DSDB_SEARCH_SHOW_RECYCLED);
214 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
215 "instanceType", 0);
216 talloc_free(res);
217 if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
218 DEBUG(4,(__location__ ": Object %s is NC\n",
219 ldb_dn_get_linearized(msg->dn)));
220 return LDB_SUCCESS;
222 parent_dn = ldb_dn_get_parent(msg, msg->dn);
224 if (parent_dn == NULL) {
225 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
226 ldb_dn_get_linearized(msg->dn)));
227 return LDB_SUCCESS;
229 ret = dsdb_module_search_dn(module, msg, &parent_res, parent_dn, attrs2,
230 DSDB_FLAG_NEXT_MODULE |
231 DSDB_SEARCH_SHOW_RECYCLED);
232 talloc_free(parent_dn);
234 /* not NC, so the object should have a parent*/
235 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
236 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
237 ldb_dn_get_linearized(msg->dn)));
238 return ldb_operr(ldb_module_get_ctx(module));
239 } else if (ret != LDB_SUCCESS) {
240 return ret;
243 parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
244 if (!parent_guid) {
245 talloc_free(parent_res);
246 return LDB_SUCCESS;
249 v = data_blob_dup_talloc(parent_res, parent_guid);
250 if (!v.data) {
251 talloc_free(parent_res);
252 return ldb_oom(ldb_module_get_ctx(module));
254 ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
255 talloc_free(parent_res);
256 return ret;
260 construct a subSchemaSubEntry
262 static int construct_subschema_subentry(struct ldb_module *module,
263 struct ldb_message *msg, enum ldb_scope scope)
265 struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
266 char *subSchemaSubEntry;
268 /* We may be being called before the init function has finished */
269 if (!data) {
270 return LDB_SUCCESS;
273 /* Try and set this value up, if possible. Don't worry if it
274 * fails, we may not have the DB set up yet, and it's not
275 * really vital anyway */
276 if (!data->aggregate_dn) {
277 struct ldb_context *ldb = ldb_module_get_ctx(module);
278 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
281 if (data->aggregate_dn) {
282 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
283 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
285 return LDB_SUCCESS;
289 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
290 struct ldb_message *msg,
291 struct ldb_message_element *object_category)
293 struct ldb_context *ldb;
294 struct ldb_dn *dn;
295 const struct ldb_val *val;
297 ldb = ldb_module_get_ctx(module);
298 if (!ldb) {
299 DEBUG(4, (__location__ ": Failed to get ldb \n"));
300 return ldb_operr(ldb);
303 dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
304 if (!dn) {
305 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
306 (const char *)object_category->values[0].data));
307 return ldb_operr(ldb);
310 val = ldb_dn_get_rdn_val(dn);
311 if (!val) {
312 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
313 ldb_dn_get_linearized(dn)));
314 return ldb_operr(ldb);
317 if (strequal((const char *)val->data, "NTDS-DSA")) {
318 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
319 } else {
320 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
322 return LDB_SUCCESS;
325 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
326 struct ldb_message *msg,
327 struct ldb_dn *dn)
329 struct ldb_dn *server_dn;
330 const char *attr_obj_cat[] = { "objectCategory", NULL };
331 struct ldb_result *res;
332 struct ldb_message_element *object_category;
333 int ret;
335 server_dn = ldb_dn_copy(msg, dn);
336 if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
337 DEBUG(4, (__location__ ": Failed to add child to %s \n",
338 ldb_dn_get_linearized(server_dn)));
339 return ldb_operr(ldb_module_get_ctx(module));
342 ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
343 DSDB_FLAG_NEXT_MODULE);
344 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
345 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
346 ldb_dn_get_linearized(server_dn)));
347 return LDB_SUCCESS;
348 } else if (ret != LDB_SUCCESS) {
349 return ret;
352 object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
353 if (!object_category) {
354 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
355 ldb_dn_get_linearized(res->msgs[0]->dn)));
356 return LDB_SUCCESS;
358 return construct_msds_isrodc_with_dn(module, msg, object_category);
361 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
362 struct ldb_message *msg)
364 struct ldb_context *ldb;
365 const char *attr[] = { "serverReferenceBL", NULL };
366 struct ldb_result *res;
367 int ret;
368 struct ldb_dn *server_dn;
370 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attr,
371 DSDB_FLAG_NEXT_MODULE);
372 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
373 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
374 ldb_dn_get_linearized(msg->dn)));
375 return LDB_SUCCESS;
376 } else if (ret != LDB_SUCCESS) {
377 return ret;
380 ldb = ldb_module_get_ctx(module);
381 if (!ldb) {
382 return LDB_SUCCESS;
385 server_dn = ldb_msg_find_attr_as_dn(ldb, msg, res->msgs[0], "serverReferenceBL");
386 if (!server_dn) {
387 DEBUG(4,(__location__ ": Can't find serverReferenceBL for %s \n",
388 ldb_dn_get_linearized(res->msgs[0]->dn)));
389 return LDB_SUCCESS;
391 return construct_msds_isrodc_with_server_dn(module, msg, server_dn);
395 construct msDS-isRODC attr
397 static int construct_msds_isrodc(struct ldb_module *module,
398 struct ldb_message *msg, enum ldb_scope scope)
400 struct ldb_message_element * object_class;
401 struct ldb_message_element * object_category;
402 unsigned int i;
404 object_class = ldb_msg_find_element(msg, "objectClass");
405 if (!object_class) {
406 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
407 ldb_dn_get_linearized(msg->dn)));
408 return ldb_operr(ldb_module_get_ctx(module));
411 for (i=0; i<object_class->num_values; i++) {
412 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
413 /* If TO!objectCategory equals the DN of the classSchema object for the nTDSDSA
414 * object class, then TO!msDS-isRODC is false. Otherwise, TO!msDS-isRODC is true.
416 object_category = ldb_msg_find_element(msg, "objectCategory");
417 if (!object_category) {
418 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
419 ldb_dn_get_linearized(msg->dn)));
420 return LDB_SUCCESS;
422 return construct_msds_isrodc_with_dn(module, msg, object_category);
424 if (strequal((const char*)object_class->values[i].data, "server")) {
425 /* Let TN be the nTDSDSA object whose DN is "CN=NTDS Settings," prepended to
426 * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA object" case,
427 * substituting TN for TO.
429 return construct_msds_isrodc_with_server_dn(module, msg, msg->dn);
431 if (strequal((const char*)object_class->values[i].data, "computer")) {
432 /* Let TS be the server object named by TO!serverReferenceBL. Apply the previous
433 * rule for the "TO is a server object" case, substituting TS for TO.
435 return construct_msds_isrodc_with_computer_dn(module, msg);
439 return LDB_SUCCESS;
444 construct msDS-keyVersionNumber attr
446 TODO: Make this based on the 'win2k' DS huristics bit...
449 static int construct_msds_keyversionnumber(struct ldb_module *module,
450 struct ldb_message *msg,
451 enum ldb_scope scope)
453 uint32_t i;
454 enum ndr_err_code ndr_err;
455 const struct ldb_val *omd_value;
456 struct replPropertyMetaDataBlob *omd;
457 int ret;
459 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
460 if (!omd_value) {
461 /* We can't make up a key version number without meta data */
462 return LDB_SUCCESS;
464 if (!omd_value) {
465 return LDB_SUCCESS;
468 omd = talloc(msg, struct replPropertyMetaDataBlob);
469 if (!omd) {
470 ldb_module_oom(module);
471 return LDB_SUCCESS;
474 ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
475 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
476 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
477 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
478 ldb_dn_get_linearized(msg->dn)));
479 return ldb_operr(ldb_module_get_ctx(module));
482 if (omd->version != 1) {
483 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
484 omd->version, ldb_dn_get_linearized(msg->dn)));
485 talloc_free(omd);
486 return LDB_SUCCESS;
488 for (i=0; i<omd->ctr.ctr1.count; i++) {
489 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
490 ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
491 msg, msg,
492 "msDS-KeyVersionNumber",
493 omd->ctr.ctr1.array[i].version);
494 if (ret != LDB_SUCCESS) {
495 talloc_free(omd);
496 return ret;
498 break;
501 return LDB_SUCCESS;
505 struct op_controls_flags {
506 bool sd;
507 bool bypassoperational;
510 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
511 if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
512 return true;
514 return false;
518 a list of attribute names that should be substituted in the parse
519 tree before the search is done
521 static const struct {
522 const char *attr;
523 const char *replace;
524 } parse_tree_sub[] = {
525 { "createTimestamp", "whenCreated" },
526 { "modifyTimestamp", "whenChanged" }
531 a list of attribute names that are hidden, but can be searched for
532 using another (non-hidden) name to produce the correct result
534 static const struct {
535 const char *attr;
536 const char *replace;
537 const char *extra_attr;
538 int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope);
539 } search_sub[] = {
540 { "createTimestamp", "whenCreated", NULL , NULL },
541 { "modifyTimestamp", "whenChanged", NULL , NULL },
542 { "structuralObjectClass", "objectClass", NULL , NULL },
543 { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
544 { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
545 { "tokenGroups", "objectClass", NULL, construct_token_groups },
546 { "parentGUID", NULL, NULL, construct_parent_guid },
547 { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
548 { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
549 { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
553 enum op_remove {
554 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
555 OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
556 OPERATIONAL_SD_FLAGS, /* show if SD_FLAGS_OID set, or asked for */
557 OPERATIONAL_REMOVE_UNLESS_CONTROL /* remove always unless an adhoc control has been specified */
561 a list of attributes that may need to be removed from the
562 underlying db return
564 Some of these are attributes that were once stored, but are now calculated
566 static const struct {
567 const char *attr;
568 enum op_remove op;
569 } operational_remove[] = {
570 { "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
571 { "msDS-KeyVersionNumber", OPERATIONAL_REMOVE_UNLESS_CONTROL },
572 { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
573 { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
574 { "unicodePwd", OPERATIONAL_REMOVE_UNASKED },
575 { "dBCSPwd", OPERATIONAL_REMOVE_UNASKED },
576 { "ntPwdHistory", OPERATIONAL_REMOVE_UNASKED },
577 { "lmPwdHistory", OPERATIONAL_REMOVE_UNASKED },
578 { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
583 post process a search result record. For any search_sub[] attributes that were
584 asked for, we need to call the appropriate copy routine to copy the result
585 into the message, then remove any attributes that we added to the search but
586 were not asked for by the user
588 static int operational_search_post_process(struct ldb_module *module,
589 struct ldb_message *msg,
590 enum ldb_scope scope,
591 const char * const *attrs_from_user,
592 const char * const *attrs_searched_for,
593 struct op_controls_flags* controls_flags)
595 struct ldb_context *ldb;
596 unsigned int i, a = 0;
597 bool constructed_attributes = false;
599 ldb = ldb_module_get_ctx(module);
601 /* removed any attrs that should not be shown to the user */
602 for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
603 switch (operational_remove[i].op) {
604 case OPERATIONAL_REMOVE_UNASKED:
605 if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
606 continue;
608 if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
609 continue;
611 case OPERATIONAL_REMOVE_ALWAYS:
612 ldb_msg_remove_attr(msg, operational_remove[i].attr);
613 break;
614 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
615 if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
616 ldb_msg_remove_attr(msg, operational_remove[i].attr);
617 break;
618 } else {
619 continue;
621 case OPERATIONAL_SD_FLAGS:
622 if (controls_flags->sd ||
623 ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
624 continue;
626 ldb_msg_remove_attr(msg, operational_remove[i].attr);
627 break;
631 for (a=0;attrs_from_user && attrs_from_user[a];a++) {
632 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
633 continue;
635 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
636 if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
637 continue;
640 /* construct the new attribute, using either a supplied
641 constructor or a simple copy */
642 constructed_attributes = true;
643 if (search_sub[i].constructor != NULL) {
644 if (search_sub[i].constructor(module, msg, scope) != LDB_SUCCESS) {
645 goto failed;
647 } else if (ldb_msg_copy_attr(msg,
648 search_sub[i].replace,
649 search_sub[i].attr) != LDB_SUCCESS) {
650 goto failed;
655 /* Deletion of the search helper attributes are needed if:
656 * - we generated constructed attributes and
657 * - we aren't requesting all attributes
659 if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
660 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
661 /* remove the added search helper attributes, unless
662 * they were asked for by the user */
663 if (search_sub[i].replace != NULL &&
664 !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
665 ldb_msg_remove_attr(msg, search_sub[i].replace);
667 if (search_sub[i].extra_attr != NULL &&
668 !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
669 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
674 return 0;
676 failed:
677 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
678 "operational_search_post_process failed for attribute '%s'",
679 attrs_from_user[a]);
680 return -1;
684 hook search operations
687 struct operational_context {
688 struct ldb_module *module;
689 struct ldb_request *req;
690 enum ldb_scope scope;
691 const char * const *attrs;
692 struct op_controls_flags* controls_flags;
695 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
697 struct operational_context *ac;
698 int ret;
700 ac = talloc_get_type(req->context, struct operational_context);
702 if (!ares) {
703 return ldb_module_done(ac->req, NULL, NULL,
704 LDB_ERR_OPERATIONS_ERROR);
706 if (ares->error != LDB_SUCCESS) {
707 return ldb_module_done(ac->req, ares->controls,
708 ares->response, ares->error);
711 switch (ares->type) {
712 case LDB_REPLY_ENTRY:
713 /* for each record returned post-process to add any derived
714 attributes that have been asked for */
715 ret = operational_search_post_process(ac->module,
716 ares->message,
717 ac->scope,
718 ac->attrs,
719 req->op.search.attrs,
720 ac->controls_flags);
721 if (ret != 0) {
722 return ldb_module_done(ac->req, NULL, NULL,
723 LDB_ERR_OPERATIONS_ERROR);
725 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
727 case LDB_REPLY_REFERRAL:
728 return ldb_module_send_referral(ac->req, ares->referral);
730 case LDB_REPLY_DONE:
732 return ldb_module_done(ac->req, ares->controls,
733 ares->response, LDB_SUCCESS);
736 talloc_free(ares);
737 return LDB_SUCCESS;
740 static int operational_search(struct ldb_module *module, struct ldb_request *req)
742 struct ldb_context *ldb;
743 struct operational_context *ac;
744 struct ldb_request *down_req;
745 const char **search_attrs = NULL;
746 unsigned int i, a;
747 int ret;
749 /* There are no operational attributes on special DNs */
750 if (ldb_dn_is_special(req->op.search.base)) {
751 return ldb_next_request(module, req);
754 ldb = ldb_module_get_ctx(module);
756 ac = talloc(req, struct operational_context);
757 if (ac == NULL) {
758 return ldb_oom(ldb);
761 ac->module = module;
762 ac->req = req;
763 ac->scope = req->op.search.scope;
764 ac->attrs = req->op.search.attrs;
766 /* FIXME: We must copy the tree and keep the original
767 * unmodified. SSS */
768 /* replace any attributes in the parse tree that are
769 searchable, but are stored using a different name in the
770 backend */
771 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
772 ldb_parse_tree_attr_replace(req->op.search.tree,
773 parse_tree_sub[i].attr,
774 parse_tree_sub[i].replace);
777 ac->controls_flags = talloc(ac, struct op_controls_flags);
778 /* remember if the SD_FLAGS_OID was set */
779 ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
780 /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
781 ac->controls_flags->bypassoperational =
782 (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
784 /* in the list of attributes we are looking for, rename any
785 attributes to the alias for any hidden attributes that can
786 be fetched directly using non-hidden names */
787 for (a=0;ac->attrs && ac->attrs[a];a++) {
788 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
789 continue;
791 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
792 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
793 search_sub[i].replace) {
795 if (search_sub[i].extra_attr) {
796 const char **search_attrs2;
797 /* Only adds to the end of the list */
798 search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
799 ? search_attrs
800 : ac->attrs,
801 search_sub[i].extra_attr);
802 if (search_attrs2 == NULL) {
803 return ldb_operr(ldb);
805 /* may be NULL, talloc_free() doesn't mind */
806 talloc_free(search_attrs);
807 search_attrs = search_attrs2;
810 if (!search_attrs) {
811 search_attrs = ldb_attr_list_copy(req, ac->attrs);
812 if (search_attrs == NULL) {
813 return ldb_operr(ldb);
816 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
817 search_attrs[a] = search_sub[i].replace;
822 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
823 req->op.search.base,
824 req->op.search.scope,
825 req->op.search.tree,
826 /* use new set of attrs if any */
827 search_attrs == NULL?req->op.search.attrs:search_attrs,
828 req->controls,
829 ac, operational_callback,
830 req);
831 LDB_REQ_SET_LOCATION(down_req);
832 if (ret != LDB_SUCCESS) {
833 return ldb_operr(ldb);
836 /* perform the search */
837 return ldb_next_request(module, down_req);
840 static int operational_init(struct ldb_module *ctx)
842 struct operational_data *data;
843 int ret;
844 auth_init();
846 ret = ldb_next_init(ctx);
848 if (ret != LDB_SUCCESS) {
849 return ret;
852 data = talloc_zero(ctx, struct operational_data);
853 if (!data) {
854 return ldb_module_oom(ctx);
857 ldb_module_set_private(ctx, data);
859 return LDB_SUCCESS;
862 _PUBLIC_ const struct ldb_module_ops ldb_operational_module_ops = {
863 .name = "operational",
864 .search = operational_search,
865 .init_context = operational_init