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