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, struct ldb_reply *ares)
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, struct ldb_reply *ares)
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 7112 : 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 7112 : 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 7112 : if (ldb_msg_find_element(msg, "primaryGroupID") == NULL) {
168 4 : return LDB_SUCCESS;
169 : }
170 :
171 : /* Ensure it has an objectSID too */
172 7108 : account_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
173 7108 : if (account_sid == NULL) {
174 0 : return LDB_SUCCESS;
175 : }
176 :
177 7108 : status = dom_sid_split_rid(mem_ctx, account_sid, &domain_sid, NULL);
178 7108 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
179 0 : return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
180 7108 : } else if (!NT_STATUS_IS_OK(status)) {
181 0 : return LDB_ERR_OPERATIONS_ERROR;
182 : }
183 :
184 7108 : primary_group_sid = dom_sid_add_rid(mem_ctx,
185 : domain_sid,
186 : ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
187 7108 : if (!primary_group_sid) {
188 0 : return ldb_oom(ldb);
189 : }
190 :
191 : /* only return security groups */
192 7108 : 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 6086 : case TOKEN_GROUPS_NO_GC_ACCEPTABLE:
202 : case TOKEN_GROUPS:
203 6086 : filter = talloc_asprintf(mem_ctx,
204 : "(&(objectClass=group)"
205 : "(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
206 : GROUP_TYPE_SECURITY_ENABLED);
207 6086 : break;
208 :
209 : /* for RevMembGetAccountGroups, exclude built-in groups */
210 1018 : case ACCOUNT_GROUPS:
211 1018 : 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 1018 : break;
217 : }
218 :
219 7108 : if (!filter) {
220 0 : return ldb_oom(ldb);
221 : }
222 :
223 7108 : primary_group_string = dom_sid_string(mem_ctx, primary_group_sid);
224 7108 : if (!primary_group_string) {
225 0 : return ldb_oom(ldb);
226 : }
227 :
228 7108 : primary_group_dn = talloc_asprintf(mem_ctx, "<SID=%s>", primary_group_string);
229 7108 : if (!primary_group_dn) {
230 0 : return ldb_oom(ldb);
231 : }
232 :
233 7108 : primary_group_blob = data_blob_string_const(primary_group_dn);
234 :
235 7108 : account_sid_string = dom_sid_string(mem_ctx, account_sid);
236 7108 : if (!account_sid_string) {
237 0 : return ldb_oom(ldb);
238 : }
239 :
240 7108 : account_sid_dn = talloc_asprintf(mem_ctx, "<SID=%s>", account_sid_string);
241 7108 : if (!account_sid_dn) {
242 0 : return ldb_oom(ldb);
243 : }
244 :
245 7108 : account_sid_blob = data_blob_string_const(account_sid_dn);
246 :
247 7108 : 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 7108 : 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 7108 : status = dsdb_expand_nested_groups(ldb, &primary_group_blob, false, filter,
267 : mem_ctx, groupSIDs, num_groupSIDs);
268 7108 : 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 6838 : return LDB_SUCCESS;
276 : }
277 :
278 : /*
279 : construct the token groups for SAM objects from a message
280 : */
281 6094 : 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 6094 : struct ldb_context *ldb = ldb_module_get_ctx(module);
288 6094 : TALLOC_CTX *tmp_ctx = talloc_new(msg);
289 270 : uint32_t i;
290 270 : int ret;
291 6094 : struct auth_SidAttr *groupSIDs = NULL;
292 6094 : uint32_t num_groupSIDs = 0;
293 :
294 6094 : 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 6094 : ret = get_group_sids(ldb, tmp_ctx, msg, attribute_string, type,
301 : &groupSIDs, &num_groupSIDs);
302 :
303 6094 : 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 51681 : for (i=0; i < num_groupSIDs; i++) {
310 45587 : ret = samdb_msg_add_dom_sid(ldb, msg, msg, attribute_string, &groupSIDs[i].sid);
311 45587 : if (ret) {
312 0 : talloc_free(tmp_ctx);
313 0 : return ret;
314 : }
315 : }
316 :
317 5824 : return LDB_SUCCESS;
318 : }
319 :
320 6090 : static int construct_token_groups(struct ldb_module *module,
321 : struct ldb_message *msg, enum ldb_scope scope,
322 : struct ldb_request *parent, struct ldb_reply *ares)
323 : {
324 : /**
325 : * TODO: Add in a limiting domain when we start to support
326 : * trusted domains.
327 : */
328 6090 : 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, struct ldb_reply *ares)
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, struct ldb_reply *ares)
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 839296 : static int construct_parent_guid(struct ldb_module *module,
358 : struct ldb_message *msg, enum ldb_scope scope,
359 : struct ldb_request *parent, struct ldb_reply *ares)
360 : {
361 0 : struct ldb_result *res, *parent_res;
362 0 : const struct ldb_val *parent_guid;
363 839296 : const char *attrs[] = { "instanceType", NULL };
364 839296 : 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 839296 : ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
372 : DSDB_FLAG_NEXT_MODULE |
373 : DSDB_SEARCH_SHOW_RECYCLED, parent);
374 839296 : if (ret != LDB_SUCCESS) {
375 0 : return ret;
376 : }
377 :
378 839296 : instanceType = ldb_msg_find_attr_as_uint(res->msgs[0],
379 : "instanceType", 0);
380 839296 : talloc_free(res);
381 839296 : if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
382 2613 : DEBUG(4,(__location__ ": Object %s is NC\n",
383 : ldb_dn_get_linearized(msg->dn)));
384 2613 : return LDB_SUCCESS;
385 : }
386 836683 : parent_dn = ldb_dn_get_parent(msg, msg->dn);
387 :
388 836683 : 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 836683 : 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 836683 : 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 836683 : } else if (ret != LDB_SUCCESS) {
405 0 : talloc_free(parent_dn);
406 0 : return ret;
407 : }
408 836683 : talloc_free(parent_dn);
409 :
410 836683 : parent_guid = ldb_msg_find_ldb_val(parent_res->msgs[0], "objectGUID");
411 836683 : if (!parent_guid) {
412 0 : talloc_free(parent_res);
413 0 : return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
414 : }
415 :
416 836683 : v = data_blob_dup_talloc(parent_res, *parent_guid);
417 836683 : if (!v.data) {
418 0 : talloc_free(parent_res);
419 0 : return ldb_oom(ldb_module_get_ctx(module));
420 : }
421 836683 : ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
422 836683 : talloc_free(parent_res);
423 836683 : 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, struct ldb_reply *ares)
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 3 : static int construct_subschema_subentry(struct ldb_module *module,
470 : struct ldb_message *msg, enum ldb_scope scope,
471 : struct ldb_request *parent, struct ldb_reply *ares)
472 : {
473 3 : 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 3 : 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 3 : if (!data->aggregate_dn) {
485 2 : struct ldb_context *ldb = ldb_module_get_ctx(module);
486 2 : data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
487 : }
488 :
489 3 : if (data->aggregate_dn) {
490 3 : subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
491 3 : return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
492 : }
493 0 : return LDB_SUCCESS;
494 : }
495 :
496 :
497 37746 : 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 863 : struct ldb_context *ldb;
502 863 : struct ldb_dn *dn;
503 863 : const struct ldb_val *val;
504 :
505 37746 : ldb = ldb_module_get_ctx(module);
506 37746 : if (!ldb) {
507 0 : DEBUG(4, (__location__ ": Failed to get ldb \n"));
508 0 : return LDB_ERR_OPERATIONS_ERROR;
509 : }
510 :
511 37746 : dn = ldb_dn_new(msg, ldb, (const char *)object_category->values[0].data);
512 37746 : 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 37746 : val = ldb_dn_get_rdn_val(dn);
519 37746 : 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 37746 : if (strequal((const char *)val->data, "NTDS-DSA")) {
526 37388 : ldb_msg_add_string(msg, "msDS-isRODC", "FALSE");
527 : } else {
528 358 : ldb_msg_add_string(msg, "msDS-isRODC", "TRUE");
529 : }
530 36883 : 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 37752 : static int construct_msds_isrodc(struct ldb_module *module,
595 : struct ldb_message *msg, enum ldb_scope scope,
596 : struct ldb_request *parent, struct ldb_reply *ares)
597 : {
598 863 : struct ldb_message_element * object_class;
599 863 : struct ldb_message_element * object_category;
600 863 : unsigned int i;
601 :
602 37752 : object_class = ldb_msg_find_element(msg, "objectClass");
603 37752 : 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 113266 : for (i=0; i<object_class->num_values; i++) {
610 113266 : 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 37726 : object_category = ldb_msg_find_element(msg, "objectCategory");
615 37726 : 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 37726 : return construct_msds_isrodc_with_dn(module, msg, object_category);
621 : }
622 75540 : 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 75526 : 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 393949 : 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 : struct ldb_reply *ares)
652 : {
653 12981 : uint32_t i;
654 12981 : enum ndr_err_code ndr_err;
655 12981 : const struct ldb_val *omd_value;
656 12981 : struct replPropertyMetaDataBlob *omd;
657 12981 : int ret;
658 :
659 393949 : omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
660 393949 : if (!omd_value) {
661 : /* We can't make up a key version number without meta data */
662 0 : return LDB_SUCCESS;
663 : }
664 :
665 393949 : omd = talloc(msg, struct replPropertyMetaDataBlob);
666 393949 : if (!omd) {
667 0 : ldb_module_oom(module);
668 0 : return LDB_SUCCESS;
669 : }
670 :
671 393949 : ndr_err = ndr_pull_struct_blob(omd_value, omd, omd,
672 : (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
673 393949 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
674 0 : DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
675 : ldb_dn_get_linearized(msg->dn)));
676 0 : return ldb_operr(ldb_module_get_ctx(module));
677 : }
678 :
679 393949 : if (omd->version != 1) {
680 0 : DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s when trying to add msDS-KeyVersionNumber\n",
681 : omd->version, ldb_dn_get_linearized(msg->dn)));
682 0 : talloc_free(omd);
683 0 : return LDB_SUCCESS;
684 : }
685 5236568 : for (i=0; i<omd->ctr.ctr1.count; i++) {
686 5236568 : if (omd->ctr.ctr1.array[i].attid == DRSUAPI_ATTID_unicodePwd) {
687 393949 : ret = samdb_msg_add_uint(ldb_module_get_ctx(module),
688 : msg, msg,
689 : "msDS-KeyVersionNumber",
690 380968 : omd->ctr.ctr1.array[i].version);
691 393949 : if (ret != LDB_SUCCESS) {
692 0 : talloc_free(omd);
693 0 : return ret;
694 : }
695 380968 : break;
696 : }
697 : }
698 380968 : return LDB_SUCCESS;
699 :
700 : }
701 :
702 : #define _UF_TRUST_ACCOUNTS ( \
703 : UF_WORKSTATION_TRUST_ACCOUNT | \
704 : UF_SERVER_TRUST_ACCOUNT | \
705 : UF_INTERDOMAIN_TRUST_ACCOUNT \
706 : )
707 : #define _UF_NO_EXPIRY_ACCOUNTS ( \
708 : UF_SMARTCARD_REQUIRED | \
709 : UF_DONT_EXPIRE_PASSWD | \
710 : _UF_TRUST_ACCOUNTS \
711 : )
712 :
713 :
714 : /*
715 : * Returns the Effective-MaximumPasswordAge for a user
716 : */
717 671331 : static int64_t get_user_max_pwd_age(struct ldb_module *module,
718 : struct ldb_message *user_msg,
719 : struct ldb_request *parent,
720 : struct ldb_dn *nc_root)
721 : {
722 23020 : int ret;
723 671331 : struct ldb_message *pso = NULL;
724 671331 : struct ldb_context *ldb = ldb_module_get_ctx(module);
725 :
726 : /* if a PSO applies to the user, use its maxPwdAge */
727 671331 : ret = get_pso_for_user(module, user_msg, parent, &pso);
728 671331 : if (ret != LDB_SUCCESS) {
729 :
730 : /* log the error, but fallback to the domain default */
731 0 : DBG_ERR("Error retrieving PSO for %s\n",
732 : ldb_dn_get_linearized(user_msg->dn));
733 : }
734 :
735 671331 : if (pso != NULL) {
736 1667 : return ldb_msg_find_attr_as_int64(pso,
737 : "msDS-MaximumPasswordAge", 0);
738 : }
739 :
740 : /* otherwise return the default domain value */
741 669664 : return samdb_search_int64(ldb, user_msg, 0, nc_root, "maxPwdAge", NULL);
742 : }
743 :
744 : /*
745 : calculate msDS-UserPasswordExpiryTimeComputed
746 : */
747 743636 : static NTTIME get_msds_user_password_expiry_time_computed(struct ldb_module *module,
748 : struct ldb_message *msg,
749 : struct ldb_request *parent,
750 : struct ldb_dn *domain_dn)
751 : {
752 24537 : int64_t pwdLastSet, maxPwdAge;
753 24537 : uint32_t userAccountControl;
754 24537 : NTTIME ret;
755 :
756 743636 : userAccountControl = ldb_msg_find_attr_as_uint(msg,
757 : "userAccountControl",
758 : 0);
759 743636 : if (userAccountControl & _UF_NO_EXPIRY_ACCOUNTS) {
760 52884 : return INT64_MAX;
761 : }
762 :
763 689269 : pwdLastSet = ldb_msg_find_attr_as_int64(msg, "pwdLastSet", 0);
764 689269 : if (pwdLastSet == 0) {
765 17904 : return 0;
766 : }
767 :
768 671331 : if (pwdLastSet <= -1) {
769 : /*
770 : * This can't really happen...
771 : */
772 0 : return INT64_MAX;
773 : }
774 :
775 671331 : if (pwdLastSet >= INT64_MAX) {
776 : /*
777 : * Somethings wrong with the clock...
778 : */
779 0 : return INT64_MAX;
780 : }
781 :
782 : /*
783 : * Note that maxPwdAge is a stored as negative value.
784 : *
785 : * Possible values are in the range of:
786 : *
787 : * maxPwdAge: -864000000001
788 : * to
789 : * maxPwdAge: -9223372036854775808 (INT64_MIN)
790 : *
791 : */
792 671331 : maxPwdAge = get_user_max_pwd_age(module, msg, parent, domain_dn);
793 671331 : if (maxPwdAge >= -864000000000) {
794 : /*
795 : * This is not really possible...
796 : */
797 64 : return INT64_MAX;
798 : }
799 :
800 671267 : if (maxPwdAge == INT64_MIN) {
801 0 : return INT64_MAX;
802 : }
803 :
804 : /*
805 : * Note we already caught maxPwdAge == INT64_MIN
806 : * and pwdLastSet >= INT64_MAX above.
807 : *
808 : * Remember maxPwdAge is a negative number,
809 : * so it results in the following.
810 : *
811 : * 0x7FFFFFFFFFFFFFFEULL + INT64_MAX
812 : * =
813 : * 0xFFFFFFFFFFFFFFFDULL
814 : *
815 : * or to put it another way, adding two numbers less than 1<<63 can't
816 : * ever be more than 1<<64, therefore this result can't wrap.
817 : */
818 671267 : ret = (NTTIME)pwdLastSet - (NTTIME)maxPwdAge;
819 671267 : if (ret >= INT64_MAX) {
820 0 : return INT64_MAX;
821 : }
822 :
823 648247 : return ret;
824 : }
825 :
826 : /*
827 : * Returns the Effective-LockoutDuration for a user
828 : */
829 2264 : static int64_t get_user_lockout_duration(struct ldb_module *module,
830 : struct ldb_message *user_msg,
831 : struct ldb_request *parent,
832 : struct ldb_dn *nc_root)
833 : {
834 0 : int ret;
835 2264 : struct ldb_message *pso = NULL;
836 2264 : struct ldb_context *ldb = ldb_module_get_ctx(module);
837 :
838 : /* if a PSO applies to the user, use its lockoutDuration */
839 2264 : ret = get_pso_for_user(module, user_msg, parent, &pso);
840 2264 : if (ret != LDB_SUCCESS) {
841 :
842 : /* log the error, but fallback to the domain default */
843 0 : DBG_ERR("Error retrieving PSO for %s\n",
844 : ldb_dn_get_linearized(user_msg->dn));
845 : }
846 :
847 2264 : if (pso != NULL) {
848 315 : return ldb_msg_find_attr_as_int64(pso,
849 : "msDS-LockoutDuration", 0);
850 : }
851 :
852 : /* otherwise return the default domain value */
853 1949 : return samdb_search_int64(ldb, user_msg, 0, nc_root, "lockoutDuration",
854 : NULL);
855 : }
856 :
857 : /*
858 : construct msDS-User-Account-Control-Computed attr
859 : */
860 420784 : static int construct_msds_user_account_control_computed(struct ldb_module *module,
861 : struct ldb_message *msg, enum ldb_scope scope,
862 : struct ldb_request *parent, struct ldb_reply *ares)
863 : {
864 13360 : uint32_t userAccountControl;
865 420784 : uint32_t msDS_User_Account_Control_Computed = 0;
866 420784 : struct ldb_context *ldb = ldb_module_get_ctx(module);
867 13360 : NTTIME now;
868 13360 : struct ldb_dn *nc_root;
869 13360 : int ret;
870 :
871 420784 : ret = dsdb_find_nc_root(ldb, msg, msg->dn, &nc_root);
872 420784 : if (ret != 0) {
873 0 : ldb_asprintf_errstring(ldb,
874 : "Failed to find NC root of DN: %s: %s",
875 : ldb_dn_get_linearized(msg->dn),
876 : ldb_errstring(ldb_module_get_ctx(module)));
877 0 : return ret;
878 : }
879 420784 : if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) != 0) {
880 : /* Only calculate this on our default NC */
881 0 : return 0;
882 : }
883 : /* Test account expire time */
884 420784 : unix_to_nt_time(&now, time(NULL));
885 :
886 420784 : userAccountControl = ldb_msg_find_attr_as_uint(msg,
887 : "userAccountControl",
888 : 0);
889 420784 : if (!(userAccountControl & _UF_TRUST_ACCOUNTS)) {
890 :
891 366479 : int64_t lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
892 366479 : if (lockoutTime != 0) {
893 0 : int64_t lockoutDuration;
894 :
895 2264 : lockoutDuration = get_user_lockout_duration(module, msg,
896 : parent,
897 : nc_root);
898 :
899 : /* zero locks out until the administrator intervenes */
900 2264 : if (lockoutDuration >= 0) {
901 78 : msDS_User_Account_Control_Computed |= UF_LOCKOUT;
902 2186 : } else if (lockoutTime - lockoutDuration >= now) {
903 1565 : msDS_User_Account_Control_Computed |= UF_LOCKOUT;
904 : }
905 : }
906 : }
907 :
908 420784 : if (!(userAccountControl & _UF_NO_EXPIRY_ACCOUNTS)) {
909 11698 : NTTIME must_change_time
910 363335 : = get_msds_user_password_expiry_time_computed(module,
911 : msg,
912 : parent,
913 : nc_root);
914 : /* check for expired password */
915 363335 : if (must_change_time < now) {
916 16378 : msDS_User_Account_Control_Computed |= UF_PASSWORD_EXPIRED;
917 : }
918 : }
919 :
920 420784 : return samdb_msg_add_int64(ldb,
921 420784 : msg->elements, msg,
922 : "msDS-User-Account-Control-Computed",
923 : msDS_User_Account_Control_Computed);
924 : }
925 :
926 : /*
927 : construct msDS-UserPasswordExpiryTimeComputed
928 : */
929 380301 : static int construct_msds_user_password_expiry_time_computed(struct ldb_module *module,
930 : struct ldb_message *msg, enum ldb_scope scope,
931 : struct ldb_request *parent, struct ldb_reply *ares)
932 : {
933 380301 : struct ldb_context *ldb = ldb_module_get_ctx(module);
934 12839 : struct ldb_dn *nc_root;
935 12839 : int64_t password_expiry_time;
936 12839 : int ret;
937 :
938 380301 : ret = dsdb_find_nc_root(ldb, msg, msg->dn, &nc_root);
939 380301 : if (ret != 0) {
940 0 : ldb_asprintf_errstring(ldb,
941 : "Failed to find NC root of DN: %s: %s",
942 : ldb_dn_get_linearized(msg->dn),
943 : ldb_errstring(ldb));
944 0 : return ret;
945 : }
946 :
947 380301 : if (ldb_dn_compare(nc_root, ldb_get_default_basedn(ldb)) != 0) {
948 : /* Only calculate this on our default NC */
949 0 : return 0;
950 : }
951 :
952 12839 : password_expiry_time
953 380301 : = get_msds_user_password_expiry_time_computed(module, msg,
954 : parent, nc_root);
955 :
956 380301 : return samdb_msg_add_int64(ldb,
957 380301 : msg->elements, msg,
958 : "msDS-UserPasswordExpiryTimeComputed",
959 : password_expiry_time);
960 : }
961 :
962 : /*
963 : * Checks whether the msDS-ResultantPSO attribute is supported for a given
964 : * user object. As per MS-ADTS, section 3.1.1.4.5.36 msDS-ResultantPSO.
965 : */
966 867191 : static bool pso_is_supported(struct ldb_context *ldb, struct ldb_message *msg)
967 : {
968 29189 : int functional_level;
969 29189 : uint32_t uac;
970 29189 : uint32_t user_rid;
971 :
972 867191 : functional_level = dsdb_functional_level(ldb);
973 867191 : if (functional_level < DS_DOMAIN_FUNCTION_2008) {
974 68247 : return false;
975 : }
976 :
977 : /* msDS-ResultantPSO is only supported for user objects */
978 798935 : if (!ldb_match_msg_objectclass(msg, "user")) {
979 1 : return false;
980 : }
981 :
982 : /* ...and only if the ADS_UF_NORMAL_ACCOUNT bit is set */
983 798934 : uac = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
984 798934 : if (!(uac & UF_NORMAL_ACCOUNT)) {
985 23562 : return false;
986 : }
987 :
988 : /* skip it if it's the special KRBTGT default account */
989 774479 : user_rid = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
990 774479 : if (user_rid == DOMAIN_RID_KRBTGT) {
991 297523 : return false;
992 : }
993 :
994 : /* ...or if it's a special KRBTGT account for an RODC KDC */
995 464728 : if (ldb_msg_find_ldb_val(msg, "msDS-SecondaryKrbTgtNumber") != NULL) {
996 14050 : return false;
997 : }
998 :
999 434619 : return true;
1000 : }
1001 :
1002 : /*
1003 : * Returns the number of PSO objects that exist in the DB
1004 : */
1005 448254 : static int get_pso_count(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1006 : struct ldb_request *parent, int *pso_count)
1007 : {
1008 16059 : static const char * const attrs[] = { NULL };
1009 16059 : int ret;
1010 448254 : struct ldb_dn *psc_dn = NULL;
1011 448254 : struct ldb_result *res = NULL;
1012 448254 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1013 16059 : bool psc_ok;
1014 :
1015 448254 : *pso_count = 0;
1016 448254 : psc_dn = samdb_system_container_dn(ldb, mem_ctx);
1017 448254 : if (psc_dn == NULL) {
1018 0 : return ldb_oom(ldb);
1019 : }
1020 448254 : psc_ok = ldb_dn_add_child_fmt(psc_dn, "CN=Password Settings Container");
1021 448254 : if (psc_ok == false) {
1022 0 : return ldb_oom(ldb);
1023 : }
1024 :
1025 : /* get the number of PSO children */
1026 448254 : ret = dsdb_module_search(module, mem_ctx, &res, psc_dn,
1027 : LDB_SCOPE_ONELEVEL, attrs,
1028 : DSDB_FLAG_NEXT_MODULE, parent,
1029 : "(objectClass=msDS-PasswordSettings)");
1030 :
1031 : /*
1032 : * Just ignore PSOs if the container doesn't exist. This is a weird
1033 : * corner-case where the AD DB was created from a pre-2008 base schema,
1034 : * and then the FL was manually upgraded.
1035 : */
1036 448254 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1037 0 : DBG_NOTICE("No Password Settings Container exists\n");
1038 0 : return LDB_SUCCESS;
1039 : }
1040 :
1041 448254 : if (ret != LDB_SUCCESS) {
1042 0 : return ret;
1043 : }
1044 :
1045 448254 : *pso_count = res->count;
1046 448254 : talloc_free(res);
1047 448254 : talloc_free(psc_dn);
1048 :
1049 448254 : return LDB_SUCCESS;
1050 : }
1051 :
1052 : /*
1053 : * Compares two PSO objects returned by a search, to work out the better PSO.
1054 : * The PSO with the lowest precedence is better, otherwise (if the precedence
1055 : * is equal) the PSO with the lower GUID wins.
1056 : */
1057 450 : static int pso_compare(struct ldb_message **m1, struct ldb_message **m2)
1058 : {
1059 0 : uint32_t prec1;
1060 0 : uint32_t prec2;
1061 :
1062 450 : prec1 = ldb_msg_find_attr_as_uint(*m1, "msDS-PasswordSettingsPrecedence",
1063 : 0xffffffff);
1064 450 : prec2 = ldb_msg_find_attr_as_uint(*m2, "msDS-PasswordSettingsPrecedence",
1065 : 0xffffffff);
1066 :
1067 : /* if precedence is equal, use the lowest GUID */
1068 450 : if (prec1 == prec2) {
1069 114 : struct GUID guid1 = samdb_result_guid(*m1, "objectGUID");
1070 114 : struct GUID guid2 = samdb_result_guid(*m2, "objectGUID");
1071 :
1072 114 : return ndr_guid_compare(&guid1, &guid2);
1073 : } else {
1074 336 : return prec1 - prec2;
1075 : }
1076 : }
1077 :
1078 : /*
1079 : * Search for PSO objects that apply to the object SIDs specified
1080 : */
1081 2329 : static int pso_search_by_sids(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1082 : struct ldb_request *parent,
1083 : struct auth_SidAttr *sid_array, unsigned int num_sids,
1084 : struct ldb_result **result)
1085 : {
1086 0 : int ret;
1087 0 : int i;
1088 2329 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1089 2329 : char *sid_filter = NULL;
1090 2329 : struct ldb_dn *psc_dn = NULL;
1091 0 : bool psc_ok;
1092 2329 : const char *attrs[] = {
1093 : "msDS-PasswordSettingsPrecedence",
1094 : "objectGUID",
1095 : "msDS-LockoutDuration",
1096 : "msDS-MaximumPasswordAge",
1097 : NULL
1098 : };
1099 :
1100 : /* build a query for PSO objects that apply to any of the SIDs given */
1101 2329 : sid_filter = talloc_strdup(mem_ctx, "");
1102 2329 : if (sid_filter == NULL) {
1103 0 : return ldb_oom(ldb);
1104 : }
1105 :
1106 7820 : for (i = 0; sid_filter && i < num_sids; i++) {
1107 0 : struct dom_sid_buf sid_buf;
1108 :
1109 5491 : sid_filter = talloc_asprintf_append(
1110 : sid_filter,
1111 : "(msDS-PSOAppliesTo=<SID=%s>)",
1112 5491 : dom_sid_str_buf(&sid_array[i].sid, &sid_buf));
1113 5491 : if (sid_filter == NULL) {
1114 0 : return ldb_oom(ldb);
1115 : }
1116 : }
1117 :
1118 : /* only PSOs located in the Password Settings Container are valid */
1119 2329 : psc_dn = samdb_system_container_dn(ldb, mem_ctx);
1120 2329 : if (psc_dn == NULL) {
1121 0 : return ldb_oom(ldb);
1122 : }
1123 2329 : psc_ok = ldb_dn_add_child_fmt(psc_dn, "CN=Password Settings Container");
1124 2329 : if (psc_ok == false) {
1125 0 : return ldb_oom(ldb);
1126 : }
1127 :
1128 2329 : ret = dsdb_module_search(module, mem_ctx, result, psc_dn,
1129 : LDB_SCOPE_ONELEVEL, attrs,
1130 : DSDB_FLAG_NEXT_MODULE, parent,
1131 : "(&(objectClass=msDS-PasswordSettings)(|%s))",
1132 : sid_filter);
1133 2329 : talloc_free(sid_filter);
1134 2329 : return ret;
1135 : }
1136 :
1137 : /*
1138 : * Returns the best PSO object that applies to the object SID(s) specified
1139 : */
1140 2329 : static int pso_find_best(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1141 : struct ldb_request *parent, struct auth_SidAttr *sid_array,
1142 : unsigned int num_sids, struct ldb_message **best_pso)
1143 : {
1144 2329 : struct ldb_result *res = NULL;
1145 0 : int ret;
1146 :
1147 2329 : *best_pso = NULL;
1148 :
1149 : /* find any PSOs that apply to the SIDs specified */
1150 2329 : ret = pso_search_by_sids(module, mem_ctx, parent, sid_array, num_sids,
1151 : &res);
1152 2329 : if (ret != LDB_SUCCESS) {
1153 0 : DBG_ERR("Error %d retrieving PSO for SID(s)\n", ret);
1154 0 : return ret;
1155 : }
1156 :
1157 : /* sort the list so that the best PSO is first */
1158 2329 : TYPESAFE_QSORT(res->msgs, res->count, pso_compare);
1159 :
1160 2329 : if (res->count > 0) {
1161 1544 : *best_pso = res->msgs[0];
1162 : }
1163 :
1164 2329 : return LDB_SUCCESS;
1165 : }
1166 :
1167 : /*
1168 : * Determines the Password Settings Object (PSO) that applies to the given user
1169 : */
1170 867191 : static int get_pso_for_user(struct ldb_module *module,
1171 : struct ldb_message *user_msg,
1172 : struct ldb_request *parent,
1173 : struct ldb_message **pso_msg)
1174 : {
1175 29189 : bool pso_supported;
1176 867191 : struct auth_SidAttr *groupSIDs = NULL;
1177 867191 : uint32_t num_groupSIDs = 0;
1178 867191 : struct ldb_context *ldb = ldb_module_get_ctx(module);
1179 867191 : struct ldb_message *best_pso = NULL;
1180 867191 : struct ldb_dn *pso_dn = NULL;
1181 29189 : int ret;
1182 867191 : struct ldb_message_element *el = NULL;
1183 867191 : TALLOC_CTX *tmp_ctx = NULL;
1184 867191 : int pso_count = 0;
1185 867191 : struct ldb_result *res = NULL;
1186 29189 : static const char *attrs[] = {
1187 : "msDS-LockoutDuration",
1188 : "msDS-MaximumPasswordAge",
1189 : NULL
1190 : };
1191 :
1192 867191 : *pso_msg = NULL;
1193 :
1194 : /* first, check msDS-ResultantPSO is supported for this object */
1195 867191 : pso_supported = pso_is_supported(ldb, user_msg);
1196 :
1197 867191 : if (!pso_supported) {
1198 403383 : return LDB_SUCCESS;
1199 : }
1200 :
1201 450678 : tmp_ctx = talloc_new(user_msg);
1202 :
1203 : /*
1204 : * Several different constructed attributes try to use the PSO info. If
1205 : * we've already constructed the msDS-ResultantPSO for this user, we can
1206 : * just re-use the result, rather than calculating it from scratch again
1207 : */
1208 450678 : pso_dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, user_msg,
1209 : "msDS-ResultantPSO");
1210 :
1211 450678 : if (pso_dn != NULL) {
1212 1132 : ret = dsdb_module_search_dn(module, tmp_ctx, &res, pso_dn,
1213 : attrs, DSDB_FLAG_NEXT_MODULE,
1214 : parent);
1215 1132 : if (ret != LDB_SUCCESS) {
1216 0 : DBG_ERR("Error %d retrieving PSO %s\n", ret,
1217 : ldb_dn_get_linearized(pso_dn));
1218 0 : talloc_free(tmp_ctx);
1219 0 : return ret;
1220 : }
1221 :
1222 1132 : if (res->count == 1) {
1223 1132 : *pso_msg = res->msgs[0];
1224 1132 : return LDB_SUCCESS;
1225 : }
1226 : }
1227 :
1228 : /*
1229 : * if any PSOs apply directly to the user, they are considered first
1230 : * before we check group membership PSOs
1231 : */
1232 449546 : el = ldb_msg_find_element(user_msg, "msDS-PSOApplied");
1233 :
1234 449546 : if (el != NULL && el->num_values > 0) {
1235 1311 : struct auth_SidAttr *user_sid = NULL;
1236 :
1237 : /* lookup the best PSO object, based on the user's SID */
1238 1311 : user_sid = samdb_result_dom_sid_attrs(
1239 : tmp_ctx, user_msg, "objectSid",
1240 : SE_GROUP_DEFAULT_FLAGS);
1241 :
1242 1311 : ret = pso_find_best(module, tmp_ctx, parent, user_sid, 1,
1243 : &best_pso);
1244 1311 : if (ret != LDB_SUCCESS) {
1245 0 : talloc_free(tmp_ctx);
1246 0 : return ret;
1247 : }
1248 :
1249 1311 : if (best_pso != NULL) {
1250 1292 : *pso_msg = best_pso;
1251 1292 : return LDB_SUCCESS;
1252 : }
1253 : }
1254 :
1255 : /*
1256 : * If no valid PSO applies directly to the user, then try its groups.
1257 : * The group expansion is expensive, so check there are actually
1258 : * PSOs in the DB first (which is a quick search). Note in the above
1259 : * cases we could tell that a PSO applied to the user, based on info
1260 : * already retrieved by the user search.
1261 : */
1262 448254 : ret = get_pso_count(module, tmp_ctx, parent, &pso_count);
1263 448254 : if (ret != LDB_SUCCESS) {
1264 0 : DBG_ERR("Error %d determining PSOs in system\n", ret);
1265 0 : talloc_free(tmp_ctx);
1266 0 : return ret;
1267 : }
1268 :
1269 448254 : if (pso_count == 0) {
1270 447236 : talloc_free(tmp_ctx);
1271 447236 : return LDB_SUCCESS;
1272 : }
1273 :
1274 : /* Work out the SIDs of any account groups the user is a member of */
1275 1018 : ret = get_group_sids(ldb, tmp_ctx, user_msg,
1276 : "msDS-ResultantPSO", ACCOUNT_GROUPS,
1277 : &groupSIDs, &num_groupSIDs);
1278 1018 : if (ret != LDB_SUCCESS) {
1279 0 : DBG_ERR("Error %d determining group SIDs for %s\n", ret,
1280 : ldb_dn_get_linearized(user_msg->dn));
1281 0 : talloc_free(tmp_ctx);
1282 0 : return ret;
1283 : }
1284 :
1285 : /* lookup the best PSO that applies to any of these groups */
1286 1018 : ret = pso_find_best(module, tmp_ctx, parent, groupSIDs,
1287 : num_groupSIDs, &best_pso);
1288 1018 : if (ret != LDB_SUCCESS) {
1289 0 : talloc_free(tmp_ctx);
1290 0 : return ret;
1291 : }
1292 :
1293 1018 : *pso_msg = best_pso;
1294 1018 : return LDB_SUCCESS;
1295 : }
1296 :
1297 : /*
1298 : * Constructs the msDS-ResultantPSO attribute, which is the DN of the Password
1299 : * Settings Object (PSO) that applies to that user.
1300 : */
1301 193596 : static int construct_resultant_pso(struct ldb_module *module,
1302 : struct ldb_message *msg,
1303 : enum ldb_scope scope,
1304 : struct ldb_request *parent,
1305 : struct ldb_reply *ares)
1306 : {
1307 193596 : struct ldb_message *pso = NULL;
1308 6169 : int ret;
1309 :
1310 : /* work out the PSO (if any) that applies to this user */
1311 193596 : ret = get_pso_for_user(module, msg, parent, &pso);
1312 193596 : if (ret != LDB_SUCCESS) {
1313 0 : DBG_ERR("Couldn't determine PSO for %s\n",
1314 : ldb_dn_get_linearized(msg->dn));
1315 0 : return ret;
1316 : }
1317 :
1318 193596 : if (pso != NULL) {
1319 694 : DBG_INFO("%s is resultant PSO for user %s\n",
1320 : ldb_dn_get_linearized(pso->dn),
1321 : ldb_dn_get_linearized(msg->dn));
1322 694 : return ldb_msg_add_string(msg, "msDS-ResultantPSO",
1323 694 : ldb_dn_get_linearized(pso->dn));
1324 : }
1325 :
1326 : /* no PSO applies to this user */
1327 186733 : return LDB_SUCCESS;
1328 : }
1329 :
1330 : struct op_controls_flags {
1331 : bool sd;
1332 : bool bypassoperational;
1333 : };
1334 :
1335 118854026 : static bool check_keep_control_for_attribute(struct op_controls_flags* controls_flags, const char* attr) {
1336 114056036 : if (controls_flags->bypassoperational && ldb_attr_cmp(attr, "msDS-KeyVersionNumber") == 0 ) {
1337 8 : return true;
1338 : }
1339 114056028 : return false;
1340 : }
1341 :
1342 : /*
1343 : a list of attribute names that should be substituted in the parse
1344 : tree before the search is done
1345 : */
1346 : static const struct {
1347 : const char *attr;
1348 : const char *replace;
1349 : } parse_tree_sub[] = {
1350 : { "createTimeStamp", "whenCreated" },
1351 : { "modifyTimeStamp", "whenChanged" }
1352 : };
1353 :
1354 :
1355 : struct op_attributes_replace {
1356 : const char *attr;
1357 : const char *replace;
1358 : const char * const *extra_attrs;
1359 : int (*constructor)(struct ldb_module *, struct ldb_message *, enum ldb_scope, struct ldb_request *, struct ldb_reply *);
1360 : };
1361 :
1362 : /* the 'extra_attrs' required for msDS-ResultantPSO */
1363 : #define RESULTANT_PSO_COMPUTED_ATTRS \
1364 : "msDS-PSOApplied", \
1365 : "userAccountControl", \
1366 : "objectSid", \
1367 : "msDS-SecondaryKrbTgtNumber", \
1368 : "primaryGroupID"
1369 :
1370 : /*
1371 : * any other constructed attributes that want to work out the PSO also need to
1372 : * include objectClass (this gets included via 'replace' for msDS-ResultantPSO)
1373 : */
1374 : #define PSO_ATTR_DEPENDENCIES \
1375 : RESULTANT_PSO_COMPUTED_ATTRS, \
1376 : "objectClass"
1377 :
1378 : static const char *objectSid_attr[] =
1379 : {
1380 : "objectSid",
1381 : NULL
1382 : };
1383 :
1384 :
1385 : static const char *objectCategory_attr[] =
1386 : {
1387 : "objectCategory",
1388 : NULL
1389 : };
1390 :
1391 :
1392 : static const char *user_account_control_computed_attrs[] =
1393 : {
1394 : "lockoutTime",
1395 : "pwdLastSet",
1396 : PSO_ATTR_DEPENDENCIES,
1397 : NULL
1398 : };
1399 :
1400 :
1401 : static const char *user_password_expiry_time_computed_attrs[] =
1402 : {
1403 : "pwdLastSet",
1404 : PSO_ATTR_DEPENDENCIES,
1405 : NULL
1406 : };
1407 :
1408 : static const char *resultant_pso_computed_attrs[] =
1409 : {
1410 : RESULTANT_PSO_COMPUTED_ATTRS,
1411 : NULL
1412 : };
1413 :
1414 : /*
1415 : a list of attribute names that are hidden, but can be searched for
1416 : using another (non-hidden) name to produce the correct result
1417 : */
1418 : static const struct op_attributes_replace search_sub[] = {
1419 : { "createTimeStamp", "whenCreated", NULL , NULL },
1420 : { "modifyTimeStamp", "whenChanged", NULL , construct_modifyTimeStamp},
1421 : { "structuralObjectClass", "objectClass", NULL , NULL },
1422 : { "canonicalName", NULL, NULL , construct_canonical_name },
1423 : { "primaryGroupToken", "objectClass", objectSid_attr, construct_primary_group_token },
1424 : { "tokenGroups", "primaryGroupID", objectSid_attr, construct_token_groups },
1425 : { "tokenGroupsNoGCAcceptable", "primaryGroupID", objectSid_attr, construct_token_groups_no_gc},
1426 : { "tokenGroupsGlobalAndUniversal", "primaryGroupID", objectSid_attr, construct_global_universal_token_groups },
1427 : { "parentGUID", "objectGUID", NULL, construct_parent_guid },
1428 : { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry },
1429 : { "msDS-isRODC", "objectClass", objectCategory_attr, construct_msds_isrodc },
1430 : { "msDS-KeyVersionNumber", "replPropertyMetaData", NULL, construct_msds_keyversionnumber },
1431 : { "msDS-User-Account-Control-Computed", "userAccountControl", user_account_control_computed_attrs,
1432 : construct_msds_user_account_control_computed },
1433 : { "msDS-UserPasswordExpiryTimeComputed", "userAccountControl", user_password_expiry_time_computed_attrs,
1434 : construct_msds_user_password_expiry_time_computed },
1435 : { "msDS-ResultantPSO", "objectClass", resultant_pso_computed_attrs,
1436 : construct_resultant_pso }
1437 : };
1438 :
1439 :
1440 : enum op_remove {
1441 : OPERATIONAL_REMOVE_ALWAYS, /* remove always */
1442 : OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
1443 : OPERATIONAL_SD_FLAGS, /* show if SD_FLAGS_OID set, or asked for */
1444 : OPERATIONAL_REMOVE_UNLESS_CONTROL /* remove always unless an ad hoc control has been specified */
1445 : };
1446 :
1447 : /*
1448 : a list of attributes that may need to be removed from the
1449 : underlying db return
1450 :
1451 : Some of these are attributes that were once stored, but are now calculated
1452 : */
1453 : struct op_attributes_operations {
1454 : const char *attr;
1455 : enum op_remove op;
1456 : };
1457 :
1458 : static const struct op_attributes_operations operational_remove[] = {
1459 : { "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
1460 : { "msDS-KeyVersionNumber", OPERATIONAL_REMOVE_UNLESS_CONTROL },
1461 : { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
1462 : { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
1463 : #define _SEP ,OPERATIONAL_REMOVE_UNASKED},{
1464 : { DSDB_SECRET_ATTRIBUTES_EX(_SEP), OPERATIONAL_REMOVE_UNASKED }
1465 : };
1466 :
1467 :
1468 : /*
1469 : post process a search result record. For any search_sub[] attributes that were
1470 : asked for, we need to call the appropriate copy routine to copy the result
1471 : into the message, then remove any attributes that we added to the search but
1472 : were not asked for by the user
1473 : */
1474 86944057 : static int operational_search_post_process(struct ldb_module *module,
1475 : struct ldb_message *msg,
1476 : enum ldb_scope scope,
1477 : const char * const *attrs_from_user,
1478 : const char * const *attrs_searched_for,
1479 : struct op_controls_flags* controls_flags,
1480 : struct op_attributes_operations *list,
1481 : unsigned int list_size,
1482 : struct op_attributes_replace *list_replace,
1483 : unsigned int list_replace_size,
1484 : struct ldb_request *parent,
1485 : struct ldb_reply *ares)
1486 : {
1487 2572405 : struct ldb_context *ldb;
1488 86944057 : unsigned int i, a = 0;
1489 86944057 : bool constructed_attributes = false;
1490 :
1491 86944057 : ldb = ldb_module_get_ctx(module);
1492 :
1493 : /* removed any attrs that should not be shown to the user */
1494 1628495524 : for (i=0; i < list_size; i++) {
1495 1538979062 : ldb_msg_remove_attr(msg, list[i].attr);
1496 : }
1497 :
1498 89215885 : for (a=0; a < list_replace_size; a++) {
1499 2318310 : if (check_keep_control_for_attribute(controls_flags,
1500 2271828 : list_replace[a].attr)) {
1501 0 : continue;
1502 : }
1503 :
1504 : /* construct the new attribute, using either a supplied
1505 : constructor or a simple copy */
1506 2271828 : constructed_attributes = true;
1507 2271828 : if (list_replace[a].constructor != NULL) {
1508 2271826 : if (list_replace[a].constructor(module, msg, scope, parent, ares) != LDB_SUCCESS) {
1509 0 : goto failed;
1510 : }
1511 2 : } else if (ldb_msg_copy_attr(msg,
1512 2 : list_replace[a].replace,
1513 2 : list_replace[a].attr) != LDB_SUCCESS) {
1514 0 : goto failed;
1515 : }
1516 : }
1517 :
1518 : /* Deletion of the search helper attributes are needed if:
1519 : * - we generated constructed attributes and
1520 : * - we aren't requesting all attributes
1521 : */
1522 86944057 : if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
1523 2207949 : for (i=0; i < list_replace_size; i++) {
1524 : /* remove the added search helper attributes, unless
1525 : * they were asked for by the user */
1526 3171890 : if (list_replace[i].replace != NULL &&
1527 1585928 : !ldb_attr_in_list(attrs_from_user, list_replace[i].replace)) {
1528 590495 : ldb_msg_remove_attr(msg, list_replace[i].replace);
1529 : }
1530 1585962 : if (list_replace[i].extra_attrs != NULL) {
1531 : unsigned int j;
1532 8078770 : for (j=0; list_replace[i].extra_attrs[j]; j++) {
1533 7040224 : if (!ldb_attr_in_list(attrs_from_user, list_replace[i].extra_attrs[j])) {
1534 1799223 : ldb_msg_remove_attr(msg, list_replace[i].extra_attrs[j]);
1535 : }
1536 : }
1537 : }
1538 : }
1539 : }
1540 :
1541 84371652 : return 0;
1542 :
1543 0 : failed:
1544 0 : ldb_debug_set(ldb, LDB_DEBUG_WARNING,
1545 : "operational_search_post_process failed for attribute '%s' - %s",
1546 0 : list_replace[a].attr, ldb_errstring(ldb));
1547 0 : return -1;
1548 : }
1549 :
1550 : /*
1551 : hook search operations
1552 : */
1553 :
1554 : struct operational_context {
1555 : struct ldb_module *module;
1556 : struct ldb_request *req;
1557 : enum ldb_scope scope;
1558 : const char * const *attrs;
1559 : struct ldb_parse_tree *tree;
1560 : struct op_controls_flags* controls_flags;
1561 : struct op_attributes_operations *list_operations;
1562 : unsigned int list_operations_size;
1563 : struct op_attributes_replace *attrs_to_replace;
1564 : unsigned int attrs_to_replace_size;
1565 : };
1566 :
1567 132086222 : static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
1568 : {
1569 4905247 : struct operational_context *ac;
1570 4905247 : int ret;
1571 :
1572 132086222 : ac = talloc_get_type(req->context, struct operational_context);
1573 :
1574 132086222 : if (!ares) {
1575 0 : return ldb_module_done(ac->req, NULL, NULL,
1576 : LDB_ERR_OPERATIONS_ERROR);
1577 : }
1578 132086222 : if (ares->error != LDB_SUCCESS) {
1579 2172136 : return ldb_module_done(ac->req, ares->controls,
1580 : ares->response, ares->error);
1581 : }
1582 :
1583 129914086 : switch (ares->type) {
1584 86944057 : case LDB_REPLY_ENTRY:
1585 : /* for each record returned post-process to add any derived
1586 : attributes that have been asked for */
1587 86944057 : ret = operational_search_post_process(ac->module,
1588 : ares->message,
1589 : ac->scope,
1590 : ac->attrs,
1591 : req->op.search.attrs,
1592 : ac->controls_flags,
1593 : ac->list_operations,
1594 : ac->list_operations_size,
1595 : ac->attrs_to_replace,
1596 : ac->attrs_to_replace_size,
1597 : req,
1598 : ares);
1599 86944057 : if (ret != 0) {
1600 0 : return ldb_module_done(ac->req, NULL, NULL,
1601 : LDB_ERR_OPERATIONS_ERROR);
1602 : }
1603 86944057 : return ldb_module_send_entry(ac->req, ares->message, ares->controls);
1604 :
1605 4291307 : case LDB_REPLY_REFERRAL:
1606 4291307 : return ldb_module_send_referral(ac->req, ares->referral);
1607 :
1608 38678722 : case LDB_REPLY_DONE:
1609 :
1610 38678722 : return ldb_module_done(ac->req, ares->controls,
1611 : ares->response, LDB_SUCCESS);
1612 : }
1613 :
1614 0 : talloc_free(ares);
1615 0 : return LDB_SUCCESS;
1616 : }
1617 :
1618 40904306 : static struct op_attributes_operations* operation_get_op_list(TALLOC_CTX *ctx,
1619 : const char* const* attrs,
1620 : const char* const* searched_attrs,
1621 : struct op_controls_flags* controls_flags)
1622 : {
1623 40904306 : int idx = 0;
1624 2241541 : int i;
1625 40904306 : struct op_attributes_operations *list = talloc_zero_array(ctx,
1626 : struct op_attributes_operations,
1627 : ARRAY_SIZE(operational_remove) + 1);
1628 :
1629 40904306 : if (list == NULL) {
1630 0 : return NULL;
1631 : }
1632 :
1633 777181814 : for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
1634 736277508 : switch (operational_remove[i].op) {
1635 613564590 : case OPERATIONAL_REMOVE_UNASKED:
1636 613564590 : if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
1637 17666788 : continue;
1638 : }
1639 595897802 : if (ldb_attr_in_list(searched_attrs, operational_remove[i].attr)) {
1640 395238 : continue;
1641 : }
1642 595502564 : list[idx].attr = operational_remove[i].attr;
1643 595502564 : list[idx].op = OPERATIONAL_REMOVE_UNASKED;
1644 595502564 : idx++;
1645 595502564 : break;
1646 :
1647 40904306 : case OPERATIONAL_REMOVE_ALWAYS:
1648 40904306 : list[idx].attr = operational_remove[i].attr;
1649 40904306 : list[idx].op = OPERATIONAL_REMOVE_ALWAYS;
1650 40904306 : idx++;
1651 40904306 : break;
1652 :
1653 40904306 : case OPERATIONAL_REMOVE_UNLESS_CONTROL:
1654 81252044 : if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
1655 40904302 : list[idx].attr = operational_remove[i].attr;
1656 40904302 : list[idx].op = OPERATIONAL_REMOVE_UNLESS_CONTROL;
1657 40904302 : idx++;
1658 : }
1659 38662765 : break;
1660 :
1661 40904306 : case OPERATIONAL_SD_FLAGS:
1662 40904306 : if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
1663 7460464 : continue;
1664 : }
1665 33443842 : if (controls_flags->sd) {
1666 1282232 : if (attrs == NULL) {
1667 20450 : continue;
1668 : }
1669 1261782 : if (attrs[0] == NULL) {
1670 0 : continue;
1671 : }
1672 1261782 : if (ldb_attr_in_list(attrs, "*")) {
1673 501562 : continue;
1674 : }
1675 : }
1676 32921830 : list[idx].attr = operational_remove[i].attr;
1677 32921830 : list[idx].op = OPERATIONAL_SD_FLAGS;
1678 32921830 : idx++;
1679 32921830 : break;
1680 : }
1681 : }
1682 :
1683 38662765 : return list;
1684 : }
1685 :
1686 : struct operational_present_ctx {
1687 : const char *attr;
1688 : bool found_operational;
1689 : };
1690 :
1691 : /*
1692 : callback to determine if an operational attribute (needing
1693 : replacement) is in use at all
1694 : */
1695 192765464 : static int operational_present(struct ldb_parse_tree *tree, void *private_context)
1696 : {
1697 192765464 : struct operational_present_ctx *ctx = private_context;
1698 192765464 : switch (tree->operation) {
1699 31363600 : case LDB_OP_EQUALITY:
1700 31363600 : if (ldb_attr_cmp(tree->u.equality.attr, ctx->attr) == 0) {
1701 0 : ctx->found_operational = true;
1702 : }
1703 30122338 : break;
1704 14546 : case LDB_OP_GREATER:
1705 : case LDB_OP_LESS:
1706 : case LDB_OP_APPROX:
1707 14546 : if (ldb_attr_cmp(tree->u.comparison.attr, ctx->attr) == 0) {
1708 0 : ctx->found_operational = true;
1709 : }
1710 14522 : break;
1711 33888 : case LDB_OP_SUBSTRING:
1712 33888 : if (ldb_attr_cmp(tree->u.substring.attr, ctx->attr) == 0) {
1713 0 : ctx->found_operational = true;
1714 : }
1715 33888 : break;
1716 100108298 : case LDB_OP_PRESENT:
1717 100108298 : if (ldb_attr_cmp(tree->u.present.attr, ctx->attr) == 0) {
1718 0 : ctx->found_operational = true;
1719 : }
1720 93997906 : break;
1721 6248410 : case LDB_OP_EXTENDED:
1722 6248410 : if (tree->u.extended.attr &&
1723 6248410 : ldb_attr_cmp(tree->u.extended.attr, ctx->attr) == 0) {
1724 0 : ctx->found_operational = true;
1725 : }
1726 5984228 : break;
1727 51892276 : default:
1728 51892276 : break;
1729 : }
1730 192765464 : return LDB_SUCCESS;
1731 : }
1732 :
1733 :
1734 42581599 : static int operational_search(struct ldb_module *module, struct ldb_request *req)
1735 : {
1736 2284078 : struct ldb_context *ldb;
1737 2284078 : struct operational_context *ac;
1738 2284078 : struct ldb_request *down_req;
1739 42581599 : const char **search_attrs = NULL;
1740 2284078 : struct operational_present_ctx ctx;
1741 2284078 : unsigned int i, a;
1742 2284078 : int ret;
1743 :
1744 : /* There are no operational attributes on special DNs */
1745 42581599 : if (ldb_dn_is_special(req->op.search.base)) {
1746 1677293 : return ldb_next_request(module, req);
1747 : }
1748 :
1749 40904306 : ldb = ldb_module_get_ctx(module);
1750 :
1751 40904306 : ac = talloc(req, struct operational_context);
1752 40904306 : if (ac == NULL) {
1753 0 : return ldb_oom(ldb);
1754 : }
1755 :
1756 40904306 : ac->module = module;
1757 40904306 : ac->req = req;
1758 40904306 : ac->scope = req->op.search.scope;
1759 40904306 : ac->attrs = req->op.search.attrs;
1760 :
1761 40904306 : ctx.found_operational = false;
1762 :
1763 : /*
1764 : * find any attributes in the parse tree that are searchable,
1765 : * but are stored using a different name in the backend, so we
1766 : * only duplicate the memory when needed
1767 : */
1768 122712918 : for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
1769 81808612 : ctx.attr = parse_tree_sub[i].attr;
1770 :
1771 81808612 : ldb_parse_tree_walk(req->op.search.tree,
1772 : operational_present,
1773 : &ctx);
1774 81808612 : if (ctx.found_operational) {
1775 0 : break;
1776 : }
1777 : }
1778 :
1779 40904306 : if (ctx.found_operational) {
1780 :
1781 0 : ac->tree = ldb_parse_tree_copy_shallow(ac,
1782 0 : req->op.search.tree);
1783 :
1784 0 : if (ac->tree == NULL) {
1785 0 : return ldb_operr(ldb);
1786 : }
1787 :
1788 : /* replace any attributes in the parse tree that are
1789 : searchable, but are stored using a different name in the
1790 : backend */
1791 0 : for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
1792 0 : ldb_parse_tree_attr_replace(ac->tree,
1793 0 : parse_tree_sub[i].attr,
1794 0 : parse_tree_sub[i].replace);
1795 : }
1796 : } else {
1797 : /* Avoid allocating a copy if we do not need to */
1798 40904306 : ac->tree = req->op.search.tree;
1799 : }
1800 :
1801 40904306 : ac->controls_flags = talloc(ac, struct op_controls_flags);
1802 : /* remember if the SD_FLAGS_OID was set */
1803 40904306 : ac->controls_flags->sd = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
1804 : /* remember if the LDB_CONTROL_BYPASS_OPERATIONAL_OID */
1805 43145847 : ac->controls_flags->bypassoperational =
1806 40904306 : (ldb_request_get_control(req, LDB_CONTROL_BYPASS_OPERATIONAL_OID) != NULL);
1807 :
1808 40904306 : ac->attrs_to_replace = NULL;
1809 40904306 : ac->attrs_to_replace_size = 0;
1810 : /* in the list of attributes we are looking for, rename any
1811 : attributes to the alias for any hidden attributes that can
1812 : be fetched directly using non-hidden names.
1813 : Note that order here can affect performance, e.g. we should process
1814 : msDS-ResultantPSO before msDS-User-Account-Control-Computed (as the
1815 : latter is also dependent on the PSO information) */
1816 116582198 : for (a=0;ac->attrs && ac->attrs[a];a++) {
1817 75677892 : if (check_keep_control_for_attribute(ac->controls_flags, ac->attrs[a])) {
1818 4 : continue;
1819 : }
1820 1210846208 : for (i=0;i<ARRAY_SIZE(search_sub);i++) {
1821 :
1822 1135168320 : if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) != 0 ) {
1823 1132518424 : continue;
1824 : }
1825 :
1826 2649896 : ac->attrs_to_replace = talloc_realloc(ac,
1827 : ac->attrs_to_replace,
1828 : struct op_attributes_replace,
1829 : ac->attrs_to_replace_size + 1);
1830 :
1831 2649896 : ac->attrs_to_replace[ac->attrs_to_replace_size] = search_sub[i];
1832 2649896 : ac->attrs_to_replace_size++;
1833 2649896 : if (!search_sub[i].replace) {
1834 36 : continue;
1835 : }
1836 :
1837 2649860 : if (search_sub[i].extra_attrs && search_sub[i].extra_attrs[0]) {
1838 : unsigned int j;
1839 : const char **search_attrs2;
1840 : /* Only adds to the end of the list */
1841 8108497 : for (j = 0; search_sub[i].extra_attrs[j]; j++) {
1842 7066114 : search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
1843 : ? search_attrs
1844 : : ac->attrs,
1845 6837263 : search_sub[i].extra_attrs[j]);
1846 7066114 : if (search_attrs2 == NULL) {
1847 0 : return ldb_operr(ldb);
1848 : }
1849 : /* may be NULL, talloc_free() doesn't mind */
1850 7066114 : talloc_free(search_attrs);
1851 7066114 : search_attrs = search_attrs2;
1852 : }
1853 : }
1854 :
1855 2649860 : if (!search_attrs) {
1856 1417475 : search_attrs = ldb_attr_list_copy(req, ac->attrs);
1857 1417475 : if (search_attrs == NULL) {
1858 0 : return ldb_operr(ldb);
1859 : }
1860 : }
1861 : /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
1862 2649860 : search_attrs[a] = search_sub[i].replace;
1863 : }
1864 : }
1865 40904306 : ac->list_operations = operation_get_op_list(ac, ac->attrs,
1866 : search_attrs == NULL?req->op.search.attrs:search_attrs,
1867 : ac->controls_flags);
1868 40904306 : ac->list_operations_size = 0;
1869 40904306 : i = 0;
1870 :
1871 751137308 : while (ac->list_operations && ac->list_operations[i].attr != NULL) {
1872 710233002 : i++;
1873 : }
1874 40904306 : ac->list_operations_size = i;
1875 40904306 : ret = ldb_build_search_req_ex(&down_req, ldb, ac,
1876 : req->op.search.base,
1877 : req->op.search.scope,
1878 : ac->tree,
1879 : /* use new set of attrs if any */
1880 : search_attrs == NULL?req->op.search.attrs:search_attrs,
1881 : req->controls,
1882 : ac, operational_callback,
1883 : req);
1884 40904306 : LDB_REQ_SET_LOCATION(down_req);
1885 40904306 : if (ret != LDB_SUCCESS) {
1886 0 : return ldb_operr(ldb);
1887 : }
1888 :
1889 : /* perform the search */
1890 40904306 : return ldb_next_request(module, down_req);
1891 : }
1892 :
1893 179911 : static int operational_init(struct ldb_module *ctx)
1894 : {
1895 5986 : struct operational_data *data;
1896 5986 : int ret;
1897 :
1898 179911 : ret = ldb_next_init(ctx);
1899 :
1900 179911 : if (ret != LDB_SUCCESS) {
1901 0 : return ret;
1902 : }
1903 :
1904 179911 : data = talloc_zero(ctx, struct operational_data);
1905 179911 : if (!data) {
1906 0 : return ldb_module_oom(ctx);
1907 : }
1908 :
1909 179911 : ldb_module_set_private(ctx, data);
1910 :
1911 179911 : return LDB_SUCCESS;
1912 : }
1913 :
1914 : static const struct ldb_module_ops ldb_operational_module_ops = {
1915 : .name = "operational",
1916 : .search = operational_search,
1917 : .init_context = operational_init
1918 : };
1919 :
1920 5950 : int ldb_operational_module_init(const char *version)
1921 : {
1922 5950 : LDB_MODULE_CHECK_VERSION(version);
1923 5950 : return ldb_register_module(&ldb_operational_module_ops);
1924 : }
|