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