Line data Source code
1 : /*
2 : ldb database library
3 :
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
8 :
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.
13 :
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.
18 :
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/>.
21 : */
22 :
23 : /*
24 : handle operational attributes
25 : */
26 :
27 : /*
28 : createTimeStamp: HIDDEN, searchable, ldaptime, alias for whenCreated
29 : modifyTimeStamp: HIDDEN, searchable, ldaptime, alias for whenChanged
30 :
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
35 :
36 : we also need to replace these with the whenCreated/whenChanged
37 : equivalent in the search expression trees
38 :
39 : whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
40 : whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
41 :
42 : on init we need to setup attribute handlers for these so
43 : comparisons are done correctly. The resolution is 1 second.
44 :
45 : on add we need to add both the above, for current time
46 :
47 : on modify we need to change whenChanged
48 :
49 : structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
50 :
51 : for this one we do the search as normal, then if requested ask
52 : for objectclass, change the attribute name, and add it
53 :
54 : primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
55 :
56 : contains the RID of a certain group object
57 :
58 :
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?
65 : */
66 :
67 : #include "includes.h"
68 : #include <ldb.h>
69 : #include <ldb_module.h>
70 :
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"
76 :
77 : #include "libcli/security/security.h"
78 :
79 : #ifndef ARRAY_SIZE
80 : #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
81 : #endif
82 :
83 : #undef strcasecmp
84 :
85 : struct operational_data {
86 : struct ldb_dn *aggregate_dn;
87 : };
88 :
89 : enum search_type {
90 : TOKEN_GROUPS,
91 : TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL,
92 : TOKEN_GROUPS_NO_GC_ACCEPTABLE,
93 :
94 : /*
95 : * MS-DRSR 4.1.8.1.3 RevMembGetAccountGroups: Transitive membership in
96 : * all account groups in a given domain, excluding built-in groups.
97 : * (Used internally for msDS-ResultantPSO support)
98 : */
99 : ACCOUNT_GROUPS
100 : };
101 :
102 : static int get_pso_for_user(struct ldb_module *module,
103 : struct ldb_message *user_msg,
104 : struct ldb_request *parent,
105 : struct ldb_message **pso_msg);
106 :
107 : /*
108 : construct a canonical name from a message
109 : */
110 31 : static int construct_canonical_name(struct ldb_module *module,
111 : struct ldb_message *msg, enum ldb_scope scope,
112 : struct ldb_request *parent)
113 : {
114 : char *canonicalName;
115 31 : canonicalName = ldb_dn_canonical_string(msg, msg->dn);
116 31 : if (canonicalName == NULL) {
117 0 : return ldb_operr(ldb_module_get_ctx(module));
118 : }
119 31 : return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
120 : }
121 :
122 : /*
123 : construct a primary group token for groups from a message
124 : */
125 18 : static int construct_primary_group_token(struct ldb_module *module,
126 : struct ldb_message *msg, enum ldb_scope scope,
127 : struct ldb_request *parent)
128 : {
129 : struct ldb_context *ldb;
130 : uint32_t primary_group_token;
131 :
132 18 : ldb = ldb_module_get_ctx(module);
133 18 : if (ldb_match_msg_objectclass(msg, "group") == 1) {
134 : primary_group_token
135 6 : = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
136 6 : if (primary_group_token == 0) {
137 0 : return LDB_SUCCESS;
138 : }
139 :
140 6 : return samdb_msg_add_uint(ldb, msg, msg, "primaryGroupToken",
141 : primary_group_token);
142 : } else {
143 12 : return LDB_SUCCESS;
144 : }
145 : }
146 :
147 : /*
148 : * Returns the group SIDs for the user in the given LDB message
149 : */
150 4720 : static int get_group_sids(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
151 : struct ldb_message *msg, const char *attribute_string,
152 : enum search_type type, struct dom_sid **groupSIDs,
153 : unsigned int *num_groupSIDs)
154 : {
155 4720 : const char *filter = NULL;
156 : NTSTATUS status;
157 : struct dom_sid *primary_group_sid;
158 : const char *primary_group_string;
159 : const char *primary_group_dn;
160 : DATA_BLOB primary_group_blob;
161 : struct dom_sid *account_sid;
162 : const char *account_sid_string;
163 : const char *account_sid_dn;
164 : DATA_BLOB account_sid_blob;
165 : struct dom_sid *domain_sid;
166 :
167 : /* If it's not a user, it won't have a primaryGroupID */
168 4720 : if (ldb_msg_find_element(msg, "primaryGroupID") == NULL) {
169 4 : return LDB_SUCCESS;
170 : }
171 :
172 : /* Ensure it has an objectSID too */
173 4716 : account_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
174 4716 : if (account_sid == NULL) {
175 0 : return LDB_SUCCESS;
176 : }
177 :
178 4716 : status = dom_sid_split_rid(mem_ctx, account_sid, &domain_sid, NULL);
179 4716 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
180 0 : return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
181 4716 : } else if (!NT_STATUS_IS_OK(status)) {
182 0 : return LDB_ERR_OPERATIONS_ERROR;
183 : }
184 :
185 4716 : primary_group_sid = dom_sid_add_rid(mem_ctx,
186 : domain_sid,
187 : ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
188 4716 : if (!primary_group_sid) {
189 0 : return ldb_oom(ldb);
190 : }
191 :
192 : /* only return security groups */
193 4716 : switch(type) {
194 4 : case TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL:
195 4 : filter = talloc_asprintf(mem_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u)(|(groupType:1.2.840.113556.1.4.803:=%u)(groupType:1.2.840.113556.1.4.803:=%u)))",
196 : GROUP_TYPE_SECURITY_ENABLED, GROUP_TYPE_ACCOUNT_GROUP, GROUP_TYPE_UNIVERSAL_GROUP);
197 4 : break;
198 3708 : case TOKEN_GROUPS_NO_GC_ACCEPTABLE:
199 : case TOKEN_GROUPS:
200 3708 : filter = talloc_asprintf(mem_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u))",
201 : GROUP_TYPE_SECURITY_ENABLED);
202 3708 : break;
203 :
204 : /* for RevMembGetAccountGroups, exclude built-in groups */
205 1004 : case ACCOUNT_GROUPS:
206 1004 : filter = talloc_asprintf(mem_ctx, "(&(objectClass=group)(!(groupType:1.2.840.113556.1.4.803:=%u))(groupType:1.2.840.113556.1.4.803:=%u))",
207 : GROUP_TYPE_BUILTIN_LOCAL_GROUP, GROUP_TYPE_SECURITY_ENABLED);
208 1004 : break;
209 : }
210 :
211 4716 : if (!filter) {
212 0 : return ldb_oom(ldb);
213 : }
214 :
215 4716 : primary_group_string = dom_sid_string(mem_ctx, primary_group_sid);
216 4716 : if (!primary_group_string) {
217 0 : return ldb_oom(ldb);
218 : }
219 :
220 4716 : primary_group_dn = talloc_asprintf(mem_ctx, "<SID=%s>", primary_group_string);
221 4716 : if (!primary_group_dn) {
222 0 : return ldb_oom(ldb);
223 : }
224 :
225 4716 : primary_group_blob = data_blob_string_const(primary_group_dn);
226 :
227 4716 : account_sid_string = dom_sid_string(mem_ctx, account_sid);
228 4716 : if (!account_sid_string) {
229 0 : return ldb_oom(ldb);
230 : }
231 :
232 4716 : account_sid_dn = talloc_asprintf(mem_ctx, "<SID=%s>", account_sid_string);
233 4716 : if (!account_sid_dn) {
234 0 : return ldb_oom(ldb);
235 : }
236 :
237 4716 : account_sid_blob = data_blob_string_const(account_sid_dn);
238 :
239 4716 : status = dsdb_expand_nested_groups(ldb, &account_sid_blob,
240 : true, /* We don't want to add the object's SID itself,
241 : it's not returend in this attribute */
242 : filter,
243 : mem_ctx, groupSIDs, num_groupSIDs);
244 :
245 4716 : if (!NT_STATUS_IS_OK(status)) {
246 0 : ldb_asprintf_errstring(ldb, "Failed to construct %s: expanding groups of SID %s failed: %s",
247 : attribute_string, account_sid_string,
248 : nt_errstr(status));
249 0 : return LDB_ERR_OPERATIONS_ERROR;
250 : }
251 :
252 : /* Expands the primary group - this function takes in
253 : * memberOf-like values, so we fake one up with the
254 : * <SID=S-...> format of DN and then let it expand
255 : * them, as long as they meet the filter - so only
256 : * domain groups, not builtin groups
257 : */
258 4716 : status = dsdb_expand_nested_groups(ldb, &primary_group_blob, false, filter,
259 : mem_ctx, groupSIDs, num_groupSIDs);
260 4716 : if (!NT_STATUS_IS_OK(status)) {
261 0 : ldb_asprintf_errstring(ldb, "Failed to construct %s: expanding groups of SID %s failed: %s",
262 : attribute_string, account_sid_string,
263 : nt_errstr(status));
264 0 : return LDB_ERR_OPERATIONS_ERROR;
265 : }
266 :
267 4716 : return LDB_SUCCESS;
268 : }
269 :
270 : /*
271 : construct the token groups for SAM objects from a message
272 : */
273 3716 : static int construct_generic_token_groups(struct ldb_module *module,
274 : struct ldb_message *msg, enum ldb_scope scope,
275 : struct ldb_request *parent,
276 : const char *attribute_string,
277 : enum search_type type)
278 : {
279 3716 : struct ldb_context *ldb = ldb_module_get_ctx(module);
280 3716 : TALLOC_CTX *tmp_ctx = talloc_new(msg);
281 : unsigned int i;
282 : int ret;
283 3716 : struct dom_sid *groupSIDs = NULL;
284 3716 : unsigned int num_groupSIDs = 0;
285 :
286 3716 : if (scope != LDB_SCOPE_BASE) {
287 0 : ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search");
288 0 : return LDB_ERR_OPERATIONS_ERROR;
289 : }
290 :
291 : /* calculate the group SIDs for this object */
292 3716 : ret = get_group_sids(ldb, tmp_ctx, msg, attribute_string, type,
293 : &groupSIDs, &num_groupSIDs);
294 :
295 3716 : if (ret != LDB_SUCCESS) {
296 0 : talloc_free(tmp_ctx);
297 0 : return LDB_ERR_OPERATIONS_ERROR;
298 : }
299 :
300 : /* add these SIDs to the search result */
301 30267 : for (i=0; i < num_groupSIDs; i++) {
302 26551 : ret = samdb_msg_add_dom_sid(ldb, msg, msg, attribute_string, &groupSIDs[i]);
303 26551 : if (ret) {
304 0 : talloc_free(tmp_ctx);
305 0 : return ret;
306 : }
307 : }
308 :
309 3716 : return LDB_SUCCESS;
310 : }
311 :
312 3712 : static int construct_token_groups(struct ldb_module *module,
313 : struct ldb_message *msg, enum ldb_scope scope,
314 : struct ldb_request *parent)
315 : {
316 : /**
317 : * TODO: Add in a limiting domain when we start to support
318 : * trusted domains.
319 : */
320 3712 : return construct_generic_token_groups(module, msg, scope, parent,
321 : "tokenGroups",
322 : TOKEN_GROUPS);
323 : }
324 :
325 0 : static int construct_token_groups_no_gc(struct ldb_module *module,
326 : struct ldb_message *msg, enum ldb_scope scope,
327 : struct ldb_request *parent)
328 : {
329 : /**
330 : * TODO: Add in a limiting domain when we start to support
331 : * trusted domains.
332 : */
333 0 : return construct_generic_token_groups(module, msg, scope, parent,
334 : "tokenGroupsNoGCAcceptable",
335 : TOKEN_GROUPS);
336 : }
337 :
338 4 : static int construct_global_universal_token_groups(struct ldb_module *module,
339 : struct ldb_message *msg, enum ldb_scope scope,
340 : struct ldb_request *parent)
341 : {
342 4 : return construct_generic_token_groups(module, msg, scope, parent,
343 : "tokenGroupsGlobalAndUniversal",
344 : TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL);
345 : }
346 : /*
347 : construct the parent GUID for an entry from a message
348 : */
349 606743 : static int construct_parent_guid(struct ldb_module *module,
350 : struct ldb_message *msg, enum ldb_scope scope,
351 : struct ldb_request *parent)
352 : {
353 : struct ldb_result *res, *parent_res;
354 : const struct ldb_val *parent_guid;
355 606743 : const char *attrs[] = { "instanceType", NULL };
356 606743 : const char *attrs2[] = { "objectGUID", NULL };
357 : uint32_t instanceType;
358 : int ret;
359 : struct ldb_dn *parent_dn;
360 : struct ldb_val v;
361 :
362 : /* determine if the object is NC by instance type */
363 606743 : ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
364 : DSDB_FLAG_NEXT_MODULE |
365 : DSDB_SEARCH_SHOW_RECYCLED, parent);
366 606743 : if (ret != LDB_SUCCESS) {
367 0 : return ret;
368 : }
369 :
370 606743 : instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
371 : "instanceType", 0);
372 606743 : talloc_free(res);
373 606743 : if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
374 2266 : DEBUG(4,(__location__ ": Object %s is NC\n",
375 : ldb_dn_get_linearized(msg->dn)));
376 2266 : return LDB_SUCCESS;
377 : }
378 604477 : parent_dn = ldb_dn_get_parent(msg, msg->dn);
379 :
380 604477 : if (parent_dn == NULL) {
381 0 : DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
382 : ldb_dn_get_linearized(msg->dn)));
383 0 : return LDB_ERR_OTHER;
384 : }
385 604477 : ret = dsdb_module_search_dn(module, msg, &parent_res, parent_dn, attrs2,
386 : DSDB_FLAG_NEXT_MODULE |
387 : DSDB_SEARCH_SHOW_RECYCLED, parent);
388 : /* not NC, so the object should have a parent*/
389 604477 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
390 0 : ret = ldb_error(ldb_module_get_ctx(module), LDB_ERR_OPERATIONS_ERROR,
391 : talloc_asprintf(msg, "Parent dn %s for %s does not exist",
392 : ldb_dn_get_linearized(parent_dn),
393 : ldb_dn_get_linearized(msg->dn)));
394 0 : talloc_free(parent_dn);
395 0 : return ret;
396 604477 : } else if (ret != LDB_SUCCESS) {
397 0 : talloc_free(parent_dn);
398 0 : return ret;
399 : }
400 604477 : talloc_free(parent_dn);
401 :
402 604477 : parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
403 604477 : if (!parent_guid) {
404 0 : talloc_free(parent_res);
405 0 : return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
406 : }
407 :
408 604477 : v = data_blob_dup_talloc(parent_res, *parent_guid);
409 604477 : if (!v.data) {
410 0 : talloc_free(parent_res);
411 0 : return ldb_oom(ldb_module_get_ctx(module));
412 : }
413 604477 : ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
414 604477 : talloc_free(parent_res);
415 604477 : return ret;
416 : }
417 :
418 1 : static int construct_modifyTimeStamp(struct ldb_module *module,
419 : struct ldb_message *msg, enum ldb_scope scope,
420 : struct ldb_request *parent)
421 : {
422 1 : struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
423 1 : struct ldb_context *ldb = ldb_module_get_ctx(module);
424 :
425 : /* We may be being called before the init function has finished */
426 1 : if (!data) {
427 0 : return LDB_SUCCESS;
428 : }
429 :
430 : /* Try and set this value up, if possible. Don't worry if it
431 : * fails, we may not have the DB set up yet.
432 : */
433 1 : if (!data->aggregate_dn) {
434 1 : data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
435 : }
436 :
437 1 : if (data->aggregate_dn && ldb_dn_compare(data->aggregate_dn, msg->dn) == 0) {
438 : /*
439 : * If we have the DN for the object with common name = Aggregate and
440 : * the request is for this DN then let's do the following:
441 : * 1) search the object which changedUSN correspond to the one of the loaded
442 : * schema.
443 : * 2) Get the whenChanged attribute
444 : * 3) Generate the modifyTimestamp out of the whenChanged attribute
445 : */
446 0 : const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
447 0 : char *value = ldb_timestring(msg, schema->ts_last_change);
448 :
449 0 : if (value == NULL) {
450 0 : return ldb_oom(ldb_module_get_ctx(module));
451 : }
452 :
453 0 : return ldb_msg_add_string(msg, "modifyTimeStamp", value);
454 : }
455 1 : return ldb_msg_copy_attr(msg, "whenChanged", "modifyTimeStamp");
456 : }
457 :
458 : /*
459 : construct a subSchemaSubEntry
460 : */
461 2 : static int construct_subschema_subentry(struct ldb_module *module,
462 : struct ldb_message *msg, enum ldb_scope scope,
463 : struct ldb_request *parent)
464 : {
465 2 : struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
466 : char *subSchemaSubEntry;
467 :
468 : /* We may be being called before the init function has finished */
469 2 : if (!data) {
470 0 : return LDB_SUCCESS;
471 : }
472 :
473 : /* Try and set this value up, if possible. Don't worry if it
474 : * fails, we may not have the DB set up yet, and it's not
475 : * really vital anyway */
476 2 : if (!data->aggregate_dn) {
477 1 : struct ldb_context *ldb = ldb_module_get_ctx(module);
478 1 : data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
479 : }
480 :
481 2 : if (data->aggregate_dn) {
482 2 : subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
483 2 : return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
484 : }
485 0 : return LDB_SUCCESS;
486 : }
487 :
488 :
489 25566 : static int construct_msds_isrodc_with_dn(struct ldb_module *module,
490 : struct ldb_message *msg,
491 : struct ldb_message_element *object_category)
492 : {
493 : struct ldb_context *ldb;
494 : struct ldb_dn *dn;
495 : const struct ldb_val *val;
496 :
497 25566 : ldb = ldb_module_get_ctx(module);
498 25566 : if (!ldb) {
499 0 : DEBUG(4, (__location__ ": Failed to get ldb \n"));
500 0 : return LDB_ERR_OPERATIONS_ERROR;
501 : }
502 :
503 25566 : dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
504 25566 : if (!dn) {
505 0 : DEBUG(4, (__location__ ": Failed to create dn from %s \n",
506 : (const char *)object_category->values[0].data));
507 0 : return ldb_operr(ldb);
508 : }
509 :
510 25566 : val = ldb_dn_get_rdn_val(dn);
511 25566 : if (!val) {
512 0 : DEBUG(4, (__location__ ": Failed to get rdn val from %s \n",
513 : ldb_dn_get_linearized(dn)));
514 0 : return ldb_operr(ldb);
515 : }
516 :
517 25566 : if (strequal((const char *)val->data, "NTDS-DSA")) {
518 25210 : ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
519 : } else {
520 356 : ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
521 : }
522 25566 : return LDB_SUCCESS;
523 : }
524 :
525 24 : static int construct_msds_isrodc_with_server_dn(struct ldb_module *module,
526 : struct ldb_message *msg,
527 : struct ldb_dn *dn,
528 : struct ldb_request *parent)
529 : {
530 : struct ldb_dn *server_dn;
531 24 : const char *attr_obj_cat[] = { "objectCategory", NULL };
532 : struct ldb_result *res;
533 : struct ldb_message_element *object_category;
534 : int ret;
535 :
536 24 : server_dn = ldb_dn_copy(msg, dn);
537 24 : if (!ldb_dn_add_child_fmt(server_dn, "CN=NTDS Settings")) {
538 0 : DEBUG(4, (__location__ ": Failed to add child to %s \n",
539 : ldb_dn_get_linearized(server_dn)));
540 0 : return ldb_operr(ldb_module_get_ctx(module));
541 : }
542 :
543 24 : ret = dsdb_module_search_dn(module, msg, &res, server_dn, attr_obj_cat,
544 : DSDB_FLAG_NEXT_MODULE, parent);
545 24 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
546 4 : DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
547 : ldb_dn_get_linearized(server_dn)));
548 4 : return LDB_SUCCESS;
549 20 : } else if (ret != LDB_SUCCESS) {
550 0 : return ret;
551 : }
552 :
553 20 : object_category = ldb_msg_find_element(res->msgs[0], "objectCategory");
554 20 : if (!object_category) {
555 0 : DEBUG(4,(__location__ ": Can't find objectCategory for %s \n",
556 : ldb_dn_get_linearized(res->msgs[0]->dn)));
557 0 : return LDB_SUCCESS;
558 : }
559 20 : return construct_msds_isrodc_with_dn(module, msg, object_category);
560 : }
561 :
562 12 : static int construct_msds_isrodc_with_computer_dn(struct ldb_module *module,
563 : struct ldb_message *msg,
564 : struct ldb_request *parent)
565 : {
566 : int ret;
567 : struct ldb_dn *server_dn;
568 :
569 12 : ret = dsdb_module_reference_dn(module, msg, msg->dn, "serverReferenceBL",
570 : &server_dn, parent);
571 12 : if (ret == LDB_ERR_NO_SUCH_OBJECT || ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
572 : /* it's OK if we can't find serverReferenceBL attribute */
573 2 : DEBUG(4,(__location__ ": Can't get serverReferenceBL for %s \n",
574 : ldb_dn_get_linearized(msg->dn)));
575 2 : return LDB_SUCCESS;
576 10 : } else if (ret != LDB_SUCCESS) {
577 0 : return ret;
578 : }
579 :
580 10 : return construct_msds_isrodc_with_server_dn(module, msg, server_dn, parent);
581 : }
582 :
583 : /*
584 : construct msDS-isRODC attr
585 : */
586 25572 : static int construct_msds_isrodc(struct ldb_module *module,
587 : struct ldb_message *msg, enum ldb_scope scope,
588 : struct ldb_request *parent)
589 : {
590 : struct ldb_message_element * object_class;
591 : struct ldb_message_element * object_category;
592 : unsigned int i;
593 :
594 25572 : object_class = ldb_msg_find_element(msg, "objectClass");
595 25572 : if (!object_class) {
596 0 : DEBUG(4,(__location__ ": Can't get objectClass for %s \n",
597 : ldb_dn_get_linearized(msg->dn)));
598 0 : return ldb_operr(ldb_module_get_ctx(module));
599 : }
600 :
601 76726 : for (i=0; i<object_class->num_values; i++) {
602 76726 : if (strequal((const char*)object_class->values[i].data, "nTDSDSA")) {
603 : /* If TO!objectCategory equals the DN of the classSchema object for the nTDSDSA
604 : * object class, then TO!msDS-isRODC is false. Otherwise, TO!msDS-isRODC is true.
605 : */
606 25546 : object_category = ldb_msg_find_element(msg, "objectCategory");
607 25546 : if (!object_category) {
608 0 : DEBUG(4,(__location__ ": Can't get objectCategory for %s \n",
609 : ldb_dn_get_linearized(msg->dn)));
610 0 : return LDB_SUCCESS;
611 : }
612 25546 : return construct_msds_isrodc_with_dn(module, msg, object_category);
613 : }
614 51180 : if (strequal((const char*)object_class->values[i].data, "server")) {
615 : /* Let TN be the nTDSDSA object whose DN is "CN=NTDS Settings," prepended to
616 : * the DN of TO. Apply the previous rule for the "TO is an nTDSDSA object" case,
617 : * substituting TN for TO.
618 : */
619 14 : return construct_msds_isrodc_with_server_dn(module, msg, msg->dn, parent);
620 : }
621 51166 : if (strequal((const char*)object_class->values[i].data, "computer")) {
622 : /* Let TS be the server object named by TO!serverReferenceBL. Apply the previous
623 : * rule for the "TO is a server object" case, substituting TS for TO.
624 : */
625 12 : return construct_msds_isrodc_with_computer_dn(module, msg, parent);
626 : }
627 : }
628 :
629 0 : return LDB_SUCCESS;
630 : }
631 :
632 :
633 : /*
634 : construct msDS-keyVersionNumber attr
635 :
636 : TODO: Make this based on the 'win2k' DS huristics bit...
637 :
638 : */
639 272244 : static int construct_msds_keyversionnumber(struct ldb_module *module,
640 : struct ldb_message *msg,
641 : enum ldb_scope scope,
642 : struct ldb_request *parent)
643 : {
644 : uint32_t i;
645 : enum ndr_err_code ndr_err;
646 : const struct ldb_val *omd_value;
647 : struct replPropertyMetaDataBlob *omd;
648 : int ret;
649 :
650 272244 : omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
651 272244 : if (!omd_value) {
652 : /* We can't make up a key version number without meta data */
653 0 : return LDB_SUCCESS;
654 : }
655 :
656 272244 : omd = talloc(msg, struct replPropertyMetaDataBlob);
657 272244 : if (!omd) {
658 0 : ldb_module_oom(module);
659 0 : return LDB_SUCCESS;
660 : }
661 :
662 272244 : ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
663 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
664 272244 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
665 0 : DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
666 : ldb_dn_get_linearized(msg->dn)));
667 0 : return ldb_operr(ldb_module_get_ctx(module));
668 : }
669 :
670 272244 : if (omd->version != 1) {
671 0 : DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
672 : omd->version, ldb_dn_get_linearized(msg->dn)));
673 0 : talloc_free(omd);
674 0 : return LDB_SUCCESS;
675 : }
676 3621135 : for (i=0; i<omd->ctr.ctr1.count; i++) {
677 3621135 : if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
678 272244 : ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
679 : msg, msg,
680 : "msDS-KeyVersionNumber",
681 272244 : omd->ctr.ctr1.array[i].version);
682 272244 : if (ret != LDB_SUCCESS) {
683 0 : talloc_free(omd);
684 0 : return ret;
685 : }
686 272244 : break;
687 : }
688 : }
689 272244 : return LDB_SUCCESS;
690 :
691 : }
692 :
693 : #define _UF_TRUST_ACCOUNTS ( \
694 : UF_WORKSTATION_TRUST_ACCOUNT | \
695 : UF_SERVER_TRUST_ACCOUNT | \
696 : UF_INTERDOMAIN_TRUST_ACCOUNT \
697 : )
698 : #define _UF_NO_EXPIRY_ACCOUNTS ( \
699 : UF_SMARTCARD_REQUIRED | \
700 : UF_DONT_EXPIRE_PASSWD | \
701 : _UF_TRUST_ACCOUNTS \
702 : )
703 :
704 :
705 : /*
706 : * Returns the Effective-MaximumPasswordAge for a user
707 : */
708 467096 : static int64_t get_user_max_pwd_age(struct ldb_module *module,
709 : struct ldb_message *user_msg,
710 : struct ldb_request *parent,
711 : struct ldb_dn *nc_root)
712 : {
713 : int ret;
714 467096 : struct ldb_message *pso = NULL;
715 467096 : struct ldb_context *ldb = ldb_module_get_ctx(module);
716 :
717 : /* if a PSO applies to the user, use its maxPwdAge */
718 467096 : ret = get_pso_for_user(module, user_msg, parent, &pso);
719 467096 : if (ret != LDB_SUCCESS) {
720 :
721 : /* log the error, but fallback to the domain default */
722 0 : DBG_ERR("Error retrieving PSO for %s\n",
723 : ldb_dn_get_linearized(user_msg->dn));
724 : }
725 :
726 467096 : if (pso != NULL) {
727 1666 : return ldb_msg_find_attr_as_int64(pso,
728 : "msDS-MaximumPasswordAge", 0);
729 : }
730 :
731 : /* otherwise return the default domain value */
732 465430 : return samdb_search_int64(ldb, user_msg, 0, nc_root, "maxPwdAge", NULL);
733 : }
734 :
735 : /*
736 : calculate msDS-UserPasswordExpiryTimeComputed
737 : */
738 519368 : static NTTIME get_msds_user_password_expiry_time_computed(struct ldb_module *module,
739 : struct ldb_message *msg,
740 : struct ldb_request *parent,
741 : struct ldb_dn *domain_dn)
742 : {
743 : int64_t pwdLastSet, maxPwdAge;
744 : uint32_t userAccountControl;
745 : NTTIME ret;
746 :
747 519368 : userAccountControl = ldb_msg_find_attr_as_uint(msg,
748 : "userAccountControl",
749 : 0);
750 519368 : if (userAccountControl & _UF_NO_EXPIRY_ACCOUNTS) {
751 38175 : return 0x7FFFFFFFFFFFFFFFULL;
752 : }
753 :
754 481193 : pwdLastSet = ldb_msg_find_attr_as_int64(msg, "pwdLastSet", 0);
755 481193 : if (pwdLastSet == 0) {
756 14097 : return 0;
757 : }
758 :
759 467096 : if (pwdLastSet <= -1) {
760 : /*
761 : * This can't really happen...
762 : */
763 0 : return 0x7FFFFFFFFFFFFFFFULL;
764 : }
765 :
766 467096 : if (pwdLastSet >= 0x7FFFFFFFFFFFFFFFLL) {
767 : /*
768 : * Somethings wrong with the clock...
769 : */
770 0 : return 0x7FFFFFFFFFFFFFFFULL;
771 : }
772 :
773 : /*
774 : * Note that maxPwdAge is a stored as negative value.
775 : *
776 : * Possible values are in the range of:
777 : *
778 : * maxPwdAge: -864000000001
779 : * to
780 : * maxPwdAge: -9223372036854775808 (-0x8000000000000000ULL)
781 : *
782 : */
783 467096 : maxPwdAge = get_user_max_pwd_age(module, msg, parent, domain_dn);
784 467096 : if (maxPwdAge >= -864000000000) {
785 : /*
786 : * This is not really possible...
787 : */
788 0 : return 0x7FFFFFFFFFFFFFFFULL;
789 : }
790 :
791 467096 : if (maxPwdAge == -0x8000000000000000LL) {
792 0 : return 0x7FFFFFFFFFFFFFFFULL;
793 : }
794 :
795 : /*
796 : * Note we already caught maxPwdAge == -0x8000000000000000ULL
797 : * and pwdLastSet >= 0x7FFFFFFFFFFFFFFFULL above.
798 : *
799 : * Remember maxPwdAge is a negative number,
800 : * so it results in the following.
801 : *
802 : * 0x7FFFFFFFFFFFFFFEULL + 0x7FFFFFFFFFFFFFFFULL
803 : * =
804 : * 0xFFFFFFFFFFFFFFFDULL
805 : *
806 : * or to put it another way, adding two numbers less than 1<<63 can't
807 : * ever be more than 1<<64, therefore this result can't wrap.
808 : */
809 467096 : ret = (NTTIME)pwdLastSet - (NTTIME)maxPwdAge;
810 467096 : if (ret >= 0x7FFFFFFFFFFFFFFFULL) {
811 0 : return 0x7FFFFFFFFFFFFFFFULL;
812 : }
813 :
814 467096 : return ret;
815 : }
816 :
817 : /*
818 : * Returns the Effective-LockoutDuration for a user
819 : */
820 2210 : static int64_t get_user_lockout_duration(struct ldb_module *module,
821 : struct ldb_message *user_msg,
822 : struct ldb_request *parent,
823 : struct ldb_dn *nc_root)
824 : {
825 : int ret;
826 2210 : struct ldb_message *pso = NULL;
827 2210 : struct ldb_context *ldb = ldb_module_get_ctx(module);
828 :
829 : /* if a PSO applies to the user, use its lockoutDuration */
830 2210 : ret = get_pso_for_user(module, user_msg, parent, &pso);
831 2210 : if (ret != LDB_SUCCESS) {
832 :
833 : /* log the error, but fallback to the domain default */
834 0 : DBG_ERR("Error retrieving PSO for %s\n",
835 : ldb_dn_get_linearized(user_msg->dn));
836 : }
837 :
838 2210 : if (pso != NULL) {
839 321 : return ldb_msg_find_attr_as_int64(pso,
840 : "msDS-LockoutDuration", 0);
841 : }
842 :
843 : /* otherwise return the default domain value */
844 1889 : return samdb_search_int64(ldb, user_msg, 0, nc_root, "lockoutDuration",
845 : NULL);
846 : }
847 :
848 : /*
849 : construct msDS-User-Account-Control-Computed attr
850 : */
851 294444 : static int construct_msds_user_account_control_computed(struct ldb_module *module,
852 : struct ldb_message *msg, enum ldb_scope scope,
853 : struct ldb_request *parent)
854 : {
855 : uint32_t userAccountControl;
856 294444 : uint32_t msDS_User_Account_Control_Computed = 0;
857 294444 : struct ldb_context *ldb = ldb_module_get_ctx(module);
858 : NTTIME now;
859 : struct ldb_dn *nc_root;
860 : int ret;
861 :
862 294444 : ret = dsdb_find_nc_root(ldb, msg, msg->dn, &nc_root);
863 294444 : if (ret != 0) {
864 0 : ldb_asprintf_errstring(ldb,
865 : "Failed to find NC root of DN: %s: %s",
866 : ldb_dn_get_linearized(msg->dn),
867 : ldb_errstring(ldb_module_get_ctx(module)));
868 0 : return ret;
869 : }
870 294444 : if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) != 0) {
871 : /* Only calculate this on our default NC */
872 0 : return 0;
873 : }
874 : /* Test account expire time */
875 294444 : unix_to_nt_time(&now, time(NULL));
876 :
877 294444 : userAccountControl = ldb_msg_find_attr_as_uint(msg,
878 : "userAccountControl",
879 : 0);
880 294444 : if (!(userAccountControl & _UF_TRUST_ACCOUNTS)) {
881 :
882 257051 : int64_t lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
883 257051 : if (lockoutTime != 0) {
884 : int64_t lockoutDuration;
885 :
886 2210 : lockoutDuration = get_user_lockout_duration(module, msg,
887 : parent,
888 : nc_root);
889 :
890 : /* zero locks out until the administrator intervenes */
891 2210 : if (lockoutDuration >= 0) {
892 0 : msDS_User_Account_Control_Computed |= UF_LOCKOUT;
893 2210 : } else if (lockoutTime - lockoutDuration >= now) {
894 1589 : msDS_User_Account_Control_Computed |= UF_LOCKOUT;
895 : }
896 : }
897 : }
898 :
899 294444 : if (!(userAccountControl & _UF_NO_EXPIRY_ACCOUNTS)) {
900 240039 : NTTIME must_change_time
901 254053 : = get_msds_user_password_expiry_time_computed(module,
902 : msg,
903 : parent,
904 : nc_root);
905 : /* check for expired password */
906 254053 : if (must_change_time < now) {
907 12623 : msDS_User_Account_Control_Computed |= UF_PASSWORD_EXPIRED;
908 : }
909 : }
910 :
911 573902 : return samdb_msg_add_int64(ldb,
912 294444 : msg->elements, msg,
913 : "msDS-User-Account-Control-Computed",
914 : msDS_User_Account_Control_Computed);
915 : }
916 :
917 : /*
918 : construct msDS-UserPasswordExpiryTimeComputed
919 : */
920 265315 : static int construct_msds_user_password_expiry_time_computed(struct ldb_module *module,
921 : struct ldb_message *msg, enum ldb_scope scope,
922 : struct ldb_request *parent)
923 : {
924 265315 : struct ldb_context *ldb = ldb_module_get_ctx(module);
925 : struct ldb_dn *nc_root;
926 : int64_t password_expiry_time;
927 : int ret;
928 :
929 265315 : ret = dsdb_find_nc_root(ldb, msg, msg->dn, &nc_root);
930 265315 : if (ret != 0) {
931 0 : ldb_asprintf_errstring(ldb,
932 : "Failed to find NC root of DN: %s: %s",
933 : ldb_dn_get_linearized(msg->dn),
934 : ldb_errstring(ldb));
935 0 : return ret;
936 : }
937 :
938 265315 : if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) != 0) {
939 : /* Only calculate this on our default NC */
940 0 : return 0;
941 : }
942 :
943 : password_expiry_time
944 265315 : = get_msds_user_password_expiry_time_computed(module, msg,
945 : parent, nc_root);
946 :
947 265315 : return samdb_msg_add_int64(ldb,
948 265315 : msg->elements, msg,
949 : "msDS-UserPasswordExpiryTimeComputed",
950 : password_expiry_time);
951 : }
952 :
953 : /*
954 : * Checks whether the msDS-ResultantPSO attribute is supported for a given
955 : * user object. As per MS-ADTS, section 3.1.1.4.5.36 msDS-ResultantPSO.
956 : */
957 604569 : static bool pso_is_supported(struct ldb_context *ldb, struct ldb_message *msg)
958 : {
959 : int functional_level;
960 : uint32_t uac;
961 : uint32_t user_rid;
962 :
963 604569 : functional_level = dsdb_functional_level(ldb);
964 604569 : if (functional_level < DS_DOMAIN_FUNCTION_2008) {
965 60110 : return false;
966 : }
967 :
968 : /* msDS-ResultantPSO is only supported for user objects */
969 544459 : if (!ldb_match_msg_objectclass(msg, "user")) {
970 1 : return false;
971 : }
972 :
973 : /* ...and only if the ADS_UF_NORMAL_ACCOUNT bit is set */
974 544458 : uac = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
975 544458 : if (!(uac & UF_NORMAL_ACCOUNT)) {
976 17709 : return false;
977 : }
978 :
979 : /* skip it if it's the special KRBTGT default account */
980 526749 : user_rid = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
981 526749 : if (user_rid == DOMAIN_RID_KRBTGT) {
982 208211 : return false;
983 : }
984 :
985 : /* ...or if it's a special KRBTGT account for an RODC KDC */
986 318538 : if (ldb_msg_find_ldb_val(msg, "msDS-SecondaryKrbTgtNumber") != NULL) {
987 13762 : return false;
988 : }
989 :
990 304776 : return true;
991 : }
992 :
993 : /*
994 : * Returns the number of PSO objects that exist in the DB
995 : */
996 302348 : static int get_pso_count(struct ldb_module *module, TALLOC_CTX *mem_ctx,
997 : struct ldb_request *parent, int *pso_count)
998 : {
999 : static const char * const attrs[] = { NULL };
1000 : int ret;
1001 302348 : struct ldb_dn *psc_dn = NULL;
1002 302348 : struct ldb_result *res = NULL;
1003 302348 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1004 : bool psc_ok;
1005 :
1006 302348 : *pso_count = 0;
1007 302348 : psc_dn = samdb_system_container_dn(ldb, mem_ctx);
1008 302348 : if (psc_dn == NULL) {
1009 0 : return ldb_oom(ldb);
1010 : }
1011 302348 : psc_ok = ldb_dn_add_child_fmt(psc_dn, "CN=Password Settings Container");
1012 302348 : if (psc_ok == false) {
1013 0 : return ldb_oom(ldb);
1014 : }
1015 :
1016 : /* get the number of PSO children */
1017 302348 : ret = dsdb_module_search(module, mem_ctx, &res, psc_dn,
1018 : LDB_SCOPE_ONELEVEL, attrs,
1019 : DSDB_FLAG_NEXT_MODULE, parent,
1020 : "(objectClass=msDS-PasswordSettings)");
1021 :
1022 : /*
1023 : * Just ignore PSOs if the container doesn't exist. This is a weird
1024 : * corner-case where the AD DB was created from a pre-2008 base schema,
1025 : * and then the FL was manually upgraded.
1026 : */
1027 302348 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1028 0 : DBG_NOTICE("No Password Settings Container exists\n");
1029 0 : return LDB_SUCCESS;
1030 : }
1031 :
1032 302348 : if (ret != LDB_SUCCESS) {
1033 0 : return ret;
1034 : }
1035 :
1036 302348 : *pso_count = res->count;
1037 302348 : talloc_free(res);
1038 302348 : talloc_free(psc_dn);
1039 :
1040 302348 : return LDB_SUCCESS;
1041 : }
1042 :
1043 : /*
1044 : * Compares two PSO objects returned by a search, to work out the better PSO.
1045 : * The PSO with the lowest precedence is better, otherwise (if the precedence
1046 : * is equal) the PSO with the lower GUID wins.
1047 : */
1048 645 : static int pso_compare(struct ldb_message **m1, struct ldb_message **m2)
1049 : {
1050 : uint32_t prec1;
1051 : uint32_t prec2;
1052 :
1053 645 : prec1 = ldb_msg_find_attr_as_uint(*m1, "msDS-PasswordSettingsPrecedence",
1054 : 0xffffffff);
1055 645 : prec2 = ldb_msg_find_attr_as_uint(*m2, "msDS-PasswordSettingsPrecedence",
1056 : 0xffffffff);
1057 :
1058 : /* if precedence is equal, use the lowest GUID */
1059 645 : if (prec1 == prec2) {
1060 102 : struct GUID guid1 = samdb_result_guid(*m1, "objectGUID");
1061 102 : struct GUID guid2 = samdb_result_guid(*m2, "objectGUID");
1062 :
1063 102 : return ndr_guid_compare(&guid1, &guid2);
1064 : } else {
1065 543 : return prec1 - prec2;
1066 : }
1067 : }
1068 :
1069 : /*
1070 : * Search for PSO objects that apply to the object SIDs specified
1071 : */
1072 2314 : static int pso_search_by_sids(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1073 : struct ldb_request *parent,
1074 : struct dom_sid *sid_array, unsigned int num_sids,
1075 : struct ldb_result **result)
1076 : {
1077 : int ret;
1078 : int i;
1079 2314 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1080 2314 : char *sid_filter = NULL;
1081 2314 : struct ldb_dn *psc_dn = NULL;
1082 : bool psc_ok;
1083 2314 : const char *attrs[] = {
1084 : "msDS-PasswordSettingsPrecedence",
1085 : "objectGUID",
1086 : "msDS-LockoutDuration",
1087 : "msDS-MaximumPasswordAge",
1088 : NULL
1089 : };
1090 :
1091 : /* build a query for PSO objects that apply to any of the SIDs given */
1092 2314 : sid_filter = talloc_strdup(mem_ctx, "");
1093 :
1094 7766 : for (i = 0; sid_filter && i < num_sids; i++) {
1095 : struct dom_sid_buf sid_buf;
1096 :
1097 5452 : sid_filter = talloc_asprintf_append(
1098 : sid_filter,
1099 : "(msDS-PSOAppliesTo=<SID=%s>)",
1100 5452 : dom_sid_str_buf(&sid_array[i], &sid_buf));
1101 : }
1102 :
1103 2314 : if (sid_filter == NULL) {
1104 0 : return ldb_oom(ldb);
1105 : }
1106 :
1107 : /* only PSOs located in the Password Settings Container are valid */
1108 2314 : psc_dn = samdb_system_container_dn(ldb, mem_ctx);
1109 2314 : if (psc_dn == NULL) {
1110 0 : return ldb_oom(ldb);
1111 : }
1112 2314 : psc_ok = ldb_dn_add_child_fmt(psc_dn, "CN=Password Settings Container");
1113 2314 : if (psc_ok == false) {
1114 0 : return ldb_oom(ldb);
1115 : }
1116 :
1117 2314 : ret = dsdb_module_search(module, mem_ctx, result, psc_dn,
1118 : LDB_SCOPE_ONELEVEL, attrs,
1119 : DSDB_FLAG_NEXT_MODULE, parent,
1120 : "(&(objectClass=msDS-PasswordSettings)(|%s))",
1121 : sid_filter);
1122 2314 : talloc_free(sid_filter);
1123 2314 : return ret;
1124 : }
1125 :
1126 : /*
1127 : * Returns the best PSO object that applies to the object SID(s) specified
1128 : */
1129 2314 : static int pso_find_best(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1130 : struct ldb_request *parent, struct dom_sid *sid_array,
1131 : unsigned int num_sids, struct ldb_message **best_pso)
1132 : {
1133 2314 : struct ldb_result *res = NULL;
1134 : int ret;
1135 :
1136 2314 : *best_pso = NULL;
1137 :
1138 : /* find any PSOs that apply to the SIDs specified */
1139 2314 : ret = pso_search_by_sids(module, mem_ctx, parent, sid_array, num_sids,
1140 : &res);
1141 2314 : if (ret != LDB_SUCCESS) {
1142 0 : DBG_ERR("Error %d retrieving PSO for SID(s)\n", ret);
1143 0 : return ret;
1144 : }
1145 :
1146 : /* sort the list so that the best PSO is first */
1147 2314 : TYPESAFE_QSORT(res->msgs, res->count, pso_compare);
1148 :
1149 2314 : if (res->count > 0) {
1150 1537 : *best_pso = res->msgs[0];
1151 : }
1152 :
1153 2314 : return LDB_SUCCESS;
1154 : }
1155 :
1156 : /*
1157 : * Determines the Password Settings Object (PSO) that applies to the given user
1158 : */
1159 604569 : static int get_pso_for_user(struct ldb_module *module,
1160 : struct ldb_message *user_msg,
1161 : struct ldb_request *parent,
1162 : struct ldb_message **pso_msg)
1163 : {
1164 : bool pso_supported;
1165 604569 : struct dom_sid *groupSIDs = NULL;
1166 604569 : unsigned int num_groupSIDs = 0;
1167 604569 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1168 604569 : struct ldb_message *best_pso = NULL;
1169 604569 : struct ldb_dn *pso_dn = NULL;
1170 : int ret;
1171 604569 : struct ldb_message_element *el = NULL;
1172 604569 : TALLOC_CTX *tmp_ctx = NULL;
1173 604569 : int pso_count = 0;
1174 604569 : struct ldb_result *res = NULL;
1175 : static const char *attrs[] = {
1176 : "msDS-LockoutDuration",
1177 : "msDS-MaximumPasswordAge",
1178 : NULL
1179 : };
1180 :
1181 604569 : *pso_msg = NULL;
1182 :
1183 : /* first, check msDS-ResultantPSO is supported for this object */
1184 604569 : pso_supported = pso_is_supported(ldb, user_msg);
1185 :
1186 604569 : if (!pso_supported) {
1187 299793 : return LDB_SUCCESS;
1188 : }
1189 :
1190 304776 : tmp_ctx = talloc_new(user_msg);
1191 :
1192 : /*
1193 : * Several different constructed attributes try to use the PSO info. If
1194 : * we've already constructed the msDS-ResultantPSO for this user, we can
1195 : * just re-use the result, rather than calculating it from scratch again
1196 : */
1197 304776 : pso_dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, user_msg,
1198 : "msDS-ResultantPSO");
1199 :
1200 304776 : if (pso_dn != NULL) {
1201 1137 : ret = dsdb_module_search_dn(module, tmp_ctx, &res, pso_dn,
1202 : attrs, DSDB_FLAG_NEXT_MODULE,
1203 : parent);
1204 1137 : if (ret != LDB_SUCCESS) {
1205 0 : DBG_ERR("Error %d retrieving PSO %s\n", ret,
1206 : ldb_dn_get_linearized(pso_dn));
1207 0 : talloc_free(tmp_ctx);
1208 0 : return ret;
1209 : }
1210 :
1211 1137 : if (res->count == 1) {
1212 1137 : *pso_msg = res->msgs[0];
1213 1137 : return LDB_SUCCESS;
1214 : }
1215 : }
1216 :
1217 : /*
1218 : * if any PSOs apply directly to the user, they are considered first
1219 : * before we check group membership PSOs
1220 : */
1221 303639 : el = ldb_msg_find_element(user_msg, "msDS-PSOApplied");
1222 :
1223 303639 : if (el != NULL && el->num_values > 0) {
1224 1310 : struct dom_sid *user_sid = NULL;
1225 :
1226 : /* lookup the best PSO object, based on the user's SID */
1227 1310 : user_sid = samdb_result_dom_sid(tmp_ctx, user_msg, "objectSid");
1228 :
1229 1310 : ret = pso_find_best(module, tmp_ctx, parent, user_sid, 1,
1230 : &best_pso);
1231 1310 : if (ret != LDB_SUCCESS) {
1232 0 : talloc_free(tmp_ctx);
1233 0 : return ret;
1234 : }
1235 :
1236 1310 : if (best_pso != NULL) {
1237 1291 : *pso_msg = best_pso;
1238 1291 : return LDB_SUCCESS;
1239 : }
1240 : }
1241 :
1242 : /*
1243 : * If no valid PSO applies directly to the user, then try its groups.
1244 : * The group expansion is expensive, so check there are actually
1245 : * PSOs in the DB first (which is a quick search). Note in the above
1246 : * cases we could tell that a PSO applied to the user, based on info
1247 : * already retrieved by the user search.
1248 : */
1249 302348 : ret = get_pso_count(module, tmp_ctx, parent, &pso_count);
1250 302348 : if (ret != LDB_SUCCESS) {
1251 0 : DBG_ERR("Error %d determining PSOs in system\n", ret);
1252 0 : talloc_free(tmp_ctx);
1253 0 : return ret;
1254 : }
1255 :
1256 302348 : if (pso_count == 0) {
1257 301344 : talloc_free(tmp_ctx);
1258 301344 : return LDB_SUCCESS;
1259 : }
1260 :
1261 : /* Work out the SIDs of any account groups the user is a member of */
1262 1004 : ret = get_group_sids(ldb, tmp_ctx, user_msg,
1263 : "msDS-ResultantPSO", ACCOUNT_GROUPS,
1264 : &groupSIDs, &num_groupSIDs);
1265 1004 : if (ret != LDB_SUCCESS) {
1266 0 : DBG_ERR("Error %d determining group SIDs for %s\n", ret,
1267 : ldb_dn_get_linearized(user_msg->dn));
1268 0 : talloc_free(tmp_ctx);
1269 0 : return ret;
1270 : }
1271 :
1272 : /* lookup the best PSO that applies to any of these groups */
1273 1004 : ret = pso_find_best(module, tmp_ctx, parent, groupSIDs,
1274 : num_groupSIDs, &best_pso);
1275 1004 : if (ret != LDB_SUCCESS) {
1276 0 : talloc_free(tmp_ctx);
1277 0 : return ret;
1278 : }
1279 :
1280 1004 : *pso_msg = best_pso;
1281 1004 : return LDB_SUCCESS;
1282 : }
1283 :
1284 : /*
1285 : * Constructs the msDS-ResultantPSO attribute, which is the DN of the Password
1286 : * Settings Object (PSO) that applies to that user.
1287 : */
1288 135263 : static int construct_resultant_pso(struct ldb_module *module,
1289 : struct ldb_message *msg,
1290 : enum ldb_scope scope,
1291 : struct ldb_request *parent)
1292 : {
1293 135263 : struct ldb_message *pso = NULL;
1294 : int ret;
1295 :
1296 : /* work out the PSO (if any) that applies to this user */
1297 135263 : ret = get_pso_for_user(module, msg, parent, &pso);
1298 135263 : if (ret != LDB_SUCCESS) {
1299 0 : DBG_ERR("Couldn't determine PSO for %s\n",
1300 : ldb_dn_get_linearized(msg->dn));
1301 0 : return ret;
1302 : }
1303 :
1304 135263 : if (pso != NULL) {
1305 687 : DBG_INFO("%s is resultant PSO for user %s\n",
1306 : ldb_dn_get_linearized(pso->dn),
1307 : ldb_dn_get_linearized(msg->dn));
1308 687 : return ldb_msg_add_string(msg, "msDS-ResultantPSO",
1309 687 : ldb_dn_get_linearized(pso->dn));
1310 : }
1311 :
1312 : /* no PSO applies to this user */
1313 134576 : return LDB_SUCCESS;
1314 : }
1315 :
1316 : struct op_controls_flags {
1317 : bool sd;
1318 : bool bypassoperational;
1319 : };
1320 :
1321 74840860 : static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
1322 74840860 : if (controls_flags->bypassoperational && ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 ) {
1323 8 : return true;
1324 : }
1325 74840852 : return false;
1326 : }
1327 :
1328 : /*
1329 : a list of attribute names that should be substituted in the parse
1330 : tree before the search is done
1331 : */
1332 : static const struct {
1333 : const char *attr;
1334 : const char *replace;
1335 : } parse_tree_sub[] = {
1336 : { "createTimeStamp", "whenCreated" },
1337 : { "modifyTimeStamp", "whenChanged" }
1338 : };
1339 :
1340 :
1341 : struct op_attributes_replace {
1342 : const char *attr;
1343 : const char *replace;
1344 : const char * const *extra_attrs;
1345 : int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope, struct ldb_request *);
1346 : };
1347 :
1348 : /* the 'extra_attrs' required for msDS-ResultantPSO */
1349 : #define RESULTANT_PSO_COMPUTED_ATTRS \
1350 : "msDS-PSOApplied", \
1351 : "userAccountControl", \
1352 : "objectSid", \
1353 : "msDS-SecondaryKrbTgtNumber", \
1354 : "primaryGroupID"
1355 :
1356 : /*
1357 : * any other constructed attributes that want to work out the PSO also need to
1358 : * include objectClass (this gets included via 'replace' for msDS-ResultantPSO)
1359 : */
1360 : #define PSO_ATTR_DEPENDENCIES \
1361 : RESULTANT_PSO_COMPUTED_ATTRS, \
1362 : "objectClass"
1363 :
1364 : static const char *objectSid_attr[] =
1365 : {
1366 : "objectSid",
1367 : NULL
1368 : };
1369 :
1370 :
1371 : static const char *objectCategory_attr[] =
1372 : {
1373 : "objectCategory",
1374 : NULL
1375 : };
1376 :
1377 :
1378 : static const char *user_account_control_computed_attrs[] =
1379 : {
1380 : "lockoutTime",
1381 : "pwdLastSet",
1382 : PSO_ATTR_DEPENDENCIES,
1383 : NULL
1384 : };
1385 :
1386 :
1387 : static const char *user_password_expiry_time_computed_attrs[] =
1388 : {
1389 : "pwdLastSet",
1390 : PSO_ATTR_DEPENDENCIES,
1391 : NULL
1392 : };
1393 :
1394 : static const char *resultant_pso_computed_attrs[] =
1395 : {
1396 : RESULTANT_PSO_COMPUTED_ATTRS,
1397 : NULL
1398 : };
1399 :
1400 : /*
1401 : a list of attribute names that are hidden, but can be searched for
1402 : using another (non-hidden) name to produce the correct result
1403 : */
1404 : static const struct op_attributes_replace search_sub[] = {
1405 : { "createTimeStamp", "whenCreated", NULL , NULL },
1406 : { "modifyTimeStamp", "whenChanged", NULL , construct_modifyTimeStamp},
1407 : { "structuralObjectClass", "objectClass", NULL , NULL },
1408 : { "canonicalName", NULL, NULL , construct_canonical_name },
1409 : { "primaryGroupToken", "objectClass", objectSid_attr, construct_primary_group_token },
1410 : { "tokenGroups", "primaryGroupID", objectSid_attr, construct_token_groups },
1411 : { "tokenGroupsNoGCAcceptable", "primaryGroupID", objectSid_attr, construct_token_groups_no_gc},
1412 : { "tokenGroupsGlobalAndUniversal", "primaryGroupID", objectSid_attr, construct_global_universal_token_groups },
1413 : { "parentGUID", "objectGUID", NULL, construct_parent_guid },
1414 : { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
1415 : { "msDS-isRODC", "objectClass", objectCategory_attr, construct_msds_isrodc },
1416 : { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber },
1417 : { "msDS-User-Account-Control-Computed", "userAccountControl", user_account_control_computed_attrs,
1418 : construct_msds_user_account_control_computed },
1419 : { "msDS-UserPasswordExpiryTimeComputed", "userAccountControl", user_password_expiry_time_computed_attrs,
1420 : construct_msds_user_password_expiry_time_computed },
1421 : { "msDS-ResultantPSO", "objectClass", resultant_pso_computed_attrs,
1422 : construct_resultant_pso }
1423 : };
1424 :
1425 :
1426 : enum op_remove {
1427 : OPERATIONAL_REMOVE_ALWAYS, /* remove always */
1428 : OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
1429 : OPERATIONAL_SD_FLAGS, /* show if SD_FLAGS_OID set, or asked for */
1430 : OPERATIONAL_REMOVE_UNLESS_CONTROL /* remove always unless an adhoc control has been specified */
1431 : };
1432 :
1433 : /*
1434 : a list of attributes that may need to be removed from the
1435 : underlying db return
1436 :
1437 : Some of these are attributes that were once stored, but are now calculated
1438 : */
1439 : struct op_attributes_operations {
1440 : const char *attr;
1441 : enum op_remove op;
1442 : };
1443 :
1444 : static const struct op_attributes_operations operational_remove[] = {
1445 : { "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
1446 : { "msDS-KeyVersionNumber", OPERATIONAL_REMOVE_UNLESS_CONTROL },
1447 : { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
1448 : { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
1449 : #define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
1450 : { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
1451 : };
1452 :
1453 :
1454 : /*
1455 : post process a search result record. For any search_sub[] attributes that were
1456 : asked for, we need to call the appropriate copy routine to copy the result
1457 : into the message, then remove any attributes that we added to the search but
1458 : were not asked for by the user
1459 : */
1460 58292146 : static int operational_search_post_process(struct ldb_module *module,
1461 : struct ldb_message *msg,
1462 : enum ldb_scope scope,
1463 : const char * const *attrs_from_user,
1464 : const char * const *attrs_searched_for,
1465 : struct op_controls_flags* controls_flags,
1466 : struct op_attributes_operations *list,
1467 : unsigned int list_size,
1468 : struct op_attributes_replace *list_replace,
1469 : unsigned int list_replace_size,
1470 : struct ldb_request *parent)
1471 : {
1472 : struct ldb_context *ldb;
1473 58292146 : unsigned int i, a = 0;
1474 58292146 : bool constructed_attributes = false;
1475 :
1476 58292146 : ldb = ldb_module_get_ctx(module);
1477 :
1478 : /* removed any attrs that should not be shown to the user */
1479 1089450667 : for (i=0; i < list_size; i++) {
1480 1031158521 : ldb_msg_remove_attr(msg, list[i].attr);
1481 : }
1482 :
1483 59895497 : for (a=0; a < list_replace_size; a++) {
1484 1603351 : if (check_keep_control_for_attribute(controls_flags,
1485 1603351 : list_replace[a].attr)) {
1486 0 : continue;
1487 : }
1488 :
1489 : /* construct the new attribute, using either a supplied
1490 : constructor or a simple copy */
1491 1603351 : constructed_attributes = true;
1492 1603351 : if (list_replace[a].constructor != NULL) {
1493 1603349 : if (list_replace[a].constructor(module, msg, scope, parent) != LDB_SUCCESS) {
1494 0 : goto failed;
1495 : }
1496 4 : } else if (ldb_msg_copy_attr(msg,
1497 2 : list_replace[a].replace,
1498 2 : list_replace[a].attr) != LDB_SUCCESS) {
1499 0 : goto failed;
1500 : }
1501 : }
1502 :
1503 : /* Deletion of the search helper attributes are needed if:
1504 : * - we generated constructed attributes and
1505 : * - we aren't requesting all attributes
1506 : */
1507 58292146 : if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
1508 1482735 : for (i=0; i < list_replace_size; i++) {
1509 : /* remove the added search helper attributes, unless
1510 : * they were asked for by the user */
1511 2154655 : if (list_replace[i].replace != NULL &&
1512 1077311 : !ldb_attr_in_list(attrs_from_user, list_replace[i].replace)) {
1513 385093 : ldb_msg_remove_attr(msg, list_replace[i].replace);
1514 : }
1515 1077344 : if (list_replace[i].extra_attrs != NULL) {
1516 : unsigned int j;
1517 5642706 : for (j=0; list_replace[i].extra_attrs[j]; j++) {
1518 4918378 : if (!ldb_attr_in_list(attrs_from_user, list_replace[i].extra_attrs[j])) {
1519 1265947 : ldb_msg_remove_attr(msg, list_replace[i].extra_attrs[j]);
1520 : }
1521 : }
1522 : }
1523 : }
1524 : }
1525 :
1526 58292146 : return 0;
1527 :
1528 0 : failed:
1529 0 : ldb_debug_set(ldb, LDB_DEBUG_WARNING,
1530 : "operational_search_post_process failed for attribute '%s' - %s",
1531 0 : list_replace[a].attr, ldb_errstring(ldb));
1532 0 : return -1;
1533 : }
1534 :
1535 : /*
1536 : hook search operations
1537 : */
1538 :
1539 : struct operational_context {
1540 : struct ldb_module *module;
1541 : struct ldb_request *req;
1542 : enum ldb_scope scope;
1543 : const char * const *attrs;
1544 : struct op_controls_flags* controls_flags;
1545 : struct op_attributes_operations *list_operations;
1546 : unsigned int list_operations_size;
1547 : struct op_attributes_replace *attrs_to_replace;
1548 : unsigned int attrs_to_replace_size;
1549 : };
1550 :
1551 85905031 : static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
1552 : {
1553 : struct operational_context *ac;
1554 : int ret;
1555 :
1556 85905031 : ac = talloc_get_type(req->context, struct operational_context);
1557 :
1558 85905031 : if (!ares) {
1559 0 : return ldb_module_done(ac->req, NULL, NULL,
1560 : LDB_ERR_OPERATIONS_ERROR);
1561 : }
1562 85905031 : if (ares->error != LDB_SUCCESS) {
1563 828932 : return ldb_module_done(ac->req, ares->controls,
1564 : ares->response, ares->error);
1565 : }
1566 :
1567 85076099 : switch (ares->type) {
1568 58292146 : case LDB_REPLY_ENTRY:
1569 : /* for each record returned post-process to add any derived
1570 : attributes that have been asked for */
1571 58292146 : ret = operational_search_post_process(ac->module,
1572 : ares->message,
1573 : ac->scope,
1574 : ac->attrs,
1575 : req->op.search.attrs,
1576 : ac->controls_flags,
1577 : ac->list_operations,
1578 : ac->list_operations_size,
1579 : ac->attrs_to_replace,
1580 : ac->attrs_to_replace_size,
1581 : req);
1582 58292146 : if (ret != 0) {
1583 0 : return ldb_module_done(ac->req, NULL, NULL,
1584 : LDB_ERR_OPERATIONS_ERROR);
1585 : }
1586 58292146 : return ldb_module_send_entry(ac->req, ares->message, ares->controls);
1587 :
1588 2647495 : case LDB_REPLY_REFERRAL:
1589 2647495 : return ldb_module_send_referral(ac->req, ares->referral);
1590 :
1591 24136458 : case LDB_REPLY_DONE:
1592 :
1593 24136458 : return ldb_module_done(ac->req, ares->controls,
1594 : ares->response, LDB_SUCCESS);
1595 : }
1596 :
1597 0 : talloc_free(ares);
1598 0 : return LDB_SUCCESS;
1599 : }
1600 :
1601 25002679 : static struct op_attributes_operations* operation_get_op_list(TALLOC_CTX *ctx,
1602 : const char* const* attrs,
1603 : const char* const* searched_attrs,
1604 : struct op_controls_flags* controls_flags)
1605 : {
1606 25002679 : int idx = 0;
1607 : int i;
1608 25002679 : struct op_attributes_operations *list = talloc_zero_array(ctx,
1609 : struct op_attributes_operations,
1610 : ARRAY_SIZE(operational_remove) + 1);
1611 :
1612 25002679 : if (list == NULL) {
1613 0 : return NULL;
1614 : }
1615 :
1616 475050901 : for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
1617 450048222 : switch (operational_remove[i].op) {
1618 375040185 : case OPERATIONAL_REMOVE_UNASKED:
1619 375040185 : if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
1620 12260782 : continue;
1621 : }
1622 362779403 : if (ldb_attr_in_list(searched_attrs, operational_remove[i].attr)) {
1623 272787 : continue;
1624 : }
1625 362506616 : list[idx].attr = operational_remove[i].attr;
1626 362506616 : list[idx].op = OPERATIONAL_REMOVE_UNASKED;
1627 362506616 : idx++;
1628 362506616 : break;
1629 :
1630 25002679 : case OPERATIONAL_REMOVE_ALWAYS:
1631 25002679 : list[idx].attr = operational_remove[i].attr;
1632 25002679 : list[idx].op = OPERATIONAL_REMOVE_ALWAYS;
1633 25002679 : idx++;
1634 25002679 : break;
1635 :
1636 25002679 : case OPERATIONAL_REMOVE_UNLESS_CONTROL:
1637 25002679 : if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
1638 25002675 : list[idx].attr = operational_remove[i].attr;
1639 25002675 : list[idx].op = OPERATIONAL_REMOVE_UNLESS_CONTROL;
1640 25002675 : idx++;
1641 : }
1642 25002679 : break;
1643 :
1644 25002679 : case OPERATIONAL_SD_FLAGS:
1645 25002679 : if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
1646 5199892 : continue;
1647 : }
1648 19802787 : if (controls_flags->sd) {
1649 818918 : if (attrs == NULL) {
1650 20450 : continue;
1651 : }
1652 798468 : if (attrs[0] == NULL) {
1653 0 : continue;
1654 : }
1655 798468 : if (ldb_attr_in_list(attrs, "*")) {
1656 286258 : continue;
1657 : }
1658 : }
1659 19496079 : list[idx].attr = operational_remove[i].attr;
1660 19496079 : list[idx].op = OPERATIONAL_SD_FLAGS;
1661 19496079 : idx++;
1662 19496079 : break;
1663 : }
1664 : }
1665 :
1666 25002679 : return list;
1667 : }
1668 :
1669 26064623 : static int operational_search(struct ldb_module *module, struct ldb_request *req)
1670 : {
1671 : struct ldb_context *ldb;
1672 : struct operational_context *ac;
1673 : struct ldb_request *down_req;
1674 26064623 : const char **search_attrs = NULL;
1675 : unsigned int i, a;
1676 : int ret;
1677 :
1678 : /* There are no operational attributes on special DNs */
1679 26064623 : if (ldb_dn_is_special(req->op.search.base)) {
1680 1061944 : return ldb_next_request(module, req);
1681 : }
1682 :
1683 25002679 : ldb = ldb_module_get_ctx(module);
1684 :
1685 25002679 : ac = talloc(req, struct operational_context);
1686 25002679 : if (ac == NULL) {
1687 0 : return ldb_oom(ldb);
1688 : }
1689 :
1690 25002679 : ac->module = module;
1691 25002679 : ac->req = req;
1692 25002679 : ac->scope = req->op.search.scope;
1693 25002679 : ac->attrs = req->op.search.attrs;
1694 :
1695 : /* FIXME: We must copy the tree and keep the original
1696 : * unmodified. SSS */
1697 : /* replace any attributes in the parse tree that are
1698 : searchable, but are stored using a different name in the
1699 : backend */
1700 75008037 : for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
1701 50005358 : ldb_parse_tree_attr_replace(req->op.search.tree,
1702 6033404 : parse_tree_sub[i].attr,
1703 6033404 : parse_tree_sub[i].replace);
1704 : }
1705 :
1706 25002679 : ac->controls_flags = talloc(ac, struct op_controls_flags);
1707 : /* remember if the SD_FLAGS_OID was set */
1708 25002679 : ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
1709 : /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
1710 46988656 : ac->controls_flags->bypassoperational =
1711 46988656 : (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
1712 :
1713 25002679 : ac->attrs_to_replace = NULL;
1714 25002679 : ac->attrs_to_replace_size = 0;
1715 : /* in the list of attributes we are looking for, rename any
1716 : attributes to the alias for any hidden attributes that can
1717 : be fetched directly using non-hidden names.
1718 : Note that order here can affect performance, e.g. we should process
1719 : msDS-ResultantPSO before msDS-User-Account-Control-Computed (as the
1720 : latter is also dependent on the PSO information) */
1721 73237509 : for (a=0;ac->attrs && ac->attrs[a];a++) {
1722 48234830 : if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
1723 4 : continue;
1724 : }
1725 771757216 : for (i=0;i<ARRAY_SIZE(search_sub);i++) {
1726 :
1727 723522390 : if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) != 0 ) {
1728 721602596 : continue;
1729 : }
1730 :
1731 1919794 : ac->attrs_to_replace = talloc_realloc(ac,
1732 : ac->attrs_to_replace,
1733 : struct op_attributes_replace,
1734 : ac->attrs_to_replace_size + 1);
1735 :
1736 1919794 : ac->attrs_to_replace[ac->attrs_to_replace_size] = search_sub[i];
1737 1919794 : ac->attrs_to_replace_size++;
1738 1919794 : if (!search_sub[i].replace) {
1739 35 : continue;
1740 : }
1741 :
1742 1919759 : if (search_sub[i].extra_attrs && search_sub[i].extra_attrs[0]) {
1743 : unsigned int j;
1744 : const char **search_attrs2;
1745 : /* Only adds to the end of the list */
1746 5655275 : for (j = 0; search_sub[i].extra_attrs[j]; j++) {
1747 4929348 : search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
1748 : ? search_attrs
1749 : : ac->attrs,
1750 4929348 : search_sub[i].extra_attrs[j]);
1751 4929348 : if (search_attrs2 == NULL) {
1752 0 : return ldb_operr(ldb);
1753 : }
1754 : /* may be NULL, talloc_free() doesn't mind */
1755 4929348 : talloc_free(search_attrs);
1756 4929348 : search_attrs = search_attrs2;
1757 : }
1758 : }
1759 :
1760 1919759 : if (!search_attrs) {
1761 1062567 : search_attrs = ldb_attr_list_copy(req, ac->attrs);
1762 1062567 : if (search_attrs == NULL) {
1763 0 : return ldb_operr(ldb);
1764 : }
1765 : }
1766 : /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
1767 1919759 : search_attrs[a] = search_sub[i].replace;
1768 : }
1769 : }
1770 25002679 : ac->list_operations = operation_get_op_list(ac, ac->attrs,
1771 : search_attrs == NULL?req->op.search.attrs:search_attrs,
1772 : ac->controls_flags);
1773 25002679 : ac->list_operations_size = 0;
1774 25002679 : i = 0;
1775 :
1776 478996705 : while (ac->list_operations && ac->list_operations[i].attr != NULL) {
1777 432008049 : i++;
1778 : }
1779 25002679 : ac->list_operations_size = i;
1780 25002679 : ret = ldb_build_search_req_ex(&down_req, ldb, ac,
1781 : req->op.search.base,
1782 : req->op.search.scope,
1783 : req->op.search.tree,
1784 : /* use new set of attrs if any */
1785 : search_attrs == NULL?req->op.search.attrs:search_attrs,
1786 : req->controls,
1787 : ac, operational_callback,
1788 : req);
1789 25002679 : LDB_REQ_SET_LOCATION(down_req);
1790 25002679 : if (ret != LDB_SUCCESS) {
1791 0 : return ldb_operr(ldb);
1792 : }
1793 :
1794 : /* perform the search */
1795 25002679 : return ldb_next_request(module, down_req);
1796 : }
1797 :
1798 108013 : static int operational_init(struct ldb_module *ctx)
1799 : {
1800 : struct operational_data *data;
1801 : int ret;
1802 :
1803 108013 : ret = ldb_next_init(ctx);
1804 :
1805 108013 : if (ret != LDB_SUCCESS) {
1806 0 : return ret;
1807 : }
1808 :
1809 108013 : data = talloc_zero(ctx, struct operational_data);
1810 108013 : if (!data) {
1811 0 : return ldb_module_oom(ctx);
1812 : }
1813 :
1814 108013 : ldb_module_set_private(ctx, data);
1815 :
1816 108013 : return LDB_SUCCESS;
1817 : }
1818 :
1819 : static const struct ldb_module_ops ldb_operational_module_ops = {
1820 : .name = "operational",
1821 : .search = operational_search,
1822 : .init_context = operational_init
1823 : };
1824 :
1825 4310 : int ldb_operational_module_init(const char *version)
1826 : {
1827 4310 : LDB_MODULE_CHECK_VERSION(version);
1828 4310 : return ldb_register_module(&ldb_operational_module_ops);
1829 : }
|