s3-secrets: only include secrets.h when needed.
[Samba.git] / source3 / winbindd / winbindd_ads.c
blob1d9f1f0174162ccd3e300bd22de9f93828ca57c1
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"
27 #include "../libds/common/flags.h"
28 #include "ads.h"
29 #include "secrets.h"
31 #ifdef HAVE_ADS
33 #undef DBGC_CLASS
34 #define DBGC_CLASS DBGC_WINBIND
36 extern struct winbindd_methods reconnect_methods;
39 return our ads connections structure for a domain. We keep the connection
40 open to make things faster
42 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
44 ADS_STRUCT *ads;
45 ADS_STATUS status;
46 fstring dc_name;
47 struct sockaddr_storage dc_ss;
49 DEBUG(10,("ads_cached_connection\n"));
51 if (domain->private_data) {
53 time_t expire;
54 time_t now = time(NULL);
56 /* check for a valid structure */
57 ads = (ADS_STRUCT *)domain->private_data;
59 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
61 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
62 (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
64 if ( ads->config.realm && (expire > now)) {
65 return ads;
66 } else {
67 /* we own this ADS_STRUCT so make sure it goes away */
68 DEBUG(7,("Deleting expired krb5 credential cache\n"));
69 ads->is_mine = True;
70 ads_destroy( &ads );
71 ads_kdestroy("MEMORY:winbind_ccache");
72 domain->private_data = NULL;
76 /* we don't want this to affect the users ccache */
77 setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
79 ads = ads_init(domain->alt_name, domain->name, NULL);
80 if (!ads) {
81 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
82 return NULL;
85 /* the machine acct password might have change - fetch it every time */
87 SAFE_FREE(ads->auth.password);
88 SAFE_FREE(ads->auth.realm);
90 if ( IS_DC ) {
92 if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, NULL, NULL ) ) {
93 ads_destroy( &ads );
94 return NULL;
96 ads->auth.realm = SMB_STRDUP( ads->server.realm );
97 strupper_m( ads->auth.realm );
99 else {
100 struct winbindd_domain *our_domain = domain;
102 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
104 /* always give preference to the alt_name in our
105 primary domain if possible */
107 if ( !domain->primary )
108 our_domain = find_our_domain();
110 if ( our_domain->alt_name[0] != '\0' ) {
111 ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
112 strupper_m( ads->auth.realm );
114 else
115 ads->auth.realm = SMB_STRDUP( lp_realm() );
118 ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
120 /* Setup the server affinity cache. We don't reaally care
121 about the name. Just setup affinity and the KRB5_CONFIG
122 file. */
124 get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ss );
126 status = ads_connect(ads);
127 if (!ADS_ERR_OK(status) || !ads->config.realm) {
128 DEBUG(1,("ads_connect for domain %s failed: %s\n",
129 domain->name, ads_errstr(status)));
130 ads_destroy(&ads);
132 /* if we get ECONNREFUSED then it might be a NT4
133 server, fall back to MSRPC */
134 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
135 status.err.rc == ECONNREFUSED) {
136 /* 'reconnect_methods' is the MS-RPC backend. */
137 DEBUG(1,("Trying MSRPC methods\n"));
138 domain->backend = &reconnect_methods;
140 return NULL;
143 /* set the flag that says we don't own the memory even
144 though we do so that ads_destroy() won't destroy the
145 structure we pass back by reference */
147 ads->is_mine = False;
149 domain->private_data = (void *)ads;
150 return ads;
154 /* Query display info for a realm. This is the basic user list fn */
155 static NTSTATUS query_user_list(struct winbindd_domain *domain,
156 TALLOC_CTX *mem_ctx,
157 uint32 *num_entries,
158 struct wbint_userinfo **pinfo)
160 ADS_STRUCT *ads = NULL;
161 const char *attrs[] = { "*", NULL };
162 int i, count;
163 ADS_STATUS rc;
164 LDAPMessage *res = NULL;
165 LDAPMessage *msg = NULL;
166 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
168 *num_entries = 0;
170 DEBUG(3,("ads: query_user_list\n"));
172 if ( !winbindd_can_contact_domain( domain ) ) {
173 DEBUG(10,("query_user_list: No incoming trust for domain %s\n",
174 domain->name));
175 return NT_STATUS_OK;
178 ads = ads_cached_connection(domain);
180 if (!ads) {
181 domain->last_status = NT_STATUS_SERVER_DISABLED;
182 goto done;
185 rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
186 if (!ADS_ERR_OK(rc) || !res) {
187 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
188 goto done;
191 count = ads_count_replies(ads, res);
192 if (count == 0) {
193 DEBUG(1,("query_user_list: No users found\n"));
194 goto done;
197 (*pinfo) = TALLOC_ZERO_ARRAY(mem_ctx, struct wbint_userinfo, count);
198 if (!*pinfo) {
199 status = NT_STATUS_NO_MEMORY;
200 goto done;
203 count = 0;
205 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
206 struct wbint_userinfo *info = &((*pinfo)[count]);
207 uint32 group;
208 uint32 atype;
210 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
211 ds_atype_map(atype) != SID_NAME_USER) {
212 DEBUG(1,("Not a user account? atype=0x%x\n", atype));
213 continue;
216 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
217 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
218 info->homedir = NULL;
219 info->shell = NULL;
220 info->primary_gid = (gid_t)-1;
222 if (!ads_pull_sid(ads, msg, "objectSid",
223 &info->user_sid)) {
224 DEBUG(1, ("No sid for %s !?\n", info->acct_name));
225 continue;
228 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
229 DEBUG(1, ("No primary group for %s !?\n",
230 info->acct_name));
231 continue;
233 sid_compose(&info->group_sid, &domain->sid, group);
235 count += 1;
238 (*num_entries) = count;
239 ads_msgfree(ads, res);
241 for (i=0; i<count; i++) {
242 struct wbint_userinfo *info = &((*pinfo)[i]);
243 const char *gecos = NULL;
244 gid_t primary_gid = (gid_t)-1;
247 * Don't use our variable "ads" in this call here, every call
248 * to nss_get_info_cached can destroy the connection inside
249 * the domain.
251 status = nss_get_info_cached(domain, &info->user_sid, mem_ctx,
252 ads_cached_connection(domain),
253 msg, &info->homedir, &info->shell,
254 &gecos, &primary_gid);
255 if (!NT_STATUS_IS_OK(status)) {
257 * Deliberately ignore this error, there might be more
258 * users to fill
260 continue;
263 if (gecos != NULL) {
264 info->full_name = gecos;
266 info->primary_gid = primary_gid;
269 status = NT_STATUS_OK;
271 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
273 done:
274 return status;
277 /* list all domain groups */
278 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
279 TALLOC_CTX *mem_ctx,
280 uint32 *num_entries,
281 struct acct_info **info)
283 ADS_STRUCT *ads = NULL;
284 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
285 "name", "objectSid", NULL};
286 int i, count;
287 ADS_STATUS rc;
288 LDAPMessage *res = NULL;
289 LDAPMessage *msg = NULL;
290 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
291 const char *filter;
292 bool enum_dom_local_groups = False;
294 *num_entries = 0;
296 DEBUG(3,("ads: enum_dom_groups\n"));
298 if ( !winbindd_can_contact_domain( domain ) ) {
299 DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n",
300 domain->name));
301 return NT_STATUS_OK;
304 /* only grab domain local groups for our domain */
305 if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) {
306 enum_dom_local_groups = True;
309 /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
310 * rollup-fixes:
312 * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
313 * default value, it MUST be absent. In case of extensible matching the
314 * "dnattr" boolean defaults to FALSE and so it must be only be present
315 * when set to TRUE.
317 * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
318 * filter using bitwise matching rule then the buggy AD fails to decode
319 * the extensible match. As a workaround set it to TRUE and thereby add
320 * the dnAttributes "dn" field to cope with those older AD versions.
321 * It should not harm and won't put any additional load on the AD since
322 * none of the dn components have a bitmask-attribute.
324 * Thanks to Ralf Haferkamp for input and testing - Guenther */
326 filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))",
327 ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
328 ADS_LDAP_MATCHING_RULE_BIT_AND,
329 enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
331 if (filter == NULL) {
332 status = NT_STATUS_NO_MEMORY;
333 goto done;
336 ads = ads_cached_connection(domain);
338 if (!ads) {
339 domain->last_status = NT_STATUS_SERVER_DISABLED;
340 goto done;
343 rc = ads_search_retry(ads, &res, filter, attrs);
344 if (!ADS_ERR_OK(rc) || !res) {
345 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
346 goto done;
349 count = ads_count_replies(ads, res);
350 if (count == 0) {
351 DEBUG(1,("enum_dom_groups: No groups found\n"));
352 goto done;
355 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
356 if (!*info) {
357 status = NT_STATUS_NO_MEMORY;
358 goto done;
361 i = 0;
363 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
364 char *name, *gecos;
365 struct dom_sid sid;
366 uint32 rid;
368 name = ads_pull_username(ads, mem_ctx, msg);
369 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
370 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
371 DEBUG(1,("No sid for %s !?\n", name));
372 continue;
375 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
376 DEBUG(1,("No rid for %s !?\n", name));
377 continue;
380 fstrcpy((*info)[i].acct_name, name);
381 fstrcpy((*info)[i].acct_desc, gecos);
382 (*info)[i].rid = rid;
383 i++;
386 (*num_entries) = i;
388 status = NT_STATUS_OK;
390 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
392 done:
393 if (res)
394 ads_msgfree(ads, res);
396 return status;
399 /* list all domain local groups */
400 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
401 TALLOC_CTX *mem_ctx,
402 uint32 *num_entries,
403 struct acct_info **info)
406 * This is a stub function only as we returned the domain
407 * local groups in enum_dom_groups() if the domain->native field
408 * was true. This is a simple performance optimization when
409 * using LDAP.
411 * if we ever need to enumerate domain local groups separately,
412 * then this optimization in enum_dom_groups() will need
413 * to be split out
415 *num_entries = 0;
417 return NT_STATUS_OK;
420 /* convert a single name to a sid in a domain - use rpc methods */
421 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
422 TALLOC_CTX *mem_ctx,
423 const char *domain_name,
424 const char *name,
425 uint32_t flags,
426 struct dom_sid *sid,
427 enum lsa_SidType *type)
429 return reconnect_methods.name_to_sid(domain, mem_ctx,
430 domain_name, name, flags,
431 sid, type);
434 /* convert a domain SID to a user or group name - use rpc methods */
435 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
436 TALLOC_CTX *mem_ctx,
437 const struct dom_sid *sid,
438 char **domain_name,
439 char **name,
440 enum lsa_SidType *type)
442 return reconnect_methods.sid_to_name(domain, mem_ctx, sid,
443 domain_name, name, type);
446 /* convert a list of rids to names - use rpc methods */
447 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
448 TALLOC_CTX *mem_ctx,
449 const struct dom_sid *sid,
450 uint32 *rids,
451 size_t num_rids,
452 char **domain_name,
453 char ***names,
454 enum lsa_SidType **types)
456 return reconnect_methods.rids_to_names(domain, mem_ctx, sid,
457 rids, num_rids,
458 domain_name, names, types);
461 /* If you are looking for "dn_lookup": Yes, it used to be here!
462 * It has gone now since it was a major speed bottleneck in
463 * lookup_groupmem (its only use). It has been replaced by
464 * an rpc lookup sids call... R.I.P. */
466 /* Lookup user information from a rid */
467 static NTSTATUS query_user(struct winbindd_domain *domain,
468 TALLOC_CTX *mem_ctx,
469 const struct dom_sid *sid,
470 struct wbint_userinfo *info)
472 ADS_STRUCT *ads = NULL;
473 const char *attrs[] = { "*", NULL };
474 ADS_STATUS rc;
475 int count;
476 LDAPMessage *msg = NULL;
477 char *ldap_exp;
478 char *sidstr;
479 uint32 group_rid;
480 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
481 struct netr_SamInfo3 *user = NULL;
482 gid_t gid = -1;
483 int ret;
484 char *ads_name;
486 DEBUG(3,("ads: query_user\n"));
488 info->homedir = NULL;
489 info->shell = NULL;
491 /* try netsamlogon cache first */
493 if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL )
495 DEBUG(5,("query_user: Cache lookup succeeded for %s\n",
496 sid_string_dbg(sid)));
498 sid_compose(&info->user_sid, &domain->sid, user->base.rid);
499 sid_compose(&info->group_sid, &domain->sid, user->base.primary_gid);
501 info->acct_name = talloc_strdup(mem_ctx, user->base.account_name.string);
502 info->full_name = talloc_strdup(mem_ctx, user->base.full_name.string);
504 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL,
505 &info->homedir, &info->shell, &info->full_name,
506 &gid );
507 info->primary_gid = gid;
509 TALLOC_FREE(user);
511 return NT_STATUS_OK;
514 if ( !winbindd_can_contact_domain(domain)) {
515 DEBUG(8,("query_user: No incoming trust from domain %s\n",
516 domain->name));
518 /* We still need to generate some basic information
519 about the user even if we cannot contact the
520 domain. Most of this stuff we can deduce. */
522 sid_copy( &info->user_sid, sid );
524 /* Assume "Domain Users" for the primary group */
526 sid_compose(&info->group_sid, &domain->sid, DOMAIN_RID_USERS );
528 /* Try to fill in what the nss_info backend can do */
530 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL,
531 &info->homedir, &info->shell, &info->full_name,
532 &gid);
533 info->primary_gid = gid;
535 return NT_STATUS_OK;
538 /* no cache...do the query */
540 if ( (ads = ads_cached_connection(domain)) == NULL ) {
541 domain->last_status = NT_STATUS_SERVER_DISABLED;
542 return NT_STATUS_SERVER_DISABLED;
545 sidstr = sid_binstring(talloc_tos(), sid);
547 ret = asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
548 TALLOC_FREE(sidstr);
549 if (ret == -1) {
550 return NT_STATUS_NO_MEMORY;
552 rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
553 SAFE_FREE(ldap_exp);
554 if (!ADS_ERR_OK(rc) || !msg) {
555 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
556 sid_string_dbg(sid), ads_errstr(rc)));
557 return ads_ntstatus(rc);
560 count = ads_count_replies(ads, msg);
561 if (count != 1) {
562 DEBUG(1,("query_user(sid=%s): Not found\n",
563 sid_string_dbg(sid)));
564 ads_msgfree(ads, msg);
565 return NT_STATUS_NO_SUCH_USER;
568 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
570 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
571 DEBUG(1,("No primary group for %s !?\n",
572 sid_string_dbg(sid)));
573 ads_msgfree(ads, msg);
574 return NT_STATUS_NO_SUCH_USER;
576 sid_copy(&info->user_sid, sid);
577 sid_compose(&info->group_sid, &domain->sid, group_rid);
580 * We have to fetch the "name" attribute before doing the
581 * nss_get_info_cached call. nss_get_info_cached might destroy
582 * the ads struct, potentially invalidating the ldap message.
584 ads_name = ads_pull_string(ads, mem_ctx, msg, "name");
586 ads_msgfree(ads, msg);
587 msg = NULL;
589 status = nss_get_info_cached( domain, sid, mem_ctx, ads, msg,
590 &info->homedir, &info->shell, &info->full_name,
591 &gid);
592 info->primary_gid = gid;
593 if (!NT_STATUS_IS_OK(status)) {
594 DEBUG(1, ("nss_get_info_cached failed: %s\n",
595 nt_errstr(status)));
596 return status;
599 if (info->full_name == NULL) {
600 info->full_name = ads_name;
601 } else {
602 TALLOC_FREE(ads_name);
605 status = NT_STATUS_OK;
607 DEBUG(3,("ads query_user gave %s\n", info->acct_name));
608 return NT_STATUS_OK;
611 /* Lookup groups a user is a member of - alternate method, for when
612 tokenGroups are not available. */
613 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
614 TALLOC_CTX *mem_ctx,
615 const char *user_dn,
616 struct dom_sid *primary_group,
617 size_t *p_num_groups, struct dom_sid **user_sids)
619 ADS_STATUS rc;
620 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
621 int count;
622 LDAPMessage *res = NULL;
623 LDAPMessage *msg = NULL;
624 char *ldap_exp;
625 ADS_STRUCT *ads;
626 const char *group_attrs[] = {"objectSid", NULL};
627 char *escaped_dn;
628 size_t num_groups = 0;
630 DEBUG(3,("ads: lookup_usergroups_member\n"));
632 if ( !winbindd_can_contact_domain( domain ) ) {
633 DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
634 domain->name));
635 return NT_STATUS_OK;
638 ads = ads_cached_connection(domain);
640 if (!ads) {
641 domain->last_status = NT_STATUS_SERVER_DISABLED;
642 goto done;
645 if (!(escaped_dn = escape_ldap_string(talloc_tos(), user_dn))) {
646 status = NT_STATUS_NO_MEMORY;
647 goto done;
650 ldap_exp = talloc_asprintf(mem_ctx,
651 "(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))",
652 escaped_dn,
653 ADS_LDAP_MATCHING_RULE_BIT_AND,
654 GROUP_TYPE_SECURITY_ENABLED);
655 if (!ldap_exp) {
656 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
657 TALLOC_FREE(escaped_dn);
658 status = NT_STATUS_NO_MEMORY;
659 goto done;
662 TALLOC_FREE(escaped_dn);
664 rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
666 if (!ADS_ERR_OK(rc) || !res) {
667 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
668 return ads_ntstatus(rc);
671 count = ads_count_replies(ads, res);
673 *user_sids = NULL;
674 num_groups = 0;
676 /* always add the primary group to the sid array */
677 status = add_sid_to_array(mem_ctx, primary_group, user_sids,
678 &num_groups);
679 if (!NT_STATUS_IS_OK(status)) {
680 goto done;
683 if (count > 0) {
684 for (msg = ads_first_entry(ads, res); msg;
685 msg = ads_next_entry(ads, msg)) {
686 struct dom_sid group_sid;
688 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
689 DEBUG(1,("No sid for this group ?!?\n"));
690 continue;
693 /* ignore Builtin groups from ADS - Guenther */
694 if (sid_check_is_in_builtin(&group_sid)) {
695 continue;
698 status = add_sid_to_array(mem_ctx, &group_sid,
699 user_sids, &num_groups);
700 if (!NT_STATUS_IS_OK(status)) {
701 goto done;
707 *p_num_groups = num_groups;
708 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
710 DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
711 done:
712 if (res)
713 ads_msgfree(ads, res);
715 return status;
718 /* Lookup groups a user is a member of - alternate method, for when
719 tokenGroups are not available. */
720 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
721 TALLOC_CTX *mem_ctx,
722 const char *user_dn,
723 struct dom_sid *primary_group,
724 size_t *p_num_groups,
725 struct dom_sid **user_sids)
727 ADS_STATUS rc;
728 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
729 ADS_STRUCT *ads;
730 const char *attrs[] = {"memberOf", NULL};
731 size_t num_groups = 0;
732 struct dom_sid *group_sids = NULL;
733 int i;
734 char **strings = NULL;
735 size_t num_strings = 0, num_sids = 0;
738 DEBUG(3,("ads: lookup_usergroups_memberof\n"));
740 if ( !winbindd_can_contact_domain( domain ) ) {
741 DEBUG(10,("lookup_usergroups_memberof: No incoming trust for "
742 "domain %s\n", domain->name));
743 return NT_STATUS_OK;
746 ads = ads_cached_connection(domain);
748 if (!ads) {
749 domain->last_status = NT_STATUS_SERVER_DISABLED;
750 return NT_STATUS_UNSUCCESSFUL;
753 rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
754 ADS_EXTENDED_DN_HEX_STRING,
755 &strings, &num_strings);
757 if (!ADS_ERR_OK(rc)) {
758 DEBUG(1,("lookup_usergroups_memberof ads_search "
759 "member=%s: %s\n", user_dn, ads_errstr(rc)));
760 return ads_ntstatus(rc);
763 *user_sids = NULL;
764 num_groups = 0;
766 /* always add the primary group to the sid array */
767 status = add_sid_to_array(mem_ctx, primary_group, user_sids,
768 &num_groups);
769 if (!NT_STATUS_IS_OK(status)) {
770 goto done;
773 group_sids = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, num_strings + 1);
774 if (!group_sids) {
775 status = NT_STATUS_NO_MEMORY;
776 goto done;
779 for (i=0; i<num_strings; i++) {
780 rc = ads_get_sid_from_extended_dn(mem_ctx, strings[i],
781 ADS_EXTENDED_DN_HEX_STRING,
782 &(group_sids)[i]);
783 if (!ADS_ERR_OK(rc)) {
784 /* ignore members without SIDs */
785 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
786 NT_STATUS_NOT_FOUND)) {
787 continue;
789 else {
790 status = ads_ntstatus(rc);
791 goto done;
794 num_sids++;
797 if (i == 0) {
798 DEBUG(1,("No memberOf for this user?!?\n"));
799 status = NT_STATUS_NO_MEMORY;
800 goto done;
803 for (i=0; i<num_sids; i++) {
805 /* ignore Builtin groups from ADS - Guenther */
806 if (sid_check_is_in_builtin(&group_sids[i])) {
807 continue;
810 status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
811 &num_groups);
812 if (!NT_STATUS_IS_OK(status)) {
813 goto done;
818 *p_num_groups = num_groups;
819 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
821 DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n",
822 user_dn));
824 done:
825 TALLOC_FREE(strings);
826 TALLOC_FREE(group_sids);
828 return status;
832 /* Lookup groups a user is a member of. */
833 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
834 TALLOC_CTX *mem_ctx,
835 const struct dom_sid *sid,
836 uint32 *p_num_groups, struct dom_sid **user_sids)
838 ADS_STRUCT *ads = NULL;
839 const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
840 ADS_STATUS rc;
841 int count;
842 LDAPMessage *msg = NULL;
843 char *user_dn = NULL;
844 struct dom_sid *sids;
845 int i;
846 struct dom_sid primary_group;
847 uint32 primary_group_rid;
848 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
849 size_t num_groups = 0;
851 DEBUG(3,("ads: lookup_usergroups\n"));
852 *p_num_groups = 0;
854 status = lookup_usergroups_cached(domain, mem_ctx, sid,
855 p_num_groups, user_sids);
856 if (NT_STATUS_IS_OK(status)) {
857 return NT_STATUS_OK;
860 if ( !winbindd_can_contact_domain( domain ) ) {
861 DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
862 domain->name));
864 /* Tell the cache manager not to remember this one */
866 return NT_STATUS_SYNCHRONIZATION_REQUIRED;
869 ads = ads_cached_connection(domain);
871 if (!ads) {
872 domain->last_status = NT_STATUS_SERVER_DISABLED;
873 status = NT_STATUS_SERVER_DISABLED;
874 goto done;
877 rc = ads_search_retry_sid(ads, &msg, sid, attrs);
879 if (!ADS_ERR_OK(rc)) {
880 status = ads_ntstatus(rc);
881 DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
882 "%s\n", sid_string_dbg(sid), ads_errstr(rc)));
883 goto done;
886 count = ads_count_replies(ads, msg);
887 if (count != 1) {
888 status = NT_STATUS_UNSUCCESSFUL;
889 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
890 "invalid number of results (count=%d)\n",
891 sid_string_dbg(sid), count));
892 goto done;
895 if (!msg) {
896 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
897 sid_string_dbg(sid)));
898 status = NT_STATUS_UNSUCCESSFUL;
899 goto done;
902 user_dn = ads_get_dn(ads, mem_ctx, msg);
903 if (user_dn == NULL) {
904 status = NT_STATUS_NO_MEMORY;
905 goto done;
908 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
909 DEBUG(1,("%s: No primary group for sid=%s !?\n",
910 domain->name, sid_string_dbg(sid)));
911 goto done;
914 sid_compose(&primary_group, &domain->sid, primary_group_rid);
916 count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
918 /* there must always be at least one group in the token,
919 unless we are talking to a buggy Win2k server */
921 /* actually this only happens when the machine account has no read
922 * permissions on the tokenGroup attribute - gd */
924 if (count == 0) {
926 /* no tokenGroups */
928 /* lookup what groups this user is a member of by DN search on
929 * "memberOf" */
931 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
932 &primary_group,
933 &num_groups, user_sids);
934 *p_num_groups = (uint32)num_groups;
935 if (NT_STATUS_IS_OK(status)) {
936 goto done;
939 /* lookup what groups this user is a member of by DN search on
940 * "member" */
942 status = lookup_usergroups_member(domain, mem_ctx, user_dn,
943 &primary_group,
944 &num_groups, user_sids);
945 *p_num_groups = (uint32)num_groups;
946 goto done;
949 *user_sids = NULL;
950 num_groups = 0;
952 status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
953 &num_groups);
954 if (!NT_STATUS_IS_OK(status)) {
955 goto done;
958 for (i=0;i<count;i++) {
960 /* ignore Builtin groups from ADS - Guenther */
961 if (sid_check_is_in_builtin(&sids[i])) {
962 continue;
965 status = add_sid_to_array_unique(mem_ctx, &sids[i],
966 user_sids, &num_groups);
967 if (!NT_STATUS_IS_OK(status)) {
968 goto done;
972 *p_num_groups = (uint32)num_groups;
973 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
975 DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
976 sid_string_dbg(sid)));
977 done:
978 TALLOC_FREE(user_dn);
979 ads_msgfree(ads, msg);
980 return status;
983 /* Lookup aliases a user is member of - use rpc methods */
984 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
985 TALLOC_CTX *mem_ctx,
986 uint32 num_sids, const struct dom_sid *sids,
987 uint32 *num_aliases, uint32 **alias_rids)
989 return reconnect_methods.lookup_useraliases(domain, mem_ctx,
990 num_sids, sids,
991 num_aliases,
992 alias_rids);
996 find the members of a group, given a group rid and domain
998 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
999 TALLOC_CTX *mem_ctx,
1000 const struct dom_sid *group_sid,
1001 enum lsa_SidType type,
1002 uint32 *num_names,
1003 struct dom_sid **sid_mem, char ***names,
1004 uint32 **name_types)
1006 ADS_STATUS rc;
1007 ADS_STRUCT *ads = NULL;
1008 char *ldap_exp;
1009 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1010 char *sidbinstr;
1011 char **members = NULL;
1012 int i;
1013 size_t num_members = 0;
1014 ads_control args;
1015 struct dom_sid *sid_mem_nocache = NULL;
1016 char **names_nocache = NULL;
1017 enum lsa_SidType *name_types_nocache = NULL;
1018 char **domains_nocache = NULL; /* only needed for rpccli_lsa_lookup_sids */
1019 uint32 num_nocache = 0;
1020 TALLOC_CTX *tmp_ctx = NULL;
1022 DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
1023 sid_string_dbg(group_sid)));
1025 *num_names = 0;
1027 tmp_ctx = talloc_new(mem_ctx);
1028 if (!tmp_ctx) {
1029 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1030 status = NT_STATUS_NO_MEMORY;
1031 goto done;
1034 if ( !winbindd_can_contact_domain( domain ) ) {
1035 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
1036 domain->name));
1037 return NT_STATUS_OK;
1040 ads = ads_cached_connection(domain);
1042 if (!ads) {
1043 domain->last_status = NT_STATUS_SERVER_DISABLED;
1044 goto done;
1047 if ((sidbinstr = sid_binstring(talloc_tos(), group_sid)) == NULL) {
1048 status = NT_STATUS_NO_MEMORY;
1049 goto done;
1052 /* search for all members of the group */
1053 ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", sidbinstr);
1054 TALLOC_FREE(sidbinstr);
1055 if (ldap_exp == NULL) {
1056 DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
1057 status = NT_STATUS_NO_MEMORY;
1058 goto done;
1061 args.control = ADS_EXTENDED_DN_OID;
1062 args.val = ADS_EXTENDED_DN_HEX_STRING;
1063 args.critical = True;
1065 rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
1066 ldap_exp, &args, "member", &members, &num_members);
1068 if (!ADS_ERR_OK(rc)) {
1069 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
1070 status = NT_STATUS_UNSUCCESSFUL;
1071 goto done;
1074 DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
1076 /* Now that we have a list of sids, we need to get the
1077 * lists of names and name_types belonging to these sids.
1078 * even though conceptually not quite clean, we use the
1079 * RPC call lsa_lookup_sids for this since it can handle a
1080 * list of sids. ldap calls can just resolve one sid at a time.
1082 * At this stage, the sids are still hidden in the exetended dn
1083 * member output format. We actually do a little better than
1084 * stated above: In extracting the sids from the member strings,
1085 * we try to resolve as many sids as possible from the
1086 * cache. Only the rest is passed to the lsa_lookup_sids call. */
1088 if (num_members) {
1089 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, num_members);
1090 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
1091 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
1092 (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, struct dom_sid, num_members);
1094 if ((members == NULL) || (*sid_mem == NULL) ||
1095 (*names == NULL) || (*name_types == NULL) ||
1096 (sid_mem_nocache == NULL))
1098 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1099 status = NT_STATUS_NO_MEMORY;
1100 goto done;
1103 else {
1104 (*sid_mem) = NULL;
1105 (*names) = NULL;
1106 (*name_types) = NULL;
1109 for (i=0; i<num_members; i++) {
1110 enum lsa_SidType name_type;
1111 char *name, *domain_name;
1112 struct dom_sid sid;
1114 rc = ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val,
1115 &sid);
1116 if (!ADS_ERR_OK(rc)) {
1117 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
1118 NT_STATUS_NOT_FOUND)) {
1119 /* Group members can be objects, like Exchange
1120 * Public Folders, that don't have a SID. Skip
1121 * them. */
1122 continue;
1124 else {
1125 status = ads_ntstatus(rc);
1126 goto done;
1129 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name,
1130 &name_type)) {
1131 DEBUG(10,("ads: lookup_groupmem: got sid %s from "
1132 "cache\n", sid_string_dbg(&sid)));
1133 sid_copy(&(*sid_mem)[*num_names], &sid);
1134 (*names)[*num_names] = fill_domain_username_talloc(
1135 *names,
1136 domain_name,
1137 name,
1138 true);
1140 (*name_types)[*num_names] = name_type;
1141 (*num_names)++;
1143 else {
1144 DEBUG(10, ("ads: lookup_groupmem: sid %s not found in "
1145 "cache\n", sid_string_dbg(&sid)));
1146 sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
1147 num_nocache++;
1151 DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
1152 "%d left for lsa_lookupsids\n", *num_names, num_nocache));
1154 /* handle sids not resolved from cache by lsa_lookup_sids */
1155 if (num_nocache > 0) {
1157 status = winbindd_lookup_sids(tmp_ctx,
1158 domain,
1159 num_nocache,
1160 sid_mem_nocache,
1161 &domains_nocache,
1162 &names_nocache,
1163 &name_types_nocache);
1165 if (!(NT_STATUS_IS_OK(status) ||
1166 NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) ||
1167 NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)))
1169 DEBUG(1, ("lsa_lookupsids call failed with %s "
1170 "- retrying...\n", nt_errstr(status)));
1172 status = winbindd_lookup_sids(tmp_ctx,
1173 domain,
1174 num_nocache,
1175 sid_mem_nocache,
1176 &domains_nocache,
1177 &names_nocache,
1178 &name_types_nocache);
1181 if (NT_STATUS_IS_OK(status) ||
1182 NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
1184 /* Copy the entries over from the "_nocache" arrays
1185 * to the result arrays, skipping the gaps the
1186 * lookup_sids call left. */
1187 for (i=0; i < num_nocache; i++) {
1188 if (((names_nocache)[i] != NULL) &&
1189 ((name_types_nocache)[i] != SID_NAME_UNKNOWN))
1191 sid_copy(&(*sid_mem)[*num_names],
1192 &sid_mem_nocache[i]);
1193 (*names)[*num_names] =
1194 fill_domain_username_talloc(
1195 *names,
1196 domains_nocache[i],
1197 names_nocache[i],
1198 true);
1199 (*name_types)[*num_names] = name_types_nocache[i];
1200 (*num_names)++;
1204 else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1205 DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
1206 "not map any SIDs at all.\n"));
1207 /* Don't handle this as an error here.
1208 * There is nothing left to do with respect to the
1209 * overall result... */
1211 else if (!NT_STATUS_IS_OK(status)) {
1212 DEBUG(10, ("lookup_groupmem: Error looking up %d "
1213 "sids via rpc_lsa_lookup_sids: %s\n",
1214 (int)num_members, nt_errstr(status)));
1215 goto done;
1219 status = NT_STATUS_OK;
1220 DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
1221 sid_string_dbg(group_sid)));
1223 done:
1225 TALLOC_FREE(tmp_ctx);
1227 return status;
1230 /* find the sequence number for a domain */
1231 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1233 ADS_STRUCT *ads = NULL;
1234 ADS_STATUS rc;
1236 DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1238 if ( !winbindd_can_contact_domain( domain ) ) {
1239 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1240 domain->name));
1241 *seq = time(NULL);
1242 return NT_STATUS_OK;
1245 *seq = DOM_SEQUENCE_NONE;
1247 ads = ads_cached_connection(domain);
1249 if (!ads) {
1250 domain->last_status = NT_STATUS_SERVER_DISABLED;
1251 return NT_STATUS_UNSUCCESSFUL;
1254 rc = ads_USN(ads, seq);
1256 if (!ADS_ERR_OK(rc)) {
1258 /* its a dead connection, destroy it */
1260 if (domain->private_data) {
1261 ads = (ADS_STRUCT *)domain->private_data;
1262 ads->is_mine = True;
1263 ads_destroy(&ads);
1264 ads_kdestroy("MEMORY:winbind_ccache");
1265 domain->private_data = NULL;
1268 return ads_ntstatus(rc);
1271 /* find the lockout policy of a domain - use rpc methods */
1272 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1273 TALLOC_CTX *mem_ctx,
1274 struct samr_DomInfo12 *policy)
1276 return reconnect_methods.lockout_policy(domain, mem_ctx, policy);
1279 /* find the password policy of a domain - use rpc methods */
1280 static NTSTATUS password_policy(struct winbindd_domain *domain,
1281 TALLOC_CTX *mem_ctx,
1282 struct samr_DomInfo1 *policy)
1284 return reconnect_methods.password_policy(domain, mem_ctx, policy);
1287 /* get a list of trusted domains */
1288 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1289 TALLOC_CTX *mem_ctx,
1290 struct netr_DomainTrustList *trusts)
1292 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1293 int i;
1294 uint32 flags;
1295 struct rpc_pipe_client *cli;
1296 int ret_count;
1298 DEBUG(3,("ads: trusted_domains\n"));
1300 ZERO_STRUCTP(trusts);
1302 /* If this is our primary domain or a root in our forest,
1303 query for all trusts. If not, then just look for domain
1304 trusts in the target forest */
1306 if (domain->primary || domain_is_forest_root(domain)) {
1307 flags = NETR_TRUST_FLAG_OUTBOUND |
1308 NETR_TRUST_FLAG_INBOUND |
1309 NETR_TRUST_FLAG_IN_FOREST;
1310 } else {
1311 flags = NETR_TRUST_FLAG_IN_FOREST;
1314 result = cm_connect_netlogon(domain, &cli);
1316 if (!NT_STATUS_IS_OK(result)) {
1317 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1318 "for PIPE_NETLOGON (%s)\n",
1319 domain->name, nt_errstr(result)));
1320 return NT_STATUS_UNSUCCESSFUL;
1323 result = rpccli_netr_DsrEnumerateDomainTrusts(cli, mem_ctx,
1324 cli->desthost,
1325 flags,
1326 trusts,
1327 NULL);
1328 if (!NT_STATUS_IS_OK(result)) {
1329 return result;
1331 if (trusts->count == 0) {
1332 return NT_STATUS_OK;
1335 /* Copy across names and sids */
1337 ret_count = 0;
1338 for (i = 0; i < trusts->count; i++) {
1339 struct netr_DomainTrust *trust = &trusts->array[i];
1340 struct winbindd_domain d;
1342 ZERO_STRUCT(d);
1345 * drop external trusts if this is not our primary
1346 * domain. This means that the returned number of
1347 * domains may be less that the ones actually trusted
1348 * by the DC.
1351 if ((trust->trust_attributes
1352 == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) &&
1353 !domain->primary )
1355 DEBUG(10,("trusted_domains: Skipping external trusted "
1356 "domain %s because it is outside of our "
1357 "primary domain\n",
1358 trust->netbios_name));
1359 continue;
1362 /* add to the trusted domain cache */
1364 fstrcpy(d.name, trust->netbios_name);
1365 fstrcpy(d.alt_name, trust->dns_name);
1366 if (trust->sid) {
1367 sid_copy(&d.sid, trust->sid);
1368 } else {
1369 sid_copy(&d.sid, &global_sid_NULL);
1372 if ( domain->primary ) {
1373 DEBUG(10,("trusted_domains(ads): Searching "
1374 "trusted domain list of %s and storing "
1375 "trust flags for domain %s\n",
1376 domain->name, d.alt_name));
1378 d.domain_flags = trust->trust_flags;
1379 d.domain_type = trust->trust_type;
1380 d.domain_trust_attribs = trust->trust_attributes;
1382 wcache_tdc_add_domain( &d );
1383 ret_count++;
1384 } else if (domain_is_forest_root(domain)) {
1385 /* Check if we already have this record. If
1386 * we are following our forest root that is not
1387 * our primary domain, we want to keep trust
1388 * flags from the perspective of our primary
1389 * domain not our forest root. */
1390 struct winbindd_tdc_domain *exist = NULL;
1392 exist = wcache_tdc_fetch_domain(
1393 talloc_tos(), trust->netbios_name);
1394 if (!exist) {
1395 DEBUG(10,("trusted_domains(ads): Searching "
1396 "trusted domain list of %s and "
1397 "storing trust flags for domain "
1398 "%s\n", domain->name, d.alt_name));
1399 d.domain_flags = trust->trust_flags;
1400 d.domain_type = trust->trust_type;
1401 d.domain_trust_attribs =
1402 trust->trust_attributes;
1404 wcache_tdc_add_domain( &d );
1405 ret_count++;
1407 TALLOC_FREE(exist);
1408 } else {
1409 /* This gets a little tricky. If we are
1410 following a transitive forest trust, then
1411 innerit the flags, type, and attribs from
1412 the domain we queried to make sure we don't
1413 record the view of the trust from the wrong
1414 side. Always view it from the side of our
1415 primary domain. --jerry */
1416 struct winbindd_tdc_domain *parent = NULL;
1418 DEBUG(10,("trusted_domains(ads): Searching "
1419 "trusted domain list of %s and inheriting "
1420 "trust flags for domain %s\n",
1421 domain->name, d.alt_name));
1423 parent = wcache_tdc_fetch_domain(talloc_tos(),
1424 domain->name);
1425 if (parent) {
1426 d.domain_flags = parent->trust_flags;
1427 d.domain_type = parent->trust_type;
1428 d.domain_trust_attribs = parent->trust_attribs;
1429 } else {
1430 d.domain_flags = domain->domain_flags;
1431 d.domain_type = domain->domain_type;
1432 d.domain_trust_attribs =
1433 domain->domain_trust_attribs;
1435 TALLOC_FREE(parent);
1437 wcache_tdc_add_domain( &d );
1438 ret_count++;
1441 return result;
1444 /* the ADS backend methods are exposed via this structure */
1445 struct winbindd_methods ads_methods = {
1446 True,
1447 query_user_list,
1448 enum_dom_groups,
1449 enum_local_groups,
1450 name_to_sid,
1451 sid_to_name,
1452 rids_to_names,
1453 query_user,
1454 lookup_usergroups,
1455 lookup_useraliases,
1456 lookup_groupmem,
1457 sequence_number,
1458 lockout_policy,
1459 password_policy,
1460 trusted_domains,
1463 #endif