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