s3: Add is_domain_online
[Samba.git] / source3 / winbindd / winbindd_ads.c
blob4e8045731ac5598f24728cdd2da50f5f147a64d3
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/ndr_netlogon_c.h"
27 #include "../libds/common/flags.h"
28 #include "ads.h"
29 #include "secrets.h"
30 #include "../libcli/ldap/ldap_ndr.h"
31 #include "../libcli/security/security.h"
32 #include "../libds/common/flag_mapping.h"
33 #include "passdb.h"
35 #ifdef HAVE_ADS
37 #undef DBGC_CLASS
38 #define DBGC_CLASS DBGC_WINBIND
40 extern struct winbindd_methods reconnect_methods;
43 return our ads connections structure for a domain. We keep the connection
44 open to make things faster
46 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
48 ADS_STRUCT *ads;
49 ADS_STATUS status;
50 fstring dc_name;
51 struct sockaddr_storage dc_ss;
53 DEBUG(10,("ads_cached_connection\n"));
55 if (domain->private_data) {
57 time_t expire;
58 time_t now = time(NULL);
60 /* check for a valid structure */
61 ads = (ADS_STRUCT *)domain->private_data;
63 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
65 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
66 (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
68 if ( ads->config.realm && (expire > now)) {
69 return ads;
70 } else {
71 /* we own this ADS_STRUCT so make sure it goes away */
72 DEBUG(7,("Deleting expired krb5 credential cache\n"));
73 ads->is_mine = True;
74 ads_destroy( &ads );
75 ads_kdestroy("MEMORY:winbind_ccache");
76 domain->private_data = NULL;
80 /* we don't want this to affect the users ccache */
81 setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
83 ads = ads_init(domain->alt_name, domain->name, NULL);
84 if (!ads) {
85 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
86 return NULL;
89 /* the machine acct password might have change - fetch it every time */
91 SAFE_FREE(ads->auth.password);
92 SAFE_FREE(ads->auth.realm);
94 if ( IS_DC ) {
96 if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, NULL, NULL ) ) {
97 ads_destroy( &ads );
98 return NULL;
100 ads->auth.realm = SMB_STRDUP( ads->server.realm );
101 strupper_m( ads->auth.realm );
103 else {
104 struct winbindd_domain *our_domain = domain;
106 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
108 /* always give preference to the alt_name in our
109 primary domain if possible */
111 if ( !domain->primary )
112 our_domain = find_our_domain();
114 if ( our_domain->alt_name[0] != '\0' ) {
115 ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
116 strupper_m( ads->auth.realm );
118 else
119 ads->auth.realm = SMB_STRDUP( lp_realm() );
122 ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
124 /* Setup the server affinity cache. We don't reaally care
125 about the name. Just setup affinity and the KRB5_CONFIG
126 file. */
128 get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ss );
130 status = ads_connect(ads);
131 if (!ADS_ERR_OK(status) || !ads->config.realm) {
132 DEBUG(1,("ads_connect for domain %s failed: %s\n",
133 domain->name, ads_errstr(status)));
134 ads_destroy(&ads);
136 /* if we get ECONNREFUSED then it might be a NT4
137 server, fall back to MSRPC */
138 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
139 status.err.rc == ECONNREFUSED) {
140 /* 'reconnect_methods' is the MS-RPC backend. */
141 DEBUG(1,("Trying MSRPC methods\n"));
142 domain->backend = &reconnect_methods;
144 return NULL;
147 /* set the flag that says we don't own the memory even
148 though we do so that ads_destroy() won't destroy the
149 structure we pass back by reference */
151 ads->is_mine = False;
153 domain->private_data = (void *)ads;
154 return ads;
158 /* Query display info for a realm. This is the basic user list fn */
159 static NTSTATUS query_user_list(struct winbindd_domain *domain,
160 TALLOC_CTX *mem_ctx,
161 uint32 *num_entries,
162 struct wbint_userinfo **pinfo)
164 ADS_STRUCT *ads = NULL;
165 const char *attrs[] = { "*", NULL };
166 int i, count;
167 ADS_STATUS rc;
168 LDAPMessage *res = NULL;
169 LDAPMessage *msg = NULL;
170 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
172 *num_entries = 0;
174 DEBUG(3,("ads: query_user_list\n"));
176 if ( !winbindd_can_contact_domain( domain ) ) {
177 DEBUG(10,("query_user_list: No incoming trust for domain %s\n",
178 domain->name));
179 return NT_STATUS_OK;
182 ads = ads_cached_connection(domain);
184 if (!ads) {
185 domain->last_status = NT_STATUS_SERVER_DISABLED;
186 goto done;
189 rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
190 if (!ADS_ERR_OK(rc) || !res) {
191 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
192 goto done;
195 count = ads_count_replies(ads, res);
196 if (count == 0) {
197 DEBUG(1,("query_user_list: No users found\n"));
198 goto done;
201 (*pinfo) = TALLOC_ZERO_ARRAY(mem_ctx, struct wbint_userinfo, count);
202 if (!*pinfo) {
203 status = NT_STATUS_NO_MEMORY;
204 goto done;
207 count = 0;
209 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
210 struct wbint_userinfo *info = &((*pinfo)[count]);
211 uint32 group;
212 uint32 atype;
214 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
215 ds_atype_map(atype) != SID_NAME_USER) {
216 DEBUG(1,("Not a user account? atype=0x%x\n", atype));
217 continue;
220 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
221 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
222 info->homedir = NULL;
223 info->shell = NULL;
224 info->primary_gid = (gid_t)-1;
226 if (!ads_pull_sid(ads, msg, "objectSid",
227 &info->user_sid)) {
228 DEBUG(1, ("No sid for %s !?\n", info->acct_name));
229 continue;
232 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
233 DEBUG(1, ("No primary group for %s !?\n",
234 info->acct_name));
235 continue;
237 sid_compose(&info->group_sid, &domain->sid, group);
239 count += 1;
242 (*num_entries) = count;
243 ads_msgfree(ads, res);
245 for (i=0; i<count; i++) {
246 struct wbint_userinfo *info = &((*pinfo)[i]);
247 const char *gecos = NULL;
248 gid_t primary_gid = (gid_t)-1;
250 status = nss_get_info_cached(domain, &info->user_sid, mem_ctx,
251 &info->homedir, &info->shell,
252 &gecos, &primary_gid);
253 if (!NT_STATUS_IS_OK(status)) {
255 * Deliberately ignore this error, there might be more
256 * users to fill
258 continue;
261 if (gecos != NULL) {
262 info->full_name = gecos;
264 info->primary_gid = primary_gid;
267 status = NT_STATUS_OK;
269 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
271 done:
272 return status;
275 /* list all domain groups */
276 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
277 TALLOC_CTX *mem_ctx,
278 uint32 *num_entries,
279 struct wb_acct_info **info)
281 ADS_STRUCT *ads = NULL;
282 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
283 "name", "objectSid", NULL};
284 int i, count;
285 ADS_STATUS rc;
286 LDAPMessage *res = NULL;
287 LDAPMessage *msg = NULL;
288 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
289 const char *filter;
290 bool enum_dom_local_groups = False;
292 *num_entries = 0;
294 DEBUG(3,("ads: enum_dom_groups\n"));
296 if ( !winbindd_can_contact_domain( domain ) ) {
297 DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n",
298 domain->name));
299 return NT_STATUS_OK;
302 /* only grab domain local groups for our domain */
303 if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) {
304 enum_dom_local_groups = True;
307 /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
308 * rollup-fixes:
310 * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
311 * default value, it MUST be absent. In case of extensible matching the
312 * "dnattr" boolean defaults to FALSE and so it must be only be present
313 * when set to TRUE.
315 * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
316 * filter using bitwise matching rule then the buggy AD fails to decode
317 * the extensible match. As a workaround set it to TRUE and thereby add
318 * the dnAttributes "dn" field to cope with those older AD versions.
319 * It should not harm and won't put any additional load on the AD since
320 * none of the dn components have a bitmask-attribute.
322 * Thanks to Ralf Haferkamp for input and testing - Guenther */
324 filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))",
325 ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
326 ADS_LDAP_MATCHING_RULE_BIT_AND,
327 enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
329 if (filter == NULL) {
330 status = NT_STATUS_NO_MEMORY;
331 goto done;
334 ads = ads_cached_connection(domain);
336 if (!ads) {
337 domain->last_status = NT_STATUS_SERVER_DISABLED;
338 goto done;
341 rc = ads_search_retry(ads, &res, filter, attrs);
342 if (!ADS_ERR_OK(rc) || !res) {
343 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
344 goto done;
347 count = ads_count_replies(ads, res);
348 if (count == 0) {
349 DEBUG(1,("enum_dom_groups: No groups found\n"));
350 goto done;
353 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct wb_acct_info, count);
354 if (!*info) {
355 status = NT_STATUS_NO_MEMORY;
356 goto done;
359 i = 0;
361 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
362 char *name, *gecos;
363 struct dom_sid sid;
364 uint32 rid;
366 name = ads_pull_username(ads, mem_ctx, msg);
367 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
368 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
369 DEBUG(1,("No sid for %s !?\n", name));
370 continue;
373 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
374 DEBUG(1,("No rid for %s !?\n", name));
375 continue;
378 fstrcpy((*info)[i].acct_name, name);
379 fstrcpy((*info)[i].acct_desc, gecos);
380 (*info)[i].rid = rid;
381 i++;
384 (*num_entries) = i;
386 status = NT_STATUS_OK;
388 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
390 done:
391 if (res)
392 ads_msgfree(ads, res);
394 return status;
397 /* list all domain local groups */
398 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
399 TALLOC_CTX *mem_ctx,
400 uint32 *num_entries,
401 struct wb_acct_info **info)
404 * This is a stub function only as we returned the domain
405 * local groups in enum_dom_groups() if the domain->native field
406 * was true. This is a simple performance optimization when
407 * using LDAP.
409 * if we ever need to enumerate domain local groups separately,
410 * then this optimization in enum_dom_groups() will need
411 * to be split out
413 *num_entries = 0;
415 return NT_STATUS_OK;
418 /* convert a single name to a sid in a domain - use rpc methods */
419 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
420 TALLOC_CTX *mem_ctx,
421 const char *domain_name,
422 const char *name,
423 uint32_t flags,
424 struct dom_sid *sid,
425 enum lsa_SidType *type)
427 return reconnect_methods.name_to_sid(domain, mem_ctx,
428 domain_name, name, flags,
429 sid, type);
432 /* convert a domain SID to a user or group name - use rpc methods */
433 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
434 TALLOC_CTX *mem_ctx,
435 const struct dom_sid *sid,
436 char **domain_name,
437 char **name,
438 enum lsa_SidType *type)
440 return reconnect_methods.sid_to_name(domain, mem_ctx, sid,
441 domain_name, name, type);
444 /* convert a list of rids to names - use rpc methods */
445 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
446 TALLOC_CTX *mem_ctx,
447 const struct dom_sid *sid,
448 uint32 *rids,
449 size_t num_rids,
450 char **domain_name,
451 char ***names,
452 enum lsa_SidType **types)
454 return reconnect_methods.rids_to_names(domain, mem_ctx, sid,
455 rids, num_rids,
456 domain_name, names, types);
459 /* If you are looking for "dn_lookup": Yes, it used to be here!
460 * It has gone now since it was a major speed bottleneck in
461 * lookup_groupmem (its only use). It has been replaced by
462 * an rpc lookup sids call... R.I.P. */
464 /* Lookup user information from a rid */
465 static NTSTATUS query_user(struct winbindd_domain *domain,
466 TALLOC_CTX *mem_ctx,
467 const struct dom_sid *sid,
468 struct wbint_userinfo *info)
470 ADS_STRUCT *ads = NULL;
471 const char *attrs[] = { "*", NULL };
472 ADS_STATUS rc;
473 int count;
474 LDAPMessage *msg = NULL;
475 char *ldap_exp;
476 char *sidstr;
477 uint32 group_rid;
478 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
479 struct netr_SamInfo3 *user = NULL;
480 gid_t gid = -1;
481 int ret;
482 char *ads_name;
484 DEBUG(3,("ads: query_user\n"));
486 info->homedir = NULL;
487 info->shell = NULL;
489 /* try netsamlogon cache first */
491 if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL )
493 DEBUG(5,("query_user: Cache lookup succeeded for %s\n",
494 sid_string_dbg(sid)));
496 sid_compose(&info->user_sid, &domain->sid, user->base.rid);
497 sid_compose(&info->group_sid, &domain->sid, user->base.primary_gid);
499 info->acct_name = talloc_strdup(mem_ctx, user->base.account_name.string);
500 info->full_name = talloc_strdup(mem_ctx, user->base.full_name.string);
502 nss_get_info_cached( domain, sid, mem_ctx,
503 &info->homedir, &info->shell, &info->full_name,
504 &gid );
505 info->primary_gid = gid;
507 TALLOC_FREE(user);
509 return NT_STATUS_OK;
512 if ( !winbindd_can_contact_domain(domain)) {
513 DEBUG(8,("query_user: No incoming trust from domain %s\n",
514 domain->name));
516 /* We still need to generate some basic information
517 about the user even if we cannot contact the
518 domain. Most of this stuff we can deduce. */
520 sid_copy( &info->user_sid, sid );
522 /* Assume "Domain Users" for the primary group */
524 sid_compose(&info->group_sid, &domain->sid, DOMAIN_RID_USERS );
526 /* Try to fill in what the nss_info backend can do */
528 nss_get_info_cached( domain, sid, mem_ctx,
529 &info->homedir, &info->shell, &info->full_name,
530 &gid);
531 info->primary_gid = gid;
533 return NT_STATUS_OK;
536 /* no cache...do the query */
538 if ( (ads = ads_cached_connection(domain)) == NULL ) {
539 domain->last_status = NT_STATUS_SERVER_DISABLED;
540 return NT_STATUS_SERVER_DISABLED;
543 sidstr = ldap_encode_ndr_dom_sid(talloc_tos(), sid);
545 ret = asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
546 TALLOC_FREE(sidstr);
547 if (ret == -1) {
548 return NT_STATUS_NO_MEMORY;
550 rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
551 SAFE_FREE(ldap_exp);
552 if (!ADS_ERR_OK(rc) || !msg) {
553 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
554 sid_string_dbg(sid), ads_errstr(rc)));
555 return ads_ntstatus(rc);
558 count = ads_count_replies(ads, msg);
559 if (count != 1) {
560 DEBUG(1,("query_user(sid=%s): Not found\n",
561 sid_string_dbg(sid)));
562 ads_msgfree(ads, msg);
563 return NT_STATUS_NO_SUCH_USER;
566 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
568 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
569 DEBUG(1,("No primary group for %s !?\n",
570 sid_string_dbg(sid)));
571 ads_msgfree(ads, msg);
572 return NT_STATUS_NO_SUCH_USER;
574 sid_copy(&info->user_sid, sid);
575 sid_compose(&info->group_sid, &domain->sid, group_rid);
578 * We have to fetch the "name" attribute before doing the
579 * nss_get_info_cached call. nss_get_info_cached might destroy
580 * the ads struct, potentially invalidating the ldap message.
582 ads_name = ads_pull_string(ads, mem_ctx, msg, "name");
584 ads_msgfree(ads, msg);
585 msg = NULL;
587 status = nss_get_info_cached( domain, sid, mem_ctx,
588 &info->homedir, &info->shell, &info->full_name,
589 &gid);
590 info->primary_gid = gid;
591 if (!NT_STATUS_IS_OK(status)) {
592 DEBUG(1, ("nss_get_info_cached failed: %s\n",
593 nt_errstr(status)));
594 return status;
597 if (info->full_name == NULL) {
598 info->full_name = ads_name;
599 } else {
600 TALLOC_FREE(ads_name);
603 status = NT_STATUS_OK;
605 DEBUG(3,("ads query_user gave %s\n", info->acct_name));
606 return NT_STATUS_OK;
609 /* Lookup groups a user is a member of - alternate method, for when
610 tokenGroups are not available. */
611 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
612 TALLOC_CTX *mem_ctx,
613 const char *user_dn,
614 struct dom_sid *primary_group,
615 uint32_t *p_num_groups, struct dom_sid **user_sids)
617 ADS_STATUS rc;
618 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
619 int count;
620 LDAPMessage *res = NULL;
621 LDAPMessage *msg = NULL;
622 char *ldap_exp;
623 ADS_STRUCT *ads;
624 const char *group_attrs[] = {"objectSid", NULL};
625 char *escaped_dn;
626 uint32_t num_groups = 0;
628 DEBUG(3,("ads: lookup_usergroups_member\n"));
630 if ( !winbindd_can_contact_domain( domain ) ) {
631 DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
632 domain->name));
633 return NT_STATUS_OK;
636 ads = ads_cached_connection(domain);
638 if (!ads) {
639 domain->last_status = NT_STATUS_SERVER_DISABLED;
640 goto done;
643 if (!(escaped_dn = escape_ldap_string(talloc_tos(), user_dn))) {
644 status = NT_STATUS_NO_MEMORY;
645 goto done;
648 ldap_exp = talloc_asprintf(mem_ctx,
649 "(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))",
650 escaped_dn,
651 ADS_LDAP_MATCHING_RULE_BIT_AND,
652 GROUP_TYPE_SECURITY_ENABLED);
653 if (!ldap_exp) {
654 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
655 TALLOC_FREE(escaped_dn);
656 status = NT_STATUS_NO_MEMORY;
657 goto done;
660 TALLOC_FREE(escaped_dn);
662 rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
664 if (!ADS_ERR_OK(rc) || !res) {
665 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
666 return ads_ntstatus(rc);
669 count = ads_count_replies(ads, res);
671 *user_sids = NULL;
672 num_groups = 0;
674 /* always add the primary group to the sid array */
675 status = add_sid_to_array(mem_ctx, primary_group, user_sids,
676 &num_groups);
677 if (!NT_STATUS_IS_OK(status)) {
678 goto done;
681 if (count > 0) {
682 for (msg = ads_first_entry(ads, res); msg;
683 msg = ads_next_entry(ads, msg)) {
684 struct dom_sid group_sid;
686 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
687 DEBUG(1,("No sid for this group ?!?\n"));
688 continue;
691 /* ignore Builtin groups from ADS - Guenther */
692 if (sid_check_is_in_builtin(&group_sid)) {
693 continue;
696 status = add_sid_to_array(mem_ctx, &group_sid,
697 user_sids, &num_groups);
698 if (!NT_STATUS_IS_OK(status)) {
699 goto done;
705 *p_num_groups = num_groups;
706 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
708 DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
709 done:
710 if (res)
711 ads_msgfree(ads, res);
713 return status;
716 /* Lookup groups a user is a member of - alternate method, for when
717 tokenGroups are not available. */
718 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
719 TALLOC_CTX *mem_ctx,
720 const char *user_dn,
721 struct dom_sid *primary_group,
722 uint32_t *p_num_groups,
723 struct dom_sid **user_sids)
725 ADS_STATUS rc;
726 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
727 ADS_STRUCT *ads;
728 const char *attrs[] = {"memberOf", NULL};
729 uint32_t num_groups = 0;
730 struct dom_sid *group_sids = NULL;
731 int i;
732 char **strings = NULL;
733 size_t num_strings = 0, num_sids = 0;
736 DEBUG(3,("ads: lookup_usergroups_memberof\n"));
738 if ( !winbindd_can_contact_domain( domain ) ) {
739 DEBUG(10,("lookup_usergroups_memberof: No incoming trust for "
740 "domain %s\n", domain->name));
741 return NT_STATUS_OK;
744 ads = ads_cached_connection(domain);
746 if (!ads) {
747 domain->last_status = NT_STATUS_SERVER_DISABLED;
748 return NT_STATUS_UNSUCCESSFUL;
751 rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
752 ADS_EXTENDED_DN_HEX_STRING,
753 &strings, &num_strings);
755 if (!ADS_ERR_OK(rc)) {
756 DEBUG(1,("lookup_usergroups_memberof ads_search "
757 "member=%s: %s\n", user_dn, ads_errstr(rc)));
758 return ads_ntstatus(rc);
761 *user_sids = NULL;
762 num_groups = 0;
764 /* always add the primary group to the sid array */
765 status = add_sid_to_array(mem_ctx, primary_group, user_sids,
766 &num_groups);
767 if (!NT_STATUS_IS_OK(status)) {
768 goto done;
771 group_sids = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, num_strings + 1);
772 if (!group_sids) {
773 status = NT_STATUS_NO_MEMORY;
774 goto done;
777 for (i=0; i<num_strings; i++) {
778 rc = ads_get_sid_from_extended_dn(mem_ctx, strings[i],
779 ADS_EXTENDED_DN_HEX_STRING,
780 &(group_sids)[i]);
781 if (!ADS_ERR_OK(rc)) {
782 /* ignore members without SIDs */
783 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
784 NT_STATUS_NOT_FOUND)) {
785 continue;
787 else {
788 status = ads_ntstatus(rc);
789 goto done;
792 num_sids++;
795 if (i == 0) {
796 DEBUG(1,("No memberOf for this user?!?\n"));
797 status = NT_STATUS_NO_MEMORY;
798 goto done;
801 for (i=0; i<num_sids; i++) {
803 /* ignore Builtin groups from ADS - Guenther */
804 if (sid_check_is_in_builtin(&group_sids[i])) {
805 continue;
808 status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
809 &num_groups);
810 if (!NT_STATUS_IS_OK(status)) {
811 goto done;
816 *p_num_groups = num_groups;
817 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
819 DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n",
820 user_dn));
822 done:
823 TALLOC_FREE(strings);
824 TALLOC_FREE(group_sids);
826 return status;
830 /* Lookup groups a user is a member of. */
831 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
832 TALLOC_CTX *mem_ctx,
833 const struct dom_sid *sid,
834 uint32 *p_num_groups, struct dom_sid **user_sids)
836 ADS_STRUCT *ads = NULL;
837 const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
838 ADS_STATUS rc;
839 int count;
840 LDAPMessage *msg = NULL;
841 char *user_dn = NULL;
842 struct dom_sid *sids;
843 int i;
844 struct dom_sid primary_group;
845 uint32 primary_group_rid;
846 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
847 uint32_t num_groups = 0;
849 DEBUG(3,("ads: lookup_usergroups\n"));
850 *p_num_groups = 0;
852 status = lookup_usergroups_cached(domain, mem_ctx, sid,
853 p_num_groups, user_sids);
854 if (NT_STATUS_IS_OK(status)) {
855 return NT_STATUS_OK;
858 if ( !winbindd_can_contact_domain( domain ) ) {
859 DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
860 domain->name));
862 /* Tell the cache manager not to remember this one */
864 return NT_STATUS_SYNCHRONIZATION_REQUIRED;
867 ads = ads_cached_connection(domain);
869 if (!ads) {
870 domain->last_status = NT_STATUS_SERVER_DISABLED;
871 status = NT_STATUS_SERVER_DISABLED;
872 goto done;
875 rc = ads_search_retry_sid(ads, &msg, sid, attrs);
877 if (!ADS_ERR_OK(rc)) {
878 status = ads_ntstatus(rc);
879 DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
880 "%s\n", sid_string_dbg(sid), ads_errstr(rc)));
881 goto done;
884 count = ads_count_replies(ads, msg);
885 if (count != 1) {
886 status = NT_STATUS_UNSUCCESSFUL;
887 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
888 "invalid number of results (count=%d)\n",
889 sid_string_dbg(sid), count));
890 goto done;
893 if (!msg) {
894 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
895 sid_string_dbg(sid)));
896 status = NT_STATUS_UNSUCCESSFUL;
897 goto done;
900 user_dn = ads_get_dn(ads, mem_ctx, msg);
901 if (user_dn == NULL) {
902 status = NT_STATUS_NO_MEMORY;
903 goto done;
906 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
907 DEBUG(1,("%s: No primary group for sid=%s !?\n",
908 domain->name, sid_string_dbg(sid)));
909 goto done;
912 sid_compose(&primary_group, &domain->sid, primary_group_rid);
914 count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
916 /* there must always be at least one group in the token,
917 unless we are talking to a buggy Win2k server */
919 /* actually this only happens when the machine account has no read
920 * permissions on the tokenGroup attribute - gd */
922 if (count == 0) {
924 /* no tokenGroups */
926 /* lookup what groups this user is a member of by DN search on
927 * "memberOf" */
929 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
930 &primary_group,
931 &num_groups, user_sids);
932 *p_num_groups = num_groups;
933 if (NT_STATUS_IS_OK(status)) {
934 goto done;
937 /* lookup what groups this user is a member of by DN search on
938 * "member" */
940 status = lookup_usergroups_member(domain, mem_ctx, user_dn,
941 &primary_group,
942 &num_groups, user_sids);
943 *p_num_groups = num_groups;
944 goto done;
947 *user_sids = NULL;
948 num_groups = 0;
950 status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
951 &num_groups);
952 if (!NT_STATUS_IS_OK(status)) {
953 goto done;
956 for (i=0;i<count;i++) {
958 /* ignore Builtin groups from ADS - Guenther */
959 if (sid_check_is_in_builtin(&sids[i])) {
960 continue;
963 status = add_sid_to_array_unique(mem_ctx, &sids[i],
964 user_sids, &num_groups);
965 if (!NT_STATUS_IS_OK(status)) {
966 goto done;
970 *p_num_groups = (uint32)num_groups;
971 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
973 DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
974 sid_string_dbg(sid)));
975 done:
976 TALLOC_FREE(user_dn);
977 ads_msgfree(ads, msg);
978 return status;
981 /* Lookup aliases a user is member of - use rpc methods */
982 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
983 TALLOC_CTX *mem_ctx,
984 uint32 num_sids, const struct dom_sid *sids,
985 uint32 *num_aliases, uint32 **alias_rids)
987 return reconnect_methods.lookup_useraliases(domain, mem_ctx,
988 num_sids, sids,
989 num_aliases,
990 alias_rids);
994 find the members of a group, given a group rid and domain
996 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
997 TALLOC_CTX *mem_ctx,
998 const struct dom_sid *group_sid,
999 enum lsa_SidType type,
1000 uint32 *num_names,
1001 struct dom_sid **sid_mem, char ***names,
1002 uint32 **name_types)
1004 ADS_STATUS rc;
1005 ADS_STRUCT *ads = NULL;
1006 char *ldap_exp;
1007 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1008 char *sidbinstr;
1009 char **members = NULL;
1010 int i;
1011 size_t num_members = 0;
1012 ads_control args;
1013 struct dom_sid *sid_mem_nocache = NULL;
1014 char **names_nocache = NULL;
1015 enum lsa_SidType *name_types_nocache = NULL;
1016 char **domains_nocache = NULL; /* only needed for rpccli_lsa_lookup_sids */
1017 uint32 num_nocache = 0;
1018 TALLOC_CTX *tmp_ctx = NULL;
1020 DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
1021 sid_string_dbg(group_sid)));
1023 *num_names = 0;
1025 tmp_ctx = talloc_new(mem_ctx);
1026 if (!tmp_ctx) {
1027 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1028 status = NT_STATUS_NO_MEMORY;
1029 goto done;
1032 if ( !winbindd_can_contact_domain( domain ) ) {
1033 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
1034 domain->name));
1035 return NT_STATUS_OK;
1038 ads = ads_cached_connection(domain);
1040 if (!ads) {
1041 domain->last_status = NT_STATUS_SERVER_DISABLED;
1042 goto done;
1045 if ((sidbinstr = ldap_encode_ndr_dom_sid(talloc_tos(), group_sid)) == NULL) {
1046 status = NT_STATUS_NO_MEMORY;
1047 goto done;
1050 /* search for all members of the group */
1051 ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", sidbinstr);
1052 TALLOC_FREE(sidbinstr);
1053 if (ldap_exp == NULL) {
1054 DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
1055 status = NT_STATUS_NO_MEMORY;
1056 goto done;
1059 args.control = ADS_EXTENDED_DN_OID;
1060 args.val = ADS_EXTENDED_DN_HEX_STRING;
1061 args.critical = True;
1063 rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
1064 ldap_exp, &args, "member", &members, &num_members);
1066 if (!ADS_ERR_OK(rc)) {
1067 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
1068 status = NT_STATUS_UNSUCCESSFUL;
1069 goto done;
1072 DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
1074 /* Now that we have a list of sids, we need to get the
1075 * lists of names and name_types belonging to these sids.
1076 * even though conceptually not quite clean, we use the
1077 * RPC call lsa_lookup_sids for this since it can handle a
1078 * list of sids. ldap calls can just resolve one sid at a time.
1080 * At this stage, the sids are still hidden in the exetended dn
1081 * member output format. We actually do a little better than
1082 * stated above: In extracting the sids from the member strings,
1083 * we try to resolve as many sids as possible from the
1084 * cache. Only the rest is passed to the lsa_lookup_sids call. */
1086 if (num_members) {
1087 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, num_members);
1088 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
1089 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
1090 (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, struct dom_sid, num_members);
1092 if ((members == NULL) || (*sid_mem == NULL) ||
1093 (*names == NULL) || (*name_types == NULL) ||
1094 (sid_mem_nocache == NULL))
1096 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1097 status = NT_STATUS_NO_MEMORY;
1098 goto done;
1101 else {
1102 (*sid_mem) = NULL;
1103 (*names) = NULL;
1104 (*name_types) = NULL;
1107 for (i=0; i<num_members; i++) {
1108 enum lsa_SidType name_type;
1109 char *name, *domain_name;
1110 struct dom_sid sid;
1112 rc = ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val,
1113 &sid);
1114 if (!ADS_ERR_OK(rc)) {
1115 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
1116 NT_STATUS_NOT_FOUND)) {
1117 /* Group members can be objects, like Exchange
1118 * Public Folders, that don't have a SID. Skip
1119 * them. */
1120 continue;
1122 else {
1123 status = ads_ntstatus(rc);
1124 goto done;
1127 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name,
1128 &name_type)) {
1129 DEBUG(10,("ads: lookup_groupmem: got sid %s from "
1130 "cache\n", sid_string_dbg(&sid)));
1131 sid_copy(&(*sid_mem)[*num_names], &sid);
1132 (*names)[*num_names] = fill_domain_username_talloc(
1133 *names,
1134 domain_name,
1135 name,
1136 true);
1138 (*name_types)[*num_names] = name_type;
1139 (*num_names)++;
1141 else {
1142 DEBUG(10, ("ads: lookup_groupmem: sid %s not found in "
1143 "cache\n", sid_string_dbg(&sid)));
1144 sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
1145 num_nocache++;
1149 DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
1150 "%d left for lsa_lookupsids\n", *num_names, num_nocache));
1152 /* handle sids not resolved from cache by lsa_lookup_sids */
1153 if (num_nocache > 0) {
1155 status = winbindd_lookup_sids(tmp_ctx,
1156 domain,
1157 num_nocache,
1158 sid_mem_nocache,
1159 &domains_nocache,
1160 &names_nocache,
1161 &name_types_nocache);
1163 if (!(NT_STATUS_IS_OK(status) ||
1164 NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) ||
1165 NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)))
1167 DEBUG(1, ("lsa_lookupsids call failed with %s "
1168 "- retrying...\n", nt_errstr(status)));
1170 status = winbindd_lookup_sids(tmp_ctx,
1171 domain,
1172 num_nocache,
1173 sid_mem_nocache,
1174 &domains_nocache,
1175 &names_nocache,
1176 &name_types_nocache);
1179 if (NT_STATUS_IS_OK(status) ||
1180 NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
1182 /* Copy the entries over from the "_nocache" arrays
1183 * to the result arrays, skipping the gaps the
1184 * lookup_sids call left. */
1185 for (i=0; i < num_nocache; i++) {
1186 if (((names_nocache)[i] != NULL) &&
1187 ((name_types_nocache)[i] != SID_NAME_UNKNOWN))
1189 sid_copy(&(*sid_mem)[*num_names],
1190 &sid_mem_nocache[i]);
1191 (*names)[*num_names] =
1192 fill_domain_username_talloc(
1193 *names,
1194 domains_nocache[i],
1195 names_nocache[i],
1196 true);
1197 (*name_types)[*num_names] = name_types_nocache[i];
1198 (*num_names)++;
1202 else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1203 DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
1204 "not map any SIDs at all.\n"));
1205 /* Don't handle this as an error here.
1206 * There is nothing left to do with respect to the
1207 * overall result... */
1209 else if (!NT_STATUS_IS_OK(status)) {
1210 DEBUG(10, ("lookup_groupmem: Error looking up %d "
1211 "sids via rpc_lsa_lookup_sids: %s\n",
1212 (int)num_members, nt_errstr(status)));
1213 goto done;
1217 status = NT_STATUS_OK;
1218 DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
1219 sid_string_dbg(group_sid)));
1221 done:
1223 TALLOC_FREE(tmp_ctx);
1225 return status;
1228 /* find the sequence number for a domain */
1229 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1231 ADS_STRUCT *ads = NULL;
1232 ADS_STATUS rc;
1234 DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1236 if ( !winbindd_can_contact_domain( domain ) ) {
1237 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1238 domain->name));
1239 *seq = time(NULL);
1240 return NT_STATUS_OK;
1243 *seq = DOM_SEQUENCE_NONE;
1245 ads = ads_cached_connection(domain);
1247 if (!ads) {
1248 domain->last_status = NT_STATUS_SERVER_DISABLED;
1249 return NT_STATUS_UNSUCCESSFUL;
1252 rc = ads_USN(ads, seq);
1254 if (!ADS_ERR_OK(rc)) {
1256 /* its a dead connection, destroy it */
1258 if (domain->private_data) {
1259 ads = (ADS_STRUCT *)domain->private_data;
1260 ads->is_mine = True;
1261 ads_destroy(&ads);
1262 ads_kdestroy("MEMORY:winbind_ccache");
1263 domain->private_data = NULL;
1266 return ads_ntstatus(rc);
1269 /* find the lockout policy of a domain - use rpc methods */
1270 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1271 TALLOC_CTX *mem_ctx,
1272 struct samr_DomInfo12 *policy)
1274 return reconnect_methods.lockout_policy(domain, mem_ctx, policy);
1277 /* find the password policy of a domain - use rpc methods */
1278 static NTSTATUS password_policy(struct winbindd_domain *domain,
1279 TALLOC_CTX *mem_ctx,
1280 struct samr_DomInfo1 *policy)
1282 return reconnect_methods.password_policy(domain, mem_ctx, policy);
1285 /* get a list of trusted domains */
1286 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1287 TALLOC_CTX *mem_ctx,
1288 struct netr_DomainTrustList *trusts)
1290 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1291 WERROR werr;
1292 int i;
1293 uint32 flags;
1294 struct rpc_pipe_client *cli;
1295 int ret_count;
1296 struct dcerpc_binding_handle *b;
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 b = cli->binding_handle;
1325 result = dcerpc_netr_DsrEnumerateDomainTrusts(b, mem_ctx,
1326 cli->desthost,
1327 flags,
1328 trusts,
1329 &werr);
1330 if (!NT_STATUS_IS_OK(result)) {
1331 return result;
1334 if (!W_ERROR_IS_OK(werr)) {
1335 return werror_to_ntstatus(werr);
1337 if (trusts->count == 0) {
1338 return NT_STATUS_OK;
1341 /* Copy across names and sids */
1343 ret_count = 0;
1344 for (i = 0; i < trusts->count; i++) {
1345 struct netr_DomainTrust *trust = &trusts->array[i];
1346 struct winbindd_domain d;
1348 ZERO_STRUCT(d);
1351 * drop external trusts if this is not our primary
1352 * domain. This means that the returned number of
1353 * domains may be less that the ones actually trusted
1354 * by the DC.
1357 if ((trust->trust_attributes
1358 == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) &&
1359 !domain->primary )
1361 DEBUG(10,("trusted_domains: Skipping external trusted "
1362 "domain %s because it is outside of our "
1363 "primary domain\n",
1364 trust->netbios_name));
1365 continue;
1368 /* add to the trusted domain cache */
1370 fstrcpy(d.name, trust->netbios_name);
1371 fstrcpy(d.alt_name, trust->dns_name);
1372 if (trust->sid) {
1373 sid_copy(&d.sid, trust->sid);
1374 } else {
1375 sid_copy(&d.sid, &global_sid_NULL);
1378 if ( domain->primary ) {
1379 DEBUG(10,("trusted_domains(ads): Searching "
1380 "trusted domain list of %s and storing "
1381 "trust flags for domain %s\n",
1382 domain->name, d.alt_name));
1384 d.domain_flags = trust->trust_flags;
1385 d.domain_type = trust->trust_type;
1386 d.domain_trust_attribs = trust->trust_attributes;
1388 wcache_tdc_add_domain( &d );
1389 ret_count++;
1390 } else if (domain_is_forest_root(domain)) {
1391 /* Check if we already have this record. If
1392 * we are following our forest root that is not
1393 * our primary domain, we want to keep trust
1394 * flags from the perspective of our primary
1395 * domain not our forest root. */
1396 struct winbindd_tdc_domain *exist = NULL;
1398 exist = wcache_tdc_fetch_domain(
1399 talloc_tos(), trust->netbios_name);
1400 if (!exist) {
1401 DEBUG(10,("trusted_domains(ads): Searching "
1402 "trusted domain list of %s and "
1403 "storing trust flags for domain "
1404 "%s\n", domain->name, d.alt_name));
1405 d.domain_flags = trust->trust_flags;
1406 d.domain_type = trust->trust_type;
1407 d.domain_trust_attribs =
1408 trust->trust_attributes;
1410 wcache_tdc_add_domain( &d );
1411 ret_count++;
1413 TALLOC_FREE(exist);
1414 } else {
1415 /* This gets a little tricky. If we are
1416 following a transitive forest trust, then
1417 innerit the flags, type, and attribs from
1418 the domain we queried to make sure we don't
1419 record the view of the trust from the wrong
1420 side. Always view it from the side of our
1421 primary domain. --jerry */
1422 struct winbindd_tdc_domain *parent = NULL;
1424 DEBUG(10,("trusted_domains(ads): Searching "
1425 "trusted domain list of %s and inheriting "
1426 "trust flags for domain %s\n",
1427 domain->name, d.alt_name));
1429 parent = wcache_tdc_fetch_domain(talloc_tos(),
1430 domain->name);
1431 if (parent) {
1432 d.domain_flags = parent->trust_flags;
1433 d.domain_type = parent->trust_type;
1434 d.domain_trust_attribs = parent->trust_attribs;
1435 } else {
1436 d.domain_flags = domain->domain_flags;
1437 d.domain_type = domain->domain_type;
1438 d.domain_trust_attribs =
1439 domain->domain_trust_attribs;
1441 TALLOC_FREE(parent);
1443 wcache_tdc_add_domain( &d );
1444 ret_count++;
1447 return result;
1450 /* the ADS backend methods are exposed via this structure */
1451 struct winbindd_methods ads_methods = {
1452 True,
1453 query_user_list,
1454 enum_dom_groups,
1455 enum_local_groups,
1456 name_to_sid,
1457 sid_to_name,
1458 rids_to_names,
1459 query_user,
1460 lookup_usergroups,
1461 lookup_useraliases,
1462 lookup_groupmem,
1463 sequence_number,
1464 lockout_policy,
1465 password_policy,
1466 trusted_domains,
1469 #endif