s4-selftest: add unit tests for sites's function in python
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / operational.c
blob04b746108983d1bf3aa66fa7dc097729da383c3e
1 /*
2 ldb database library
4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2010
5 Copyright (C) Andrew Tridgell 2005
6 Copyright (C) Simo Sorce 2006-2008
7 Copyright (C) Matthias Dieter Wallnöfer 2009
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 handle operational attributes
28 createTimeStamp: HIDDEN, searchable, ldaptime, alias for whenCreated
29 modifyTimeStamp: HIDDEN, searchable, ldaptime, alias for whenChanged
31 for the above two, we do the search as normal, and if
32 createTimeStamp or modifyTimeStamp is asked for, then do
33 additional searches for whenCreated and whenChanged and fill in
34 the resulting values
36 we also need to replace these with the whenCreated/whenChanged
37 equivalent in the search expression trees
39 whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
40 whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
42 on init we need to setup attribute handlers for these so
43 comparisons are done correctly. The resolution is 1 second.
45 on add we need to add both the above, for current time
47 on modify we need to change whenChanged
49 structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
51 for this one we do the search as normal, then if requested ask
52 for objectclass, change the attribute name, and add it
54 primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
56 contains the RID of a certain group object
59 attributeTypes: in schema only
60 objectClasses: in schema only
61 matchingRules: in schema only
62 matchingRuleUse: in schema only
63 creatorsName: not supported by w2k3?
64 modifiersName: not supported by w2k3?
67 #include "includes.h"
68 #include <ldb.h>
69 #include <ldb_module.h>
71 #include "librpc/gen_ndr/ndr_misc.h"
72 #include "librpc/gen_ndr/ndr_drsblobs.h"
73 #include "param/param.h"
74 #include "dsdb/samdb/samdb.h"
75 #include "dsdb/samdb/ldb_modules/util.h"
77 #include "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,
92 struct ldb_request *parent)
94 char *canonicalName;
95 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
96 if (canonicalName == NULL) {
97 return ldb_operr(ldb_module_get_ctx(module));
99 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
103 construct a primary group token for groups from a message
105 static int construct_primary_group_token(struct ldb_module *module,
106 struct ldb_message *msg, enum ldb_scope scope,
107 struct ldb_request *parent)
109 struct ldb_context *ldb;
110 uint32_t primary_group_token;
112 ldb = ldb_module_get_ctx(module);
113 if (ldb_match_msg_objectclass(msg, "group") == 1) {
114 primary_group_token
115 = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
116 if (primary_group_token == 0) {
117 return LDB_SUCCESS;
120 return samdb_msg_add_uint(ldb, msg, msg, "primaryGroupToken",
121 primary_group_token);
122 } else {
123 return LDB_SUCCESS;
128 construct the token groups for SAM objects from a message
130 static int construct_token_groups(struct ldb_module *module,
131 struct ldb_message *msg, enum ldb_scope scope,
132 struct ldb_request *parent)
134 struct ldb_context *ldb = ldb_module_get_ctx(module);;
135 TALLOC_CTX *tmp_ctx = talloc_new(msg);
136 unsigned int i;
137 int ret;
138 const char *filter;
140 NTSTATUS status;
142 struct dom_sid *primary_group_sid;
143 const char *primary_group_string;
144 const char *primary_group_dn;
145 DATA_BLOB primary_group_blob;
147 struct dom_sid *account_sid;
148 const char *account_sid_string;
149 const char *account_sid_dn;
150 DATA_BLOB account_sid_blob;
151 struct dom_sid *groupSIDs = NULL;
152 unsigned int num_groupSIDs = 0;
154 struct dom_sid *domain_sid;
156 if (scope != LDB_SCOPE_BASE) {
157 ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
158 return LDB_ERR_OPERATIONS_ERROR;
161 /* If it's not a user, it won't have a primaryGroupID */
162 if (ldb_msg_find_element(msg, "primaryGroupID") == NULL) {
163 talloc_free(tmp_ctx);
164 return LDB_SUCCESS;
167 /* Ensure it has an objectSID too */
168 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
169 if (account_sid == NULL) {
170 talloc_free(tmp_ctx);
171 return LDB_SUCCESS;
174 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
175 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
176 talloc_free(tmp_ctx);
177 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
178 } else if (!NT_STATUS_IS_OK(status)) {
179 talloc_free(tmp_ctx);
180 return LDB_ERR_OPERATIONS_ERROR;
183 primary_group_sid = dom_sid_add_rid(tmp_ctx,
184 domain_sid,
185 ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
186 if (!primary_group_sid) {
187 talloc_free(tmp_ctx);
188 return ldb_oom(ldb);
191 /* only return security groups */
192 filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u))",
193 GROUP_TYPE_SECURITY_ENABLED);
194 if (!filter) {
195 talloc_free(tmp_ctx);
196 return ldb_oom(ldb);
199 primary_group_string = dom_sid_string(tmp_ctx, primary_group_sid);
200 if (!primary_group_string) {
201 talloc_free(tmp_ctx);
202 return ldb_oom(ldb);
205 primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string);
206 if (!primary_group_dn) {
207 talloc_free(tmp_ctx);
208 return ldb_oom(ldb);
211 primary_group_blob = data_blob_string_const(primary_group_dn);
213 account_sid_string = dom_sid_string(tmp_ctx, account_sid);
214 if (!account_sid_string) {
215 talloc_free(tmp_ctx);
216 return ldb_oom(ldb);
219 account_sid_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", account_sid_string);
220 if (!account_sid_dn) {
221 talloc_free(tmp_ctx);
222 return ldb_oom(ldb);
225 account_sid_blob = data_blob_string_const(account_sid_dn);
227 status = dsdb_expand_nested_groups(ldb, &account_sid_blob,
228 true, /* We don't want to add the object's SID itself,
229 it's not returend in this attribute */
230 filter,
231 tmp_ctx, &groupSIDs, &num_groupSIDs);
233 if (!NT_STATUS_IS_OK(status)) {
234 ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s",
235 account_sid_string, nt_errstr(status));
236 talloc_free(tmp_ctx);
237 return LDB_ERR_OPERATIONS_ERROR;
240 /* Expands the primary group - this function takes in
241 * memberOf-like values, so we fake one up with the
242 * <SID=S-...> format of DN and then let it expand
243 * them, as long as they meet the filter - so only
244 * domain groups, not builtin groups
246 status = dsdb_expand_nested_groups(ldb, &primary_group_blob, false, filter,
247 tmp_ctx, &groupSIDs, &num_groupSIDs);
248 if (!NT_STATUS_IS_OK(status)) {
249 ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s",
250 account_sid_string, nt_errstr(status));
251 talloc_free(tmp_ctx);
252 return LDB_ERR_OPERATIONS_ERROR;
255 for (i=0; i < num_groupSIDs; i++) {
256 ret = samdb_msg_add_dom_sid(ldb, msg, msg, "tokenGroups", &groupSIDs[i]);
257 if (ret) {
258 talloc_free(tmp_ctx);
259 return ret;
263 return LDB_SUCCESS;
267 construct the parent GUID for an entry from a message
269 static int construct_parent_guid(struct ldb_module *module,
270 struct ldb_message *msg, enum ldb_scope scope,
271 struct ldb_request *parent)
273 struct ldb_result *res, *parent_res;
274 const struct ldb_val *parent_guid;
275 const char *attrs[] = { "instanceType", NULL };
276 const char *attrs2[] = { "objectGUID", NULL };
277 uint32_t instanceType;
278 int ret;
279 struct ldb_dn *parent_dn;
280 struct ldb_val v;
282 /* determine if the object is NC by instance type */
283 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
284 DSDB_FLAG_NEXT_MODULE |
285 DSDB_SEARCH_SHOW_RECYCLED, parent);
286 if (ret != LDB_SUCCESS) {
287 return ret;
290 instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
291 "instanceType", 0);
292 talloc_free(res);
293 if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
294 DEBUG(4,(__location__ ": Object %s is NC\n",
295 ldb_dn_get_linearized(msg->dn)));
296 return LDB_SUCCESS;
298 parent_dn = ldb_dn_get_parent(msg, msg->dn);
300 if (parent_dn == NULL) {
301 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
302 ldb_dn_get_linearized(msg->dn)));
303 return LDB_SUCCESS;
305 ret = dsdb_module_search_dn(module, msg, &parent_res, parent_dn, attrs2,
306 DSDB_FLAG_NEXT_MODULE |
307 DSDB_SEARCH_SHOW_RECYCLED, parent);
308 talloc_free(parent_dn);
310 /* not NC, so the object should have a parent*/
311 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
312 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
313 ldb_dn_get_linearized(msg->dn)));
314 return ldb_operr(ldb_module_get_ctx(module));
315 } else if (ret != LDB_SUCCESS) {
316 return ret;
319 parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
320 if (!parent_guid) {
321 talloc_free(parent_res);
322 return LDB_SUCCESS;
325 v = data_blob_dup_talloc(parent_res, *parent_guid);
326 if (!v.data) {
327 talloc_free(parent_res);
328 return ldb_oom(ldb_module_get_ctx(module));
330 ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
331 talloc_free(parent_res);
332 return ret;
336 construct a subSchemaSubEntry
338 static int construct_subschema_subentry(struct ldb_module *module,
339 struct ldb_message *msg, enum ldb_scope scope,
340 struct ldb_request *parent)
342 struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
343 char *subSchemaSubEntry;
345 /* We may be being called before the init function has finished */
346 if (!data) {
347 return LDB_SUCCESS;
350 /* Try and set this value up, if possible. Don't worry if it
351 * fails, we may not have the DB set up yet, and it's not
352 * really vital anyway */
353 if (!data->aggregate_dn) {
354 struct ldb_context *ldb = ldb_module_get_ctx(module);
355 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
358 if (data->aggregate_dn) {
359 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
360 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
362 return LDB_SUCCESS;
366 static int construct_msds_isrodc_with_dn(struct ldb_module *module,
367 struct ldb_message *msg,
368 struct ldb_message_element *object_category)
370 struct ldb_context *ldb;
371 struct ldb_dn *dn;
372 const struct ldb_val *val;
374 ldb = ldb_module_get_ctx(module);
375 if (!ldb) {
376 DEBUG(4, (__location__ ": Failed to get ldb \n"));
377 return ldb_operr(ldb);
380 dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
381 if (!dn) {
382 DEBUG(4, (__location__ ": Failed to create dn from %s \n",
383 (const char *)object_category->values[0].data));
384 return ldb_operr(ldb);
387 val = ldb_dn_get_rdn_val(dn);
388 if (!val) {
389 DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
390 ldb_dn_get_linearized(dn)));
391 return ldb_operr(ldb);
394 if (strequal((const char *)val->data, "NTDS-DSA")) {
395 ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
396 } else {
397 ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
399 return LDB_SUCCESS;
402 static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
403 struct ldb_message *msg,
404 struct ldb_dn *dn,
405 struct ldb_request *parent)
407 struct ldb_dn *server_dn;
408 const char *attr_obj_cat[] = { "objectCategory", NULL };
409 struct ldb_result *res;
410 struct ldb_message_element *object_category;
411 int ret;
413 server_dn = ldb_dn_copy(msg, dn);
414 if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
415 DEBUG(4, (__location__ ": Failed to add child to %s \n",
416 ldb_dn_get_linearized(server_dn)));
417 return ldb_operr(ldb_module_get_ctx(module));
420 ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
421 DSDB_FLAG_NEXT_MODULE, parent);
422 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
423 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
424 ldb_dn_get_linearized(server_dn)));
425 return LDB_SUCCESS;
426 } else if (ret != LDB_SUCCESS) {
427 return ret;
430 object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
431 if (!object_category) {
432 DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
433 ldb_dn_get_linearized(res->msgs[0]->dn)));
434 return LDB_SUCCESS;
436 return construct_msds_isrodc_with_dn(module, msg, object_category);
439 static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
440 struct ldb_message *msg,
441 struct ldb_request *parent)
443 int ret;
444 struct ldb_dn *server_dn;
446 ret = dsdb_module_reference_dn(module, msg, msg->dn, "serverReferenceBL",
447 &server_dn, parent);
448 if (ret == LDB_ERR_NO_SUCH_OBJECT || ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
449 /* it's OK if we can't find serverReferenceBL attribute */
450 DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
451 ldb_dn_get_linearized(msg->dn)));
452 return LDB_SUCCESS;
453 } else if (ret != LDB_SUCCESS) {
454 return ret;
457 return construct_msds_isrodc_with_server_dn(module, msg, server_dn, parent);
461 construct msDS-isRODC attr
463 static int construct_msds_isrodc(struct ldb_module *module,
464 struct ldb_message *msg, enum ldb_scope scope,
465 struct ldb_request *parent)
467 struct ldb_message_element * object_class;
468 struct ldb_message_element * object_category;
469 unsigned int i;
471 object_class = ldb_msg_find_element(msg, "objectClass");
472 if (!object_class) {
473 DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
474 ldb_dn_get_linearized(msg->dn)));
475 return ldb_operr(ldb_module_get_ctx(module));
478 for (i=0; i<object_class->num_values; i++) {
479 if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
480 /* If TO!objectCategory equals the DN of the classSchema object for the nTDSDSA
481 * object class, then TO!msDS-isRODC is false. Otherwise, TO!msDS-isRODC is true.
483 object_category = ldb_msg_find_element(msg, "objectCategory");
484 if (!object_category) {
485 DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
486 ldb_dn_get_linearized(msg->dn)));
487 return LDB_SUCCESS;
489 return construct_msds_isrodc_with_dn(module, msg, object_category);
491 if (strequal((const char*)object_class->values[i].data, "server")) {
492 /* Let TN be the nTDSDSA object whose DN is "CN=NTDS Settings," prepended to
493 * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA object" case,
494 * substituting TN for TO.
496 return construct_msds_isrodc_with_server_dn(module, msg, msg->dn, parent);
498 if (strequal((const char*)object_class->values[i].data, "computer")) {
499 /* Let TS be the server object named by TO!serverReferenceBL. Apply the previous
500 * rule for the "TO is a server object" case, substituting TS for TO.
502 return construct_msds_isrodc_with_computer_dn(module, msg, parent);
506 return LDB_SUCCESS;
511 construct msDS-keyVersionNumber attr
513 TODO: Make this based on the 'win2k' DS huristics bit...
516 static int construct_msds_keyversionnumber(struct ldb_module *module,
517 struct ldb_message *msg,
518 enum ldb_scope scope,
519 struct ldb_request *parent)
521 uint32_t i;
522 enum ndr_err_code ndr_err;
523 const struct ldb_val *omd_value;
524 struct replPropertyMetaDataBlob *omd;
525 int ret;
527 omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
528 if (!omd_value) {
529 /* We can't make up a key version number without meta data */
530 return LDB_SUCCESS;
532 if (!omd_value) {
533 return LDB_SUCCESS;
536 omd = talloc(msg, struct replPropertyMetaDataBlob);
537 if (!omd) {
538 ldb_module_oom(module);
539 return LDB_SUCCESS;
542 ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
543 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
544 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
545 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
546 ldb_dn_get_linearized(msg->dn)));
547 return ldb_operr(ldb_module_get_ctx(module));
550 if (omd->version != 1) {
551 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
552 omd->version, ldb_dn_get_linearized(msg->dn)));
553 talloc_free(omd);
554 return LDB_SUCCESS;
556 for (i=0; i<omd->ctr.ctr1.count; i++) {
557 if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
558 ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
559 msg, msg,
560 "msDS-KeyVersionNumber",
561 omd->ctr.ctr1.array[i].version);
562 if (ret != LDB_SUCCESS) {
563 talloc_free(omd);
564 return ret;
566 break;
569 return LDB_SUCCESS;
573 struct op_controls_flags {
574 bool sd;
575 bool bypassoperational;
578 static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
579 if (ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 && controls_flags->bypassoperational) {
580 return true;
582 return false;
586 a list of attribute names that should be substituted in the parse
587 tree before the search is done
589 static const struct {
590 const char *attr;
591 const char *replace;
592 } parse_tree_sub[] = {
593 { "createTimeStamp", "whenCreated" },
594 { "modifyTimeStamp", "whenChanged" }
599 a list of attribute names that are hidden, but can be searched for
600 using another (non-hidden) name to produce the correct result
602 static const struct {
603 const char *attr;
604 const char *replace;
605 const char *extra_attr;
606 int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope, struct ldb_request *);
607 } search_sub[] = {
608 { "createTimeStamp", "whenCreated", NULL , NULL },
609 { "modifyTimeStamp", "whenChanged", NULL , NULL },
610 { "structuralObjectClass", "objectClass", NULL , NULL },
611 { "canonicalName", NULL, NULL , construct_canonical_name },
612 { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
613 { "tokenGroups", "primaryGroupID", "objectSid", construct_token_groups },
614 { "parentGUID", NULL, NULL, construct_parent_guid },
615 { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
616 { "msDS-isRODC", "objectClass", "objectCategory", construct_msds_isrodc },
617 { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber }
621 enum op_remove {
622 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
623 OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
624 OPERATIONAL_SD_FLAGS, /* show if SD_FLAGS_OID set, or asked for */
625 OPERATIONAL_REMOVE_UNLESS_CONTROL /* remove always unless an adhoc control has been specified */
629 a list of attributes that may need to be removed from the
630 underlying db return
632 Some of these are attributes that were once stored, but are now calculated
634 static const struct {
635 const char *attr;
636 enum op_remove op;
637 } operational_remove[] = {
638 { "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
639 { "msDS-KeyVersionNumber", OPERATIONAL_REMOVE_UNLESS_CONTROL },
640 { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
641 { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
642 #define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
643 { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
648 post process a search result record. For any search_sub[] attributes that were
649 asked for, we need to call the appropriate copy routine to copy the result
650 into the message, then remove any attributes that we added to the search but
651 were not asked for by the user
653 static int operational_search_post_process(struct ldb_module *module,
654 struct ldb_message *msg,
655 enum ldb_scope scope,
656 const char * const *attrs_from_user,
657 const char * const *attrs_searched_for,
658 struct op_controls_flags* controls_flags,
659 struct ldb_request *parent)
661 struct ldb_context *ldb;
662 unsigned int i, a = 0;
663 bool constructed_attributes = false;
665 ldb = ldb_module_get_ctx(module);
667 /* removed any attrs that should not be shown to the user */
668 for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
669 switch (operational_remove[i].op) {
670 case OPERATIONAL_REMOVE_UNASKED:
671 if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
672 continue;
674 if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
675 continue;
677 case OPERATIONAL_REMOVE_ALWAYS:
678 ldb_msg_remove_attr(msg, operational_remove[i].attr);
679 break;
680 case OPERATIONAL_REMOVE_UNLESS_CONTROL:
681 if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
682 ldb_msg_remove_attr(msg, operational_remove[i].attr);
683 break;
684 } else {
685 continue;
687 case OPERATIONAL_SD_FLAGS:
688 if (controls_flags->sd ||
689 ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
690 continue;
692 ldb_msg_remove_attr(msg, operational_remove[i].attr);
693 break;
697 for (a=0;attrs_from_user && attrs_from_user[a];a++) {
698 if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
699 continue;
701 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
702 if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
703 continue;
706 /* construct the new attribute, using either a supplied
707 constructor or a simple copy */
708 constructed_attributes = true;
709 if (search_sub[i].constructor != NULL) {
710 if (search_sub[i].constructor(module, msg, scope, parent) != LDB_SUCCESS) {
711 goto failed;
713 } else if (ldb_msg_copy_attr(msg,
714 search_sub[i].replace,
715 search_sub[i].attr) != LDB_SUCCESS) {
716 goto failed;
721 /* Deletion of the search helper attributes are needed if:
722 * - we generated constructed attributes and
723 * - we aren't requesting all attributes
725 if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
726 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
727 /* remove the added search helper attributes, unless
728 * they were asked for by the user */
729 if (search_sub[i].replace != NULL &&
730 !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
731 ldb_msg_remove_attr(msg, search_sub[i].replace);
733 if (search_sub[i].extra_attr != NULL &&
734 !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
735 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
740 return 0;
742 failed:
743 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
744 "operational_search_post_process failed for attribute '%s' - %s",
745 attrs_from_user[a], ldb_errstring(ldb));
746 return -1;
750 hook search operations
753 struct operational_context {
754 struct ldb_module *module;
755 struct ldb_request *req;
756 enum ldb_scope scope;
757 const char * const *attrs;
758 struct op_controls_flags* controls_flags;
761 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
763 struct operational_context *ac;
764 int ret;
766 ac = talloc_get_type(req->context, struct operational_context);
768 if (!ares) {
769 return ldb_module_done(ac->req, NULL, NULL,
770 LDB_ERR_OPERATIONS_ERROR);
772 if (ares->error != LDB_SUCCESS) {
773 return ldb_module_done(ac->req, ares->controls,
774 ares->response, ares->error);
777 switch (ares->type) {
778 case LDB_REPLY_ENTRY:
779 /* for each record returned post-process to add any derived
780 attributes that have been asked for */
781 ret = operational_search_post_process(ac->module,
782 ares->message,
783 ac->scope,
784 ac->attrs,
785 req->op.search.attrs,
786 ac->controls_flags, req);
787 if (ret != 0) {
788 return ldb_module_done(ac->req, NULL, NULL,
789 LDB_ERR_OPERATIONS_ERROR);
791 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
793 case LDB_REPLY_REFERRAL:
794 return ldb_module_send_referral(ac->req, ares->referral);
796 case LDB_REPLY_DONE:
798 return ldb_module_done(ac->req, ares->controls,
799 ares->response, LDB_SUCCESS);
802 talloc_free(ares);
803 return LDB_SUCCESS;
806 static int operational_search(struct ldb_module *module, struct ldb_request *req)
808 struct ldb_context *ldb;
809 struct operational_context *ac;
810 struct ldb_request *down_req;
811 const char **search_attrs = NULL;
812 unsigned int i, a;
813 int ret;
815 /* There are no operational attributes on special DNs */
816 if (ldb_dn_is_special(req->op.search.base)) {
817 return ldb_next_request(module, req);
820 ldb = ldb_module_get_ctx(module);
822 ac = talloc(req, struct operational_context);
823 if (ac == NULL) {
824 return ldb_oom(ldb);
827 ac->module = module;
828 ac->req = req;
829 ac->scope = req->op.search.scope;
830 ac->attrs = req->op.search.attrs;
832 /* FIXME: We must copy the tree and keep the original
833 * unmodified. SSS */
834 /* replace any attributes in the parse tree that are
835 searchable, but are stored using a different name in the
836 backend */
837 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
838 ldb_parse_tree_attr_replace(req->op.search.tree,
839 parse_tree_sub[i].attr,
840 parse_tree_sub[i].replace);
843 ac->controls_flags = talloc(ac, struct op_controls_flags);
844 /* remember if the SD_FLAGS_OID was set */
845 ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
846 /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
847 ac->controls_flags->bypassoperational =
848 (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
850 /* in the list of attributes we are looking for, rename any
851 attributes to the alias for any hidden attributes that can
852 be fetched directly using non-hidden names */
853 for (a=0;ac->attrs && ac->attrs[a];a++) {
854 if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
855 continue;
857 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
858 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
859 search_sub[i].replace) {
861 if (search_sub[i].extra_attr) {
862 const char **search_attrs2;
863 /* Only adds to the end of the list */
864 search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
865 ? search_attrs
866 : ac->attrs,
867 search_sub[i].extra_attr);
868 if (search_attrs2 == NULL) {
869 return ldb_operr(ldb);
871 /* may be NULL, talloc_free() doesn't mind */
872 talloc_free(search_attrs);
873 search_attrs = search_attrs2;
876 if (!search_attrs) {
877 search_attrs = ldb_attr_list_copy(req, ac->attrs);
878 if (search_attrs == NULL) {
879 return ldb_operr(ldb);
882 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
883 search_attrs[a] = search_sub[i].replace;
888 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
889 req->op.search.base,
890 req->op.search.scope,
891 req->op.search.tree,
892 /* use new set of attrs if any */
893 search_attrs == NULL?req->op.search.attrs:search_attrs,
894 req->controls,
895 ac, operational_callback,
896 req);
897 LDB_REQ_SET_LOCATION(down_req);
898 if (ret != LDB_SUCCESS) {
899 return ldb_operr(ldb);
902 /* perform the search */
903 return ldb_next_request(module, down_req);
906 static int operational_init(struct ldb_module *ctx)
908 struct operational_data *data;
909 int ret;
911 ret = ldb_next_init(ctx);
913 if (ret != LDB_SUCCESS) {
914 return ret;
917 data = talloc_zero(ctx, struct operational_data);
918 if (!data) {
919 return ldb_module_oom(ctx);
922 ldb_module_set_private(ctx, data);
924 return LDB_SUCCESS;
927 static const struct ldb_module_ops ldb_operational_module_ops = {
928 .name = "operational",
929 .search = operational_search,
930 .init_context = operational_init
933 int ldb_operational_module_init(const char *version)
935 LDB_MODULE_CHECK_VERSION(version);
936 return ldb_register_module(&ldb_operational_module_ops);