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