s3-winbindd: Always map the LDAP error code to an NTSTATUS
[Samba.git] / source3 / winbindd / winbindd_ads.c
blob4a9bd3577db4e9f7461f4a45b14f028fdd7b20b8
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind ADS backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
8 Copyright (C) Gerald (Jerry) Carter 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "winbindd.h"
26 #include "../librpc/gen_ndr/cli_netlogon.h"
28 #ifdef HAVE_ADS
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 extern struct winbindd_methods reconnect_methods;
36 return our ads connections structure for a domain. We keep the connection
37 open to make things faster
39 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
41 ADS_STRUCT *ads;
42 ADS_STATUS status;
43 fstring dc_name;
44 struct sockaddr_storage dc_ss;
46 DEBUG(10,("ads_cached_connection\n"));
48 if (domain->private_data) {
50 time_t expire;
51 time_t now = time(NULL);
53 /* check for a valid structure */
54 ads = (ADS_STRUCT *)domain->private_data;
56 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
58 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
59 (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
61 if ( ads->config.realm && (expire > now)) {
62 return ads;
63 } else {
64 /* we own this ADS_STRUCT so make sure it goes away */
65 DEBUG(7,("Deleting expired krb5 credential cache\n"));
66 ads->is_mine = True;
67 ads_destroy( &ads );
68 ads_kdestroy("MEMORY:winbind_ccache");
69 domain->private_data = NULL;
73 /* we don't want this to affect the users ccache */
74 setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
76 ads = ads_init(domain->alt_name, domain->name, NULL);
77 if (!ads) {
78 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
79 return NULL;
82 /* the machine acct password might have change - fetch it every time */
84 SAFE_FREE(ads->auth.password);
85 SAFE_FREE(ads->auth.realm);
87 if ( IS_DC ) {
89 if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, NULL, NULL ) ) {
90 ads_destroy( &ads );
91 return NULL;
93 ads->auth.realm = SMB_STRDUP( ads->server.realm );
94 strupper_m( ads->auth.realm );
96 else {
97 struct winbindd_domain *our_domain = domain;
99 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
101 /* always give preference to the alt_name in our
102 primary domain if possible */
104 if ( !domain->primary )
105 our_domain = find_our_domain();
107 if ( our_domain->alt_name[0] != '\0' ) {
108 ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
109 strupper_m( ads->auth.realm );
111 else
112 ads->auth.realm = SMB_STRDUP( lp_realm() );
115 ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
117 /* Setup the server affinity cache. We don't reaally care
118 about the name. Just setup affinity and the KRB5_CONFIG
119 file. */
121 get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ss );
123 status = ads_connect(ads);
124 if (!ADS_ERR_OK(status) || !ads->config.realm) {
125 DEBUG(1,("ads_connect for domain %s failed: %s\n",
126 domain->name, ads_errstr(status)));
127 ads_destroy(&ads);
129 /* if we get ECONNREFUSED then it might be a NT4
130 server, fall back to MSRPC */
131 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
132 status.err.rc == ECONNREFUSED) {
133 /* 'reconnect_methods' is the MS-RPC backend. */
134 DEBUG(1,("Trying MSRPC methods\n"));
135 domain->backend = &reconnect_methods;
137 return NULL;
140 /* set the flag that says we don't own the memory even
141 though we do so that ads_destroy() won't destroy the
142 structure we pass back by reference */
144 ads->is_mine = False;
146 domain->private_data = (void *)ads;
147 return ads;
151 /* Query display info for a realm. This is the basic user list fn */
152 static NTSTATUS query_user_list(struct winbindd_domain *domain,
153 TALLOC_CTX *mem_ctx,
154 uint32 *num_entries,
155 struct wbint_userinfo **info)
157 ADS_STRUCT *ads = NULL;
158 const char *attrs[] = { "*", NULL };
159 int i, count;
160 ADS_STATUS rc;
161 LDAPMessage *res = NULL;
162 LDAPMessage *msg = NULL;
163 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
165 *num_entries = 0;
167 DEBUG(3,("ads: query_user_list\n"));
169 if ( !winbindd_can_contact_domain( domain ) ) {
170 DEBUG(10,("query_user_list: No incoming trust for domain %s\n",
171 domain->name));
172 return NT_STATUS_OK;
175 ads = ads_cached_connection(domain);
177 if (!ads) {
178 domain->last_status = NT_STATUS_SERVER_DISABLED;
179 goto done;
182 rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
183 if (!ADS_ERR_OK(rc)) {
184 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
185 status = ads_ntstatus(rc);
186 } else if (!res) {
187 DEBUG(1,("query_user_list ads_search returned NULL res\n"));
189 goto done;
192 count = ads_count_replies(ads, res);
193 if (count == 0) {
194 DEBUG(1,("query_user_list: No users found\n"));
195 goto done;
198 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct wbint_userinfo, count);
199 if (!*info) {
200 status = NT_STATUS_NO_MEMORY;
201 goto done;
204 i = 0;
206 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
207 const char *name;
208 const char *gecos = NULL;
209 const char *homedir = NULL;
210 const char *shell = NULL;
211 uint32 group;
212 uint32 atype;
213 DOM_SID user_sid;
214 gid_t primary_gid = (gid_t)-1;
216 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
217 ds_atype_map(atype) != SID_NAME_USER) {
218 DEBUG(1,("Not a user account? atype=0x%x\n", atype));
219 continue;
222 name = ads_pull_username(ads, mem_ctx, msg);
224 if ( ads_pull_sid( ads, msg, "objectSid", &user_sid ) ) {
225 status = nss_get_info_cached( domain, &user_sid, mem_ctx,
226 ads, msg, &homedir, &shell, &gecos,
227 &primary_gid );
230 if (gecos == NULL) {
231 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
234 if (!ads_pull_sid(ads, msg, "objectSid",
235 &(*info)[i].user_sid)) {
236 DEBUG(1,("No sid for %s !?\n", name));
237 continue;
239 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
240 DEBUG(1,("No primary group for %s !?\n", name));
241 continue;
244 (*info)[i].acct_name = name;
245 (*info)[i].full_name = gecos;
246 (*info)[i].homedir = homedir;
247 (*info)[i].shell = shell;
248 (*info)[i].primary_gid = primary_gid;
249 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
250 i++;
253 (*num_entries) = i;
254 status = NT_STATUS_OK;
256 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
258 done:
259 if (res)
260 ads_msgfree(ads, res);
262 return status;
265 /* list all domain groups */
266 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
267 TALLOC_CTX *mem_ctx,
268 uint32 *num_entries,
269 struct acct_info **info)
271 ADS_STRUCT *ads = NULL;
272 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
273 "name", "objectSid", NULL};
274 int i, count;
275 ADS_STATUS rc;
276 LDAPMessage *res = NULL;
277 LDAPMessage *msg = NULL;
278 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
279 const char *filter;
280 bool enum_dom_local_groups = False;
282 *num_entries = 0;
284 DEBUG(3,("ads: enum_dom_groups\n"));
286 if ( !winbindd_can_contact_domain( domain ) ) {
287 DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n",
288 domain->name));
289 return NT_STATUS_OK;
292 /* only grab domain local groups for our domain */
293 if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) {
294 enum_dom_local_groups = True;
297 /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
298 * rollup-fixes:
300 * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
301 * default value, it MUST be absent. In case of extensible matching the
302 * "dnattr" boolean defaults to FALSE and so it must be only be present
303 * when set to TRUE.
305 * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
306 * filter using bitwise matching rule then the buggy AD fails to decode
307 * the extensible match. As a workaround set it to TRUE and thereby add
308 * the dnAttributes "dn" field to cope with those older AD versions.
309 * It should not harm and won't put any additional load on the AD since
310 * none of the dn components have a bitmask-attribute.
312 * Thanks to Ralf Haferkamp for input and testing - Guenther */
314 filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))",
315 ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
316 ADS_LDAP_MATCHING_RULE_BIT_AND,
317 enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
319 if (filter == NULL) {
320 status = NT_STATUS_NO_MEMORY;
321 goto done;
324 ads = ads_cached_connection(domain);
326 if (!ads) {
327 domain->last_status = NT_STATUS_SERVER_DISABLED;
328 goto done;
331 rc = ads_search_retry(ads, &res, filter, attrs);
332 if (!ADS_ERR_OK(rc)) {
333 status = ads_ntstatus(rc);
334 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
335 goto done;
336 } else if (!res) {
337 DEBUG(1,("enum_dom_groups ads_search returned NULL res\n"));
338 goto done;
341 count = ads_count_replies(ads, res);
342 if (count == 0) {
343 DEBUG(1,("enum_dom_groups: No groups found\n"));
344 goto done;
347 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
348 if (!*info) {
349 status = NT_STATUS_NO_MEMORY;
350 goto done;
353 i = 0;
355 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
356 char *name, *gecos;
357 DOM_SID sid;
358 uint32 rid;
360 name = ads_pull_username(ads, mem_ctx, msg);
361 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
362 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
363 DEBUG(1,("No sid for %s !?\n", name));
364 continue;
367 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
368 DEBUG(1,("No rid for %s !?\n", name));
369 continue;
372 fstrcpy((*info)[i].acct_name, name);
373 fstrcpy((*info)[i].acct_desc, gecos);
374 (*info)[i].rid = rid;
375 i++;
378 (*num_entries) = i;
380 status = NT_STATUS_OK;
382 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
384 done:
385 if (res)
386 ads_msgfree(ads, res);
388 return status;
391 /* list all domain local groups */
392 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
393 TALLOC_CTX *mem_ctx,
394 uint32 *num_entries,
395 struct acct_info **info)
398 * This is a stub function only as we returned the domain
399 * local groups in enum_dom_groups() if the domain->native field
400 * was true. This is a simple performance optimization when
401 * using LDAP.
403 * if we ever need to enumerate domain local groups separately,
404 * then this optimization in enum_dom_groups() will need
405 * to be split out
407 *num_entries = 0;
409 return NT_STATUS_OK;
412 /* convert a single name to a sid in a domain - use rpc methods */
413 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
414 TALLOC_CTX *mem_ctx,
415 const char *domain_name,
416 const char *name,
417 uint32_t flags,
418 DOM_SID *sid,
419 enum lsa_SidType *type)
421 return reconnect_methods.name_to_sid(domain, mem_ctx,
422 domain_name, name, flags,
423 sid, type);
426 /* convert a domain SID to a user or group name - use rpc methods */
427 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
428 TALLOC_CTX *mem_ctx,
429 const DOM_SID *sid,
430 char **domain_name,
431 char **name,
432 enum lsa_SidType *type)
434 return reconnect_methods.sid_to_name(domain, mem_ctx, sid,
435 domain_name, name, type);
438 /* convert a list of rids to names - use rpc methods */
439 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
440 TALLOC_CTX *mem_ctx,
441 const DOM_SID *sid,
442 uint32 *rids,
443 size_t num_rids,
444 char **domain_name,
445 char ***names,
446 enum lsa_SidType **types)
448 return reconnect_methods.rids_to_names(domain, mem_ctx, sid,
449 rids, num_rids,
450 domain_name, names, types);
453 /* If you are looking for "dn_lookup": Yes, it used to be here!
454 * It has gone now since it was a major speed bottleneck in
455 * lookup_groupmem (its only use). It has been replaced by
456 * an rpc lookup sids call... R.I.P. */
458 /* Lookup user information from a rid */
459 static NTSTATUS query_user(struct winbindd_domain *domain,
460 TALLOC_CTX *mem_ctx,
461 const DOM_SID *sid,
462 struct wbint_userinfo *info)
464 ADS_STRUCT *ads = NULL;
465 const char *attrs[] = { "*", NULL };
466 ADS_STATUS rc;
467 int count;
468 LDAPMessage *msg = NULL;
469 char *ldap_exp;
470 char *sidstr;
471 uint32 group_rid;
472 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
473 struct netr_SamInfo3 *user = NULL;
474 gid_t gid;
476 DEBUG(3,("ads: query_user\n"));
478 info->homedir = NULL;
479 info->shell = NULL;
480 info->primary_gid = (gid_t)-1;
482 /* try netsamlogon cache first */
484 if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL )
486 DEBUG(5,("query_user: Cache lookup succeeded for %s\n",
487 sid_string_dbg(sid)));
489 sid_compose(&info->user_sid, &domain->sid, user->base.rid);
490 sid_compose(&info->group_sid, &domain->sid, user->base.primary_gid);
492 info->acct_name = talloc_strdup(mem_ctx, user->base.account_name.string);
493 info->full_name = talloc_strdup(mem_ctx, user->base.full_name.string);
495 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL,
496 &info->homedir, &info->shell, &info->full_name,
497 &gid );
498 info->primary_gid = gid;
500 TALLOC_FREE(user);
502 return NT_STATUS_OK;
505 if ( !winbindd_can_contact_domain(domain)) {
506 DEBUG(8,("query_user: No incoming trust from domain %s\n",
507 domain->name));
509 /* We still need to generate some basic information
510 about the user even if we cannot contact the
511 domain. Most of this stuff we can deduce. */
513 sid_copy( &info->user_sid, sid );
515 /* Assume "Domain Users" for the primary group */
517 sid_compose(&info->group_sid, &domain->sid, DOMAIN_GROUP_RID_USERS );
519 /* Try to fill in what the nss_info backend can do */
521 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL,
522 &info->homedir, &info->shell, &info->full_name,
523 &gid);
524 info->primary_gid = gid;
526 status = NT_STATUS_OK;
527 goto done;
530 /* no cache...do the query */
532 if ( (ads = ads_cached_connection(domain)) == NULL ) {
533 domain->last_status = NT_STATUS_SERVER_DISABLED;
534 goto done;
537 sidstr = sid_binstring(talloc_tos(), sid);
538 if (asprintf(&ldap_exp, "(objectSid=%s)", sidstr) == -1) {
539 status = NT_STATUS_NO_MEMORY;
540 goto done;
542 rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
543 SAFE_FREE(ldap_exp);
544 TALLOC_FREE(sidstr);
545 if (!ADS_ERR_OK(rc)) {
546 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
547 sid_string_dbg(sid), ads_errstr(rc)));
548 return ads_ntstatus(rc);
549 } else if (!msg) {
550 DEBUG(1,("query_user(sid=%s) ads_search returned NULL res\n",
551 sid_string_dbg(sid)));
552 return NT_STATUS_INTERNAL_ERROR;
555 count = ads_count_replies(ads, msg);
556 if (count != 1) {
557 DEBUG(1,("query_user(sid=%s): Not found\n",
558 sid_string_dbg(sid)));
559 goto done;
562 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
564 nss_get_info_cached( domain, sid, mem_ctx, ads, msg,
565 &info->homedir, &info->shell, &info->full_name,
566 &gid);
567 info->primary_gid = gid;
569 if (info->full_name == NULL) {
570 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
573 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
574 DEBUG(1,("No primary group for %s !?\n",
575 sid_string_dbg(sid)));
576 goto done;
579 sid_copy(&info->user_sid, sid);
580 sid_compose(&info->group_sid, &domain->sid, group_rid);
582 status = NT_STATUS_OK;
584 DEBUG(3,("ads query_user gave %s\n", info->acct_name));
585 done:
586 if (msg)
587 ads_msgfree(ads, msg);
589 return status;
592 /* Lookup groups a user is a member of - alternate method, for when
593 tokenGroups are not available. */
594 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
595 TALLOC_CTX *mem_ctx,
596 const char *user_dn,
597 DOM_SID *primary_group,
598 size_t *p_num_groups, DOM_SID **user_sids)
600 ADS_STATUS rc;
601 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
602 int count;
603 LDAPMessage *res = NULL;
604 LDAPMessage *msg = NULL;
605 char *ldap_exp;
606 ADS_STRUCT *ads;
607 const char *group_attrs[] = {"objectSid", NULL};
608 char *escaped_dn;
609 size_t num_groups = 0;
611 DEBUG(3,("ads: lookup_usergroups_member\n"));
613 if ( !winbindd_can_contact_domain( domain ) ) {
614 DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
615 domain->name));
616 return NT_STATUS_OK;
619 ads = ads_cached_connection(domain);
621 if (!ads) {
622 domain->last_status = NT_STATUS_SERVER_DISABLED;
623 goto done;
626 if (!(escaped_dn = escape_ldap_string(talloc_tos(), user_dn))) {
627 status = NT_STATUS_NO_MEMORY;
628 goto done;
631 ldap_exp = talloc_asprintf(mem_ctx,
632 "(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))",
633 escaped_dn,
634 ADS_LDAP_MATCHING_RULE_BIT_AND,
635 GROUP_TYPE_SECURITY_ENABLED);
636 if (!ldap_exp) {
637 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
638 TALLOC_FREE(escaped_dn);
639 status = NT_STATUS_NO_MEMORY;
640 goto done;
643 TALLOC_FREE(escaped_dn);
645 rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
647 if (!ADS_ERR_OK(rc)) {
648 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
649 return ads_ntstatus(rc);
650 } else if (!res) {
651 DEBUG(1,("lookup_usergroups ads_search returned NULL res\n"));
652 return NT_STATUS_INTERNAL_ERROR;
656 count = ads_count_replies(ads, res);
658 *user_sids = NULL;
659 num_groups = 0;
661 /* always add the primary group to the sid array */
662 status = add_sid_to_array(mem_ctx, primary_group, user_sids,
663 &num_groups);
664 if (!NT_STATUS_IS_OK(status)) {
665 goto done;
668 if (count > 0) {
669 for (msg = ads_first_entry(ads, res); msg;
670 msg = ads_next_entry(ads, msg)) {
671 DOM_SID group_sid;
673 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
674 DEBUG(1,("No sid for this group ?!?\n"));
675 continue;
678 /* ignore Builtin groups from ADS - Guenther */
679 if (sid_check_is_in_builtin(&group_sid)) {
680 continue;
683 status = add_sid_to_array(mem_ctx, &group_sid,
684 user_sids, &num_groups);
685 if (!NT_STATUS_IS_OK(status)) {
686 goto done;
692 *p_num_groups = num_groups;
693 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
695 DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
696 done:
697 if (res)
698 ads_msgfree(ads, res);
700 return status;
703 /* Lookup groups a user is a member of - alternate method, for when
704 tokenGroups are not available. */
705 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
706 TALLOC_CTX *mem_ctx,
707 const char *user_dn,
708 DOM_SID *primary_group,
709 size_t *p_num_groups,
710 DOM_SID **user_sids)
712 ADS_STATUS rc;
713 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
714 ADS_STRUCT *ads;
715 const char *attrs[] = {"memberOf", NULL};
716 size_t num_groups = 0;
717 DOM_SID *group_sids = NULL;
718 int i;
719 char **strings = NULL;
720 size_t num_strings = 0, num_sids = 0;
723 DEBUG(3,("ads: lookup_usergroups_memberof\n"));
725 if ( !winbindd_can_contact_domain( domain ) ) {
726 DEBUG(10,("lookup_usergroups_memberof: No incoming trust for "
727 "domain %s\n", domain->name));
728 return NT_STATUS_OK;
731 ads = ads_cached_connection(domain);
733 if (!ads) {
734 domain->last_status = NT_STATUS_SERVER_DISABLED;
735 return NT_STATUS_UNSUCCESSFUL;
738 rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
739 ADS_EXTENDED_DN_HEX_STRING,
740 &strings, &num_strings);
742 if (!ADS_ERR_OK(rc)) {
743 DEBUG(1,("lookup_usergroups_memberof ads_search "
744 "member=%s: %s\n", user_dn, ads_errstr(rc)));
745 return ads_ntstatus(rc);
748 *user_sids = NULL;
749 num_groups = 0;
751 /* always add the primary group to the sid array */
752 status = add_sid_to_array(mem_ctx, primary_group, user_sids,
753 &num_groups);
754 if (!NT_STATUS_IS_OK(status)) {
755 goto done;
758 group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1);
759 if (!group_sids) {
760 status = NT_STATUS_NO_MEMORY;
761 goto done;
764 for (i=0; i<num_strings; i++) {
765 rc = ads_get_sid_from_extended_dn(mem_ctx, strings[i],
766 ADS_EXTENDED_DN_HEX_STRING,
767 &(group_sids)[i]);
768 if (!ADS_ERR_OK(rc)) {
769 /* ignore members without SIDs */
770 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
771 NT_STATUS_NOT_FOUND)) {
772 continue;
774 else {
775 status = ads_ntstatus(rc);
776 goto done;
779 num_sids++;
782 if (i == 0) {
783 DEBUG(1,("No memberOf for this user?!?\n"));
784 status = NT_STATUS_NO_MEMORY;
785 goto done;
788 for (i=0; i<num_sids; i++) {
790 /* ignore Builtin groups from ADS - Guenther */
791 if (sid_check_is_in_builtin(&group_sids[i])) {
792 continue;
795 status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
796 &num_groups);
797 if (!NT_STATUS_IS_OK(status)) {
798 goto done;
803 *p_num_groups = num_groups;
804 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
806 DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n",
807 user_dn));
809 done:
810 TALLOC_FREE(strings);
811 TALLOC_FREE(group_sids);
813 return status;
817 /* Lookup groups a user is a member of. */
818 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
819 TALLOC_CTX *mem_ctx,
820 const DOM_SID *sid,
821 uint32 *p_num_groups, DOM_SID **user_sids)
823 ADS_STRUCT *ads = NULL;
824 const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
825 ADS_STATUS rc;
826 int count;
827 LDAPMessage *msg = NULL;
828 char *user_dn = NULL;
829 DOM_SID *sids;
830 int i;
831 DOM_SID primary_group;
832 uint32 primary_group_rid;
833 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
834 size_t num_groups = 0;
836 DEBUG(3,("ads: lookup_usergroups\n"));
837 *p_num_groups = 0;
839 status = lookup_usergroups_cached(domain, mem_ctx, sid,
840 p_num_groups, user_sids);
841 if (NT_STATUS_IS_OK(status)) {
842 return NT_STATUS_OK;
845 if ( !winbindd_can_contact_domain( domain ) ) {
846 DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
847 domain->name));
849 /* Tell the cache manager not to remember this one */
851 return NT_STATUS_SYNCHRONIZATION_REQUIRED;
854 ads = ads_cached_connection(domain);
856 if (!ads) {
857 domain->last_status = NT_STATUS_SERVER_DISABLED;
858 status = NT_STATUS_SERVER_DISABLED;
859 goto done;
862 rc = ads_search_retry_sid(ads, &msg, sid, attrs);
864 if (!ADS_ERR_OK(rc)) {
865 status = ads_ntstatus(rc);
866 DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
867 "%s\n", sid_string_dbg(sid), ads_errstr(rc)));
868 goto done;
871 count = ads_count_replies(ads, msg);
872 if (count != 1) {
873 status = NT_STATUS_UNSUCCESSFUL;
874 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
875 "invalid number of results (count=%d)\n",
876 sid_string_dbg(sid), count));
877 goto done;
880 if (!msg) {
881 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
882 sid_string_dbg(sid)));
883 status = NT_STATUS_UNSUCCESSFUL;
884 goto done;
887 user_dn = ads_get_dn(ads, mem_ctx, msg);
888 if (user_dn == NULL) {
889 status = NT_STATUS_NO_MEMORY;
890 goto done;
893 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
894 DEBUG(1,("%s: No primary group for sid=%s !?\n",
895 domain->name, sid_string_dbg(sid)));
896 goto done;
899 sid_copy(&primary_group, &domain->sid);
900 sid_append_rid(&primary_group, primary_group_rid);
902 count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
904 /* there must always be at least one group in the token,
905 unless we are talking to a buggy Win2k server */
907 /* actually this only happens when the machine account has no read
908 * permissions on the tokenGroup attribute - gd */
910 if (count == 0) {
912 /* no tokenGroups */
914 /* lookup what groups this user is a member of by DN search on
915 * "memberOf" */
917 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
918 &primary_group,
919 &num_groups, user_sids);
920 *p_num_groups = (uint32)num_groups;
921 if (NT_STATUS_IS_OK(status)) {
922 goto done;
925 /* lookup what groups this user is a member of by DN search on
926 * "member" */
928 status = lookup_usergroups_member(domain, mem_ctx, user_dn,
929 &primary_group,
930 &num_groups, user_sids);
931 *p_num_groups = (uint32)num_groups;
932 goto done;
935 *user_sids = NULL;
936 num_groups = 0;
938 status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
939 &num_groups);
940 if (!NT_STATUS_IS_OK(status)) {
941 goto done;
944 for (i=0;i<count;i++) {
946 /* ignore Builtin groups from ADS - Guenther */
947 if (sid_check_is_in_builtin(&sids[i])) {
948 continue;
951 status = add_sid_to_array_unique(mem_ctx, &sids[i],
952 user_sids, &num_groups);
953 if (!NT_STATUS_IS_OK(status)) {
954 goto done;
958 *p_num_groups = (uint32)num_groups;
959 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
961 DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
962 sid_string_dbg(sid)));
963 done:
964 TALLOC_FREE(user_dn);
965 ads_msgfree(ads, msg);
966 return status;
969 /* Lookup aliases a user is member of - use rpc methods */
970 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
971 TALLOC_CTX *mem_ctx,
972 uint32 num_sids, const DOM_SID *sids,
973 uint32 *num_aliases, uint32 **alias_rids)
975 return reconnect_methods.lookup_useraliases(domain, mem_ctx,
976 num_sids, sids,
977 num_aliases,
978 alias_rids);
982 find the members of a group, given a group rid and domain
984 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
985 TALLOC_CTX *mem_ctx,
986 const DOM_SID *group_sid,
987 enum lsa_SidType type,
988 uint32 *num_names,
989 DOM_SID **sid_mem, char ***names,
990 uint32 **name_types)
992 ADS_STATUS rc;
993 ADS_STRUCT *ads = NULL;
994 char *ldap_exp;
995 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
996 char *sidbinstr;
997 char **members = NULL;
998 int i;
999 size_t num_members = 0;
1000 ads_control args;
1001 DOM_SID *sid_mem_nocache = NULL;
1002 char **names_nocache = NULL;
1003 enum lsa_SidType *name_types_nocache = NULL;
1004 char **domains_nocache = NULL; /* only needed for rpccli_lsa_lookup_sids */
1005 uint32 num_nocache = 0;
1006 TALLOC_CTX *tmp_ctx = NULL;
1008 DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
1009 sid_string_dbg(group_sid)));
1011 *num_names = 0;
1013 tmp_ctx = talloc_new(mem_ctx);
1014 if (!tmp_ctx) {
1015 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1016 status = NT_STATUS_NO_MEMORY;
1017 goto done;
1020 if ( !winbindd_can_contact_domain( domain ) ) {
1021 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
1022 domain->name));
1023 return NT_STATUS_OK;
1026 ads = ads_cached_connection(domain);
1028 if (!ads) {
1029 domain->last_status = NT_STATUS_SERVER_DISABLED;
1030 goto done;
1033 if ((sidbinstr = sid_binstring(talloc_tos(), group_sid)) == NULL) {
1034 status = NT_STATUS_NO_MEMORY;
1035 goto done;
1038 /* search for all members of the group */
1039 ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", sidbinstr);
1040 TALLOC_FREE(sidbinstr);
1041 if (ldap_exp == NULL) {
1042 DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
1043 status = NT_STATUS_NO_MEMORY;
1044 goto done;
1047 args.control = ADS_EXTENDED_DN_OID;
1048 args.val = ADS_EXTENDED_DN_HEX_STRING;
1049 args.critical = True;
1051 rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
1052 ldap_exp, &args, "member", &members, &num_members);
1054 if (!ADS_ERR_OK(rc)) {
1055 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
1056 status = NT_STATUS_UNSUCCESSFUL;
1057 goto done;
1060 DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
1062 /* Now that we have a list of sids, we need to get the
1063 * lists of names and name_types belonging to these sids.
1064 * even though conceptually not quite clean, we use the
1065 * RPC call lsa_lookup_sids for this since it can handle a
1066 * list of sids. ldap calls can just resolve one sid at a time.
1068 * At this stage, the sids are still hidden in the exetended dn
1069 * member output format. We actually do a little better than
1070 * stated above: In extracting the sids from the member strings,
1071 * we try to resolve as many sids as possible from the
1072 * cache. Only the rest is passed to the lsa_lookup_sids call. */
1074 if (num_members) {
1075 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
1076 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
1077 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
1078 (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, DOM_SID, num_members);
1080 if ((members == NULL) || (*sid_mem == NULL) ||
1081 (*names == NULL) || (*name_types == NULL) ||
1082 (sid_mem_nocache == NULL))
1084 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1085 status = NT_STATUS_NO_MEMORY;
1086 goto done;
1089 else {
1090 (*sid_mem) = NULL;
1091 (*names) = NULL;
1092 (*name_types) = NULL;
1095 for (i=0; i<num_members; i++) {
1096 enum lsa_SidType name_type;
1097 char *name, *domain_name;
1098 DOM_SID sid;
1100 rc = ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val,
1101 &sid);
1102 if (!ADS_ERR_OK(rc)) {
1103 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
1104 NT_STATUS_NOT_FOUND)) {
1105 /* Group members can be objects, like Exchange
1106 * Public Folders, that don't have a SID. Skip
1107 * them. */
1108 continue;
1110 else {
1111 status = ads_ntstatus(rc);
1112 goto done;
1115 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name,
1116 &name_type)) {
1117 DEBUG(10,("ads: lookup_groupmem: got sid %s from "
1118 "cache\n", sid_string_dbg(&sid)));
1119 sid_copy(&(*sid_mem)[*num_names], &sid);
1120 (*names)[*num_names] = fill_domain_username_talloc(
1121 *names,
1122 domain_name,
1123 name,
1124 true);
1126 (*name_types)[*num_names] = name_type;
1127 (*num_names)++;
1129 else {
1130 DEBUG(10, ("ads: lookup_groupmem: sid %s not found in "
1131 "cache\n", sid_string_dbg(&sid)));
1132 sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
1133 num_nocache++;
1137 DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
1138 "%d left for lsa_lookupsids\n", *num_names, num_nocache));
1140 /* handle sids not resolved from cache by lsa_lookup_sids */
1141 if (num_nocache > 0) {
1143 status = winbindd_lookup_sids(tmp_ctx,
1144 domain,
1145 num_nocache,
1146 sid_mem_nocache,
1147 &domains_nocache,
1148 &names_nocache,
1149 &name_types_nocache);
1151 if (!(NT_STATUS_IS_OK(status) ||
1152 NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) ||
1153 NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)))
1155 DEBUG(1, ("lsa_lookupsids call failed with %s "
1156 "- retrying...\n", nt_errstr(status)));
1158 status = winbindd_lookup_sids(tmp_ctx,
1159 domain,
1160 num_nocache,
1161 sid_mem_nocache,
1162 &domains_nocache,
1163 &names_nocache,
1164 &name_types_nocache);
1167 if (NT_STATUS_IS_OK(status) ||
1168 NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
1170 /* Copy the entries over from the "_nocache" arrays
1171 * to the result arrays, skipping the gaps the
1172 * lookup_sids call left. */
1173 for (i=0; i < num_nocache; i++) {
1174 if (((names_nocache)[i] != NULL) &&
1175 ((name_types_nocache)[i] != SID_NAME_UNKNOWN))
1177 sid_copy(&(*sid_mem)[*num_names],
1178 &sid_mem_nocache[i]);
1179 (*names)[*num_names] =
1180 fill_domain_username_talloc(
1181 *names,
1182 domains_nocache[i],
1183 names_nocache[i],
1184 true);
1185 (*name_types)[*num_names] = name_types_nocache[i];
1186 (*num_names)++;
1190 else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1191 DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
1192 "not map any SIDs at all.\n"));
1193 /* Don't handle this as an error here.
1194 * There is nothing left to do with respect to the
1195 * overall result... */
1197 else if (!NT_STATUS_IS_OK(status)) {
1198 DEBUG(10, ("lookup_groupmem: Error looking up %d "
1199 "sids via rpc_lsa_lookup_sids: %s\n",
1200 (int)num_members, nt_errstr(status)));
1201 goto done;
1205 status = NT_STATUS_OK;
1206 DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
1207 sid_string_dbg(group_sid)));
1209 done:
1211 TALLOC_FREE(tmp_ctx);
1213 return status;
1216 /* find the sequence number for a domain */
1217 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1219 ADS_STRUCT *ads = NULL;
1220 ADS_STATUS rc;
1222 DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1224 if ( !winbindd_can_contact_domain( domain ) ) {
1225 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1226 domain->name));
1227 *seq = time(NULL);
1228 return NT_STATUS_OK;
1231 *seq = DOM_SEQUENCE_NONE;
1233 ads = ads_cached_connection(domain);
1235 if (!ads) {
1236 domain->last_status = NT_STATUS_SERVER_DISABLED;
1237 return NT_STATUS_UNSUCCESSFUL;
1240 rc = ads_USN(ads, seq);
1242 if (!ADS_ERR_OK(rc)) {
1244 /* its a dead connection, destroy it */
1246 if (domain->private_data) {
1247 ads = (ADS_STRUCT *)domain->private_data;
1248 ads->is_mine = True;
1249 ads_destroy(&ads);
1250 ads_kdestroy("MEMORY:winbind_ccache");
1251 domain->private_data = NULL;
1254 return ads_ntstatus(rc);
1257 /* find the lockout policy of a domain - use rpc methods */
1258 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1259 TALLOC_CTX *mem_ctx,
1260 struct samr_DomInfo12 *policy)
1262 return reconnect_methods.lockout_policy(domain, mem_ctx, policy);
1265 /* find the password policy of a domain - use rpc methods */
1266 static NTSTATUS password_policy(struct winbindd_domain *domain,
1267 TALLOC_CTX *mem_ctx,
1268 struct samr_DomInfo1 *policy)
1270 return reconnect_methods.password_policy(domain, mem_ctx, policy);
1273 /* get a list of trusted domains */
1274 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1275 TALLOC_CTX *mem_ctx,
1276 struct netr_DomainTrustList *trusts)
1278 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1279 int i;
1280 uint32 flags;
1281 struct rpc_pipe_client *cli;
1282 uint32 fr_flags = (NETR_TRUST_FLAG_IN_FOREST | NETR_TRUST_FLAG_TREEROOT);
1283 int ret_count;
1285 DEBUG(3,("ads: trusted_domains\n"));
1287 ZERO_STRUCTP(trusts);
1289 /* If this is our primary domain or a root in our forest,
1290 query for all trusts. If not, then just look for domain
1291 trusts in the target forest */
1293 if ( domain->primary ||
1294 ((domain->domain_flags&fr_flags) == fr_flags) )
1296 flags = NETR_TRUST_FLAG_OUTBOUND |
1297 NETR_TRUST_FLAG_INBOUND |
1298 NETR_TRUST_FLAG_IN_FOREST;
1299 } else {
1300 flags = NETR_TRUST_FLAG_IN_FOREST;
1303 result = cm_connect_netlogon(domain, &cli);
1305 if (!NT_STATUS_IS_OK(result)) {
1306 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1307 "for PIPE_NETLOGON (%s)\n",
1308 domain->name, nt_errstr(result)));
1309 return NT_STATUS_UNSUCCESSFUL;
1312 result = rpccli_netr_DsrEnumerateDomainTrusts(cli, mem_ctx,
1313 cli->desthost,
1314 flags,
1315 trusts,
1316 NULL);
1317 if (!NT_STATUS_IS_OK(result)) {
1318 return result;
1320 if (trusts->count == 0) {
1321 return NT_STATUS_OK;
1324 /* Copy across names and sids */
1326 ret_count = 0;
1327 for (i = 0; i < trusts->count; i++) {
1328 struct netr_DomainTrust *trust = &trusts->array[i];
1329 struct winbindd_domain d;
1331 ZERO_STRUCT(d);
1334 * drop external trusts if this is not our primary
1335 * domain. This means that the returned number of
1336 * domains may be less that the ones actually trusted
1337 * by the DC.
1340 if ((trust->trust_attributes
1341 == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) &&
1342 !domain->primary )
1344 DEBUG(10,("trusted_domains: Skipping external trusted "
1345 "domain %s because it is outside of our "
1346 "primary domain\n",
1347 trust->netbios_name));
1348 continue;
1351 /* add to the trusted domain cache */
1353 fstrcpy(d.name, trust->netbios_name);
1354 fstrcpy(d.alt_name, trust->dns_name);
1355 if (trust->sid) {
1356 sid_copy(&d.sid, trust->sid);
1357 } else {
1358 sid_copy(&d.sid, &global_sid_NULL);
1361 if ( domain->primary ) {
1362 DEBUG(10,("trusted_domains(ads): Searching "
1363 "trusted domain list of %s and storing "
1364 "trust flags for domain %s\n",
1365 domain->name, d.alt_name));
1367 d.domain_flags = trust->trust_flags;
1368 d.domain_type = trust->trust_type;
1369 d.domain_trust_attribs = trust->trust_attributes;
1371 wcache_tdc_add_domain( &d );
1372 ret_count++;
1373 } else if ( (domain->domain_flags&fr_flags) == fr_flags ) {
1374 /* Check if we already have this record. If
1375 * we are following our forest root that is not
1376 * our primary domain, we want to keep trust
1377 * flags from the perspective of our primary
1378 * domain not our forest root. */
1379 struct winbindd_tdc_domain *exist = NULL;
1381 exist = wcache_tdc_fetch_domain(
1382 talloc_tos(), trust->netbios_name);
1383 if (!exist) {
1384 DEBUG(10,("trusted_domains(ads): Searching "
1385 "trusted domain list of %s and "
1386 "storing trust flags for domain "
1387 "%s\n", domain->name, d.alt_name));
1388 d.domain_flags = trust->trust_flags;
1389 d.domain_type = trust->trust_type;
1390 d.domain_trust_attribs =
1391 trust->trust_attributes;
1393 wcache_tdc_add_domain( &d );
1394 ret_count++;
1396 TALLOC_FREE(exist);
1397 } else {
1398 /* This gets a little tricky. If we are
1399 following a transitive forest trust, then
1400 innerit the flags, type, and attribs from
1401 the domain we queried to make sure we don't
1402 record the view of the trust from the wrong
1403 side. Always view it from the side of our
1404 primary domain. --jerry */
1405 struct winbindd_tdc_domain *parent = NULL;
1407 DEBUG(10,("trusted_domains(ads): Searching "
1408 "trusted domain list of %s and inheriting "
1409 "trust flags for domain %s\n",
1410 domain->name, d.alt_name));
1412 parent = wcache_tdc_fetch_domain(talloc_tos(),
1413 domain->name);
1414 if (parent) {
1415 d.domain_flags = parent->trust_flags;
1416 d.domain_type = parent->trust_type;
1417 d.domain_trust_attribs = parent->trust_attribs;
1418 } else {
1419 d.domain_flags = domain->domain_flags;
1420 d.domain_type = domain->domain_type;
1421 d.domain_trust_attribs =
1422 domain->domain_trust_attribs;
1424 TALLOC_FREE(parent);
1426 wcache_tdc_add_domain( &d );
1427 ret_count++;
1430 return result;
1433 /* the ADS backend methods are exposed via this structure */
1434 struct winbindd_methods ads_methods = {
1435 True,
1436 query_user_list,
1437 enum_dom_groups,
1438 enum_local_groups,
1439 name_to_sid,
1440 sid_to_name,
1441 rids_to_names,
1442 query_user,
1443 lookup_usergroups,
1444 lookup_useraliases,
1445 lookup_groupmem,
1446 sequence_number,
1447 lockout_policy,
1448 password_policy,
1449 trusted_domains,
1452 #endif