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