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
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?
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"
80 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
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
)
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) {
113 = samdb_result_rid_from_sid(msg
, msg
, "objectSid", 0);
114 if (primary_group_token
== 0) {
118 return samdb_msg_add_int(ldb
, msg
, msg
, "primaryGroupToken",
119 primary_group_token
);
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
);
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
);
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
,
187 session_info
->security_token
->sids
[i
]);
188 if (ret
!= LDB_SUCCESS
) {
189 talloc_free(tmp_ctx
);
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
};
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
)));
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
)));
226 } else if (ret
!= LDB_SUCCESS
) {
230 parent_guid
= ldb_msg_find_ldb_val(res
->msgs
[0], "objectGUID");
236 v
= data_blob_dup_talloc(res
, parent_guid
);
239 return LDB_ERR_OPERATIONS_ERROR
;
241 ret
= ldb_msg_add_steal_value(msg
, "parentGUID", &v
);
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 */
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
);
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
;
282 const struct ldb_val
*val
;
284 ldb
= ldb_module_get_ctx(module
);
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
);
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
);
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");
307 ldb_msg_add_string(msg
, "msDS-isRODC", "TRUE");
312 static int construct_msds_isrodc_with_server_dn(struct ldb_module
*module
,
313 struct ldb_message
*msg
,
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
;
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
)));
334 } else if (ret
!= LDB_SUCCESS
) {
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
)));
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
;
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
)));
361 } else if (ret
!= LDB_SUCCESS
) {
365 ldb
= ldb_module_get_ctx(module
);
370 server_dn
= ldb_msg_find_attr_as_dn(ldb
, msg
, res
->msgs
[0], "serverReferenceBL");
372 DEBUG(4,(__location__
": Can't find serverReferenceBL for %s \n",
373 ldb_dn_get_linearized(res
->msgs
[0]->dn
)));
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
;
389 object_class
= ldb_msg_find_element(msg
, "objectClass");
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
)));
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
);
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
)
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");
445 /* We can't make up a key version number without meta data */
452 omd
= talloc(msg
, struct replPropertyMetaDataBlob
);
454 ldb_module_oom(module
);
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
)));
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
);
482 struct op_controls_flags
{
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
) {
495 a list of attribute names that should be substituted in the parse
496 tree before the search is done
498 static const struct {
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 {
514 const char *extra_attr
;
515 int (*constructor
)(struct ldb_module
*, struct ldb_message
*, enum ldb_scope
);
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
}
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
541 Some of these are attributes that were once stored, but are now calculated
543 static const struct {
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
)) {
585 if (ldb_attr_in_list(attrs_searched_for
, operational_remove
[i
].attr
)) {
588 case OPERATIONAL_REMOVE_ALWAYS
:
589 ldb_msg_remove_attr(msg
, operational_remove
[i
].attr
);
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
);
598 case OPERATIONAL_SD_FLAGS
:
599 if (controls_flags
->sd
||
600 ldb_attr_in_list(attrs_from_user
, operational_remove
[i
].attr
)) {
603 ldb_msg_remove_attr(msg
, operational_remove
[i
].attr
);
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
])) {
612 for (i
=0;i
<ARRAY_SIZE(search_sub
);i
++) {
613 if (ldb_attr_cmp(attrs_from_user
[a
], search_sub
[i
].attr
) != 0) {
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
) {
624 } else if (ldb_msg_copy_attr(msg
,
625 search_sub
[i
].replace
,
626 search_sub
[i
].attr
) != LDB_SUCCESS
) {
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
);
654 ldb_debug_set(ldb
, LDB_DEBUG_WARNING
,
655 "operational_search_post_process failed for attribute '%s'",
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
;
677 ac
= talloc_get_type(req
->context
, struct operational_context
);
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
,
696 req
->op
.search
.attrs
,
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
);
709 return ldb_module_done(ac
->req
, ares
->controls
,
710 ares
->response
, 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
;
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
);
735 return LDB_ERR_OPERATIONS_ERROR
;
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
745 /* replace any attributes in the parse tree that are
746 searchable, but are stored using a different name in the
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
])) {
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
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
;
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
,
801 req
->op
.search
.scope
,
803 /* use new set of attrs if any */
804 search_attrs
== NULL
?req
->op
.search
.attrs
:search_attrs
,
806 ac
, operational_callback
,
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
;
822 ret
= ldb_next_init(ctx
);
824 if (ret
!= LDB_SUCCESS
) {
828 data
= talloc_zero(ctx
, struct operational_data
);
831 return LDB_ERR_OPERATIONS_ERROR
;
834 ldb_module_set_private(ctx
, data
);
839 _PUBLIC_
const struct ldb_module_ops ldb_operational_module_ops
= {
840 .name
= "operational",
841 .search
= operational_search
,
842 .init_context
= operational_init