s3: Fix Coverity ID 2100, BAD_SIZEOF
[Samba.git] / source3 / winbindd / winbindd_ads.c
blob116f87315f013dac1353c00d492bc4b77d87b882
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"
34 #ifdef HAVE_ADS
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_WINBIND
39 extern struct winbindd_methods reconnect_methods;
42 return our ads connections structure for a domain. We keep the connection
43 open to make things faster
45 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
47 ADS_STRUCT *ads;
48 ADS_STATUS status;
49 fstring dc_name;
50 struct sockaddr_storage dc_ss;
52 DEBUG(10,("ads_cached_connection\n"));
54 if (domain->private_data) {
56 time_t expire;
57 time_t now = time(NULL);
59 /* check for a valid structure */
60 ads = (ADS_STRUCT *)domain->private_data;
62 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
64 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
65 (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
67 if ( ads->config.realm && (expire > now)) {
68 return ads;
69 } else {
70 /* we own this ADS_STRUCT so make sure it goes away */
71 DEBUG(7,("Deleting expired krb5 credential cache\n"));
72 ads->is_mine = True;
73 ads_destroy( &ads );
74 ads_kdestroy("MEMORY:winbind_ccache");
75 domain->private_data = NULL;
79 /* we don't want this to affect the users ccache */
80 setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
82 ads = ads_init(domain->alt_name, domain->name, NULL);
83 if (!ads) {
84 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
85 return NULL;
88 /* the machine acct password might have change - fetch it every time */
90 SAFE_FREE(ads->auth.password);
91 SAFE_FREE(ads->auth.realm);
93 if ( IS_DC ) {
95 if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, NULL, NULL ) ) {
96 ads_destroy( &ads );
97 return NULL;
99 ads->auth.realm = SMB_STRDUP( ads->server.realm );
100 strupper_m( ads->auth.realm );
102 else {
103 struct winbindd_domain *our_domain = domain;
105 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
107 /* always give preference to the alt_name in our
108 primary domain if possible */
110 if ( !domain->primary )
111 our_domain = find_our_domain();
113 if ( our_domain->alt_name[0] != '\0' ) {
114 ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
115 strupper_m( ads->auth.realm );
117 else
118 ads->auth.realm = SMB_STRDUP( lp_realm() );
121 ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
123 /* Setup the server affinity cache. We don't reaally care
124 about the name. Just setup affinity and the KRB5_CONFIG
125 file. */
127 get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ss );
129 status = ads_connect(ads);
130 if (!ADS_ERR_OK(status) || !ads->config.realm) {
131 DEBUG(1,("ads_connect for domain %s failed: %s\n",
132 domain->name, ads_errstr(status)));
133 ads_destroy(&ads);
135 /* if we get ECONNREFUSED then it might be a NT4
136 server, fall back to MSRPC */
137 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
138 status.err.rc == ECONNREFUSED) {
139 /* 'reconnect_methods' is the MS-RPC backend. */
140 DEBUG(1,("Trying MSRPC methods\n"));
141 domain->backend = &reconnect_methods;
143 return NULL;
146 /* set the flag that says we don't own the memory even
147 though we do so that ads_destroy() won't destroy the
148 structure we pass back by reference */
150 ads->is_mine = False;
152 domain->private_data = (void *)ads;
153 return ads;
157 /* Query display info for a realm. This is the basic user list fn */
158 static NTSTATUS query_user_list(struct winbindd_domain *domain,
159 TALLOC_CTX *mem_ctx,
160 uint32 *num_entries,
161 struct wbint_userinfo **pinfo)
163 ADS_STRUCT *ads = NULL;
164 const char *attrs[] = { "*", NULL };
165 int i, count;
166 ADS_STATUS rc;
167 LDAPMessage *res = NULL;
168 LDAPMessage *msg = NULL;
169 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
171 *num_entries = 0;
173 DEBUG(3,("ads: query_user_list\n"));
175 if ( !winbindd_can_contact_domain( domain ) ) {
176 DEBUG(10,("query_user_list: No incoming trust for domain %s\n",
177 domain->name));
178 return NT_STATUS_OK;
181 ads = ads_cached_connection(domain);
183 if (!ads) {
184 domain->last_status = NT_STATUS_SERVER_DISABLED;
185 goto done;
188 rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
189 if (!ADS_ERR_OK(rc) || !res) {
190 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
191 goto done;
194 count = ads_count_replies(ads, res);
195 if (count == 0) {
196 DEBUG(1,("query_user_list: No users found\n"));
197 goto done;
200 (*pinfo) = TALLOC_ZERO_ARRAY(mem_ctx, struct wbint_userinfo, count);
201 if (!*pinfo) {
202 status = NT_STATUS_NO_MEMORY;
203 goto done;
206 count = 0;
208 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
209 struct wbint_userinfo *info = &((*pinfo)[count]);
210 uint32 group;
211 uint32 atype;
213 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
214 ds_atype_map(atype) != SID_NAME_USER) {
215 DEBUG(1,("Not a user account? atype=0x%x\n", atype));
216 continue;
219 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
220 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
221 info->homedir = NULL;
222 info->shell = NULL;
223 info->primary_gid = (gid_t)-1;
225 if (!ads_pull_sid(ads, msg, "objectSid",
226 &info->user_sid)) {
227 DEBUG(1, ("No sid for %s !?\n", info->acct_name));
228 continue;
231 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
232 DEBUG(1, ("No primary group for %s !?\n",
233 info->acct_name));
234 continue;
236 sid_compose(&info->group_sid, &domain->sid, group);
238 count += 1;
241 (*num_entries) = count;
242 ads_msgfree(ads, res);
244 for (i=0; i<count; i++) {
245 struct wbint_userinfo *info = &((*pinfo)[i]);
246 const char *gecos = NULL;
247 gid_t primary_gid = (gid_t)-1;
249 status = nss_get_info_cached(domain, &info->user_sid, mem_ctx,
250 &info->homedir, &info->shell,
251 &gecos, &primary_gid);
252 if (!NT_STATUS_IS_OK(status)) {
254 * Deliberately ignore this error, there might be more
255 * users to fill
257 continue;
260 if (gecos != NULL) {
261 info->full_name = gecos;
263 info->primary_gid = primary_gid;
266 status = NT_STATUS_OK;
268 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
270 done:
271 return status;
274 /* list all domain groups */
275 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
276 TALLOC_CTX *mem_ctx,
277 uint32 *num_entries,
278 struct acct_info **info)
280 ADS_STRUCT *ads = NULL;
281 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
282 "name", "objectSid", NULL};
283 int i, count;
284 ADS_STATUS rc;
285 LDAPMessage *res = NULL;
286 LDAPMessage *msg = NULL;
287 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
288 const char *filter;
289 bool enum_dom_local_groups = False;
291 *num_entries = 0;
293 DEBUG(3,("ads: enum_dom_groups\n"));
295 if ( !winbindd_can_contact_domain( domain ) ) {
296 DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n",
297 domain->name));
298 return NT_STATUS_OK;
301 /* only grab domain local groups for our domain */
302 if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) {
303 enum_dom_local_groups = True;
306 /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
307 * rollup-fixes:
309 * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
310 * default value, it MUST be absent. In case of extensible matching the
311 * "dnattr" boolean defaults to FALSE and so it must be only be present
312 * when set to TRUE.
314 * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
315 * filter using bitwise matching rule then the buggy AD fails to decode
316 * the extensible match. As a workaround set it to TRUE and thereby add
317 * the dnAttributes "dn" field to cope with those older AD versions.
318 * It should not harm and won't put any additional load on the AD since
319 * none of the dn components have a bitmask-attribute.
321 * Thanks to Ralf Haferkamp for input and testing - Guenther */
323 filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))",
324 ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
325 ADS_LDAP_MATCHING_RULE_BIT_AND,
326 enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
328 if (filter == NULL) {
329 status = NT_STATUS_NO_MEMORY;
330 goto done;
333 ads = ads_cached_connection(domain);
335 if (!ads) {
336 domain->last_status = NT_STATUS_SERVER_DISABLED;
337 goto done;
340 rc = ads_search_retry(ads, &res, filter, attrs);
341 if (!ADS_ERR_OK(rc) || !res) {
342 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
343 goto done;
346 count = ads_count_replies(ads, res);
347 if (count == 0) {
348 DEBUG(1,("enum_dom_groups: No groups found\n"));
349 goto done;
352 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
353 if (!*info) {
354 status = NT_STATUS_NO_MEMORY;
355 goto done;
358 i = 0;
360 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
361 char *name, *gecos;
362 struct dom_sid sid;
363 uint32 rid;
365 name = ads_pull_username(ads, mem_ctx, msg);
366 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
367 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
368 DEBUG(1,("No sid for %s !?\n", name));
369 continue;
372 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
373 DEBUG(1,("No rid for %s !?\n", name));
374 continue;
377 fstrcpy((*info)[i].acct_name, name);
378 fstrcpy((*info)[i].acct_desc, gecos);
379 (*info)[i].rid = rid;
380 i++;
383 (*num_entries) = i;
385 status = NT_STATUS_OK;
387 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
389 done:
390 if (res)
391 ads_msgfree(ads, res);
393 return status;
396 /* list all domain local groups */
397 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
398 TALLOC_CTX *mem_ctx,
399 uint32 *num_entries,
400 struct acct_info **info)
403 * This is a stub function only as we returned the domain
404 * local groups in enum_dom_groups() if the domain->native field
405 * was true. This is a simple performance optimization when
406 * using LDAP.
408 * if we ever need to enumerate domain local groups separately,
409 * then this optimization in enum_dom_groups() will need
410 * to be split out
412 *num_entries = 0;
414 return NT_STATUS_OK;
417 /* convert a single name to a sid in a domain - use rpc methods */
418 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
419 TALLOC_CTX *mem_ctx,
420 const char *domain_name,
421 const char *name,
422 uint32_t flags,
423 struct dom_sid *sid,
424 enum lsa_SidType *type)
426 return reconnect_methods.name_to_sid(domain, mem_ctx,
427 domain_name, name, flags,
428 sid, type);
431 /* convert a domain SID to a user or group name - use rpc methods */
432 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
433 TALLOC_CTX *mem_ctx,
434 const struct dom_sid *sid,
435 char **domain_name,
436 char **name,
437 enum lsa_SidType *type)
439 return reconnect_methods.sid_to_name(domain, mem_ctx, sid,
440 domain_name, name, type);
443 /* convert a list of rids to names - use rpc methods */
444 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
445 TALLOC_CTX *mem_ctx,
446 const struct dom_sid *sid,
447 uint32 *rids,
448 size_t num_rids,
449 char **domain_name,
450 char ***names,
451 enum lsa_SidType **types)
453 return reconnect_methods.rids_to_names(domain, mem_ctx, sid,
454 rids, num_rids,
455 domain_name, names, types);
458 /* If you are looking for "dn_lookup": Yes, it used to be here!
459 * It has gone now since it was a major speed bottleneck in
460 * lookup_groupmem (its only use). It has been replaced by
461 * an rpc lookup sids call... R.I.P. */
463 /* Lookup user information from a rid */
464 static NTSTATUS query_user(struct winbindd_domain *domain,
465 TALLOC_CTX *mem_ctx,
466 const struct dom_sid *sid,
467 struct wbint_userinfo *info)
469 ADS_STRUCT *ads = NULL;
470 const char *attrs[] = { "*", NULL };
471 ADS_STATUS rc;
472 int count;
473 LDAPMessage *msg = NULL;
474 char *ldap_exp;
475 char *sidstr;
476 uint32 group_rid;
477 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
478 struct netr_SamInfo3 *user = NULL;
479 gid_t gid = -1;
480 int ret;
481 char *ads_name;
483 DEBUG(3,("ads: query_user\n"));
485 info->homedir = NULL;
486 info->shell = NULL;
488 /* try netsamlogon cache first */
490 if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL )
492 DEBUG(5,("query_user: Cache lookup succeeded for %s\n",
493 sid_string_dbg(sid)));
495 sid_compose(&info->user_sid, &domain->sid, user->base.rid);
496 sid_compose(&info->group_sid, &domain->sid, user->base.primary_gid);
498 info->acct_name = talloc_strdup(mem_ctx, user->base.account_name.string);
499 info->full_name = talloc_strdup(mem_ctx, user->base.full_name.string);
501 nss_get_info_cached( domain, sid, mem_ctx,
502 &info->homedir, &info->shell, &info->full_name,
503 &gid );
504 info->primary_gid = gid;
506 TALLOC_FREE(user);
508 return NT_STATUS_OK;
511 if ( !winbindd_can_contact_domain(domain)) {
512 DEBUG(8,("query_user: No incoming trust from domain %s\n",
513 domain->name));
515 /* We still need to generate some basic information
516 about the user even if we cannot contact the
517 domain. Most of this stuff we can deduce. */
519 sid_copy( &info->user_sid, sid );
521 /* Assume "Domain Users" for the primary group */
523 sid_compose(&info->group_sid, &domain->sid, DOMAIN_RID_USERS );
525 /* Try to fill in what the nss_info backend can do */
527 nss_get_info_cached( domain, sid, mem_ctx,
528 &info->homedir, &info->shell, &info->full_name,
529 &gid);
530 info->primary_gid = gid;
532 return NT_STATUS_OK;
535 /* no cache...do the query */
537 if ( (ads = ads_cached_connection(domain)) == NULL ) {
538 domain->last_status = NT_STATUS_SERVER_DISABLED;
539 return NT_STATUS_SERVER_DISABLED;
542 sidstr = ldap_encode_ndr_dom_sid(talloc_tos(), sid);
544 ret = asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
545 TALLOC_FREE(sidstr);
546 if (ret == -1) {
547 return NT_STATUS_NO_MEMORY;
549 rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
550 SAFE_FREE(ldap_exp);
551 if (!ADS_ERR_OK(rc) || !msg) {
552 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
553 sid_string_dbg(sid), ads_errstr(rc)));
554 return ads_ntstatus(rc);
557 count = ads_count_replies(ads, msg);
558 if (count != 1) {
559 DEBUG(1,("query_user(sid=%s): Not found\n",
560 sid_string_dbg(sid)));
561 ads_msgfree(ads, msg);
562 return NT_STATUS_NO_SUCH_USER;
565 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
567 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
568 DEBUG(1,("No primary group for %s !?\n",
569 sid_string_dbg(sid)));
570 ads_msgfree(ads, msg);
571 return NT_STATUS_NO_SUCH_USER;
573 sid_copy(&info->user_sid, sid);
574 sid_compose(&info->group_sid, &domain->sid, group_rid);
577 * We have to fetch the "name" attribute before doing the
578 * nss_get_info_cached call. nss_get_info_cached might destroy
579 * the ads struct, potentially invalidating the ldap message.
581 ads_name = ads_pull_string(ads, mem_ctx, msg, "name");
583 ads_msgfree(ads, msg);
584 msg = NULL;
586 status = nss_get_info_cached( domain, sid, mem_ctx,
587 &info->homedir, &info->shell, &info->full_name,
588 &gid);
589 info->primary_gid = gid;
590 if (!NT_STATUS_IS_OK(status)) {
591 DEBUG(1, ("nss_get_info_cached failed: %s\n",
592 nt_errstr(status)));
593 return status;
596 if (info->full_name == NULL) {
597 info->full_name = ads_name;
598 } else {
599 TALLOC_FREE(ads_name);
602 status = NT_STATUS_OK;
604 DEBUG(3,("ads query_user gave %s\n", info->acct_name));
605 return NT_STATUS_OK;
608 /* Lookup groups a user is a member of - alternate method, for when
609 tokenGroups are not available. */
610 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
611 TALLOC_CTX *mem_ctx,
612 const char *user_dn,
613 struct dom_sid *primary_group,
614 uint32_t *p_num_groups, struct dom_sid **user_sids)
616 ADS_STATUS rc;
617 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
618 int count;
619 LDAPMessage *res = NULL;
620 LDAPMessage *msg = NULL;
621 char *ldap_exp;
622 ADS_STRUCT *ads;
623 const char *group_attrs[] = {"objectSid", NULL};
624 char *escaped_dn;
625 uint32_t num_groups = 0;
627 DEBUG(3,("ads: lookup_usergroups_member\n"));
629 if ( !winbindd_can_contact_domain( domain ) ) {
630 DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
631 domain->name));
632 return NT_STATUS_OK;
635 ads = ads_cached_connection(domain);
637 if (!ads) {
638 domain->last_status = NT_STATUS_SERVER_DISABLED;
639 goto done;
642 if (!(escaped_dn = escape_ldap_string(talloc_tos(), user_dn))) {
643 status = NT_STATUS_NO_MEMORY;
644 goto done;
647 ldap_exp = talloc_asprintf(mem_ctx,
648 "(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))",
649 escaped_dn,
650 ADS_LDAP_MATCHING_RULE_BIT_AND,
651 GROUP_TYPE_SECURITY_ENABLED);
652 if (!ldap_exp) {
653 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
654 TALLOC_FREE(escaped_dn);
655 status = NT_STATUS_NO_MEMORY;
656 goto done;
659 TALLOC_FREE(escaped_dn);
661 rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
663 if (!ADS_ERR_OK(rc) || !res) {
664 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
665 return ads_ntstatus(rc);
668 count = ads_count_replies(ads, res);
670 *user_sids = NULL;
671 num_groups = 0;
673 /* always add the primary group to the sid array */
674 status = add_sid_to_array(mem_ctx, primary_group, user_sids,
675 &num_groups);
676 if (!NT_STATUS_IS_OK(status)) {
677 goto done;
680 if (count > 0) {
681 for (msg = ads_first_entry(ads, res); msg;
682 msg = ads_next_entry(ads, msg)) {
683 struct dom_sid group_sid;
685 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
686 DEBUG(1,("No sid for this group ?!?\n"));
687 continue;
690 /* ignore Builtin groups from ADS - Guenther */
691 if (sid_check_is_in_builtin(&group_sid)) {
692 continue;
695 status = add_sid_to_array(mem_ctx, &group_sid,
696 user_sids, &num_groups);
697 if (!NT_STATUS_IS_OK(status)) {
698 goto done;
704 *p_num_groups = num_groups;
705 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
707 DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
708 done:
709 if (res)
710 ads_msgfree(ads, res);
712 return status;
715 /* Lookup groups a user is a member of - alternate method, for when
716 tokenGroups are not available. */
717 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
718 TALLOC_CTX *mem_ctx,
719 const char *user_dn,
720 struct dom_sid *primary_group,
721 uint32_t *p_num_groups,
722 struct dom_sid **user_sids)
724 ADS_STATUS rc;
725 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
726 ADS_STRUCT *ads;
727 const char *attrs[] = {"memberOf", NULL};
728 uint32_t num_groups = 0;
729 struct dom_sid *group_sids = NULL;
730 int i;
731 char **strings = NULL;
732 size_t num_strings = 0, num_sids = 0;
735 DEBUG(3,("ads: lookup_usergroups_memberof\n"));
737 if ( !winbindd_can_contact_domain( domain ) ) {
738 DEBUG(10,("lookup_usergroups_memberof: No incoming trust for "
739 "domain %s\n", domain->name));
740 return NT_STATUS_OK;
743 ads = ads_cached_connection(domain);
745 if (!ads) {
746 domain->last_status = NT_STATUS_SERVER_DISABLED;
747 return NT_STATUS_UNSUCCESSFUL;
750 rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
751 ADS_EXTENDED_DN_HEX_STRING,
752 &strings, &num_strings);
754 if (!ADS_ERR_OK(rc)) {
755 DEBUG(1,("lookup_usergroups_memberof ads_search "
756 "member=%s: %s\n", user_dn, ads_errstr(rc)));
757 return ads_ntstatus(rc);
760 *user_sids = NULL;
761 num_groups = 0;
763 /* always add the primary group to the sid array */
764 status = add_sid_to_array(mem_ctx, primary_group, user_sids,
765 &num_groups);
766 if (!NT_STATUS_IS_OK(status)) {
767 goto done;
770 group_sids = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, num_strings + 1);
771 if (!group_sids) {
772 status = NT_STATUS_NO_MEMORY;
773 goto done;
776 for (i=0; i<num_strings; i++) {
777 rc = ads_get_sid_from_extended_dn(mem_ctx, strings[i],
778 ADS_EXTENDED_DN_HEX_STRING,
779 &(group_sids)[i]);
780 if (!ADS_ERR_OK(rc)) {
781 /* ignore members without SIDs */
782 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
783 NT_STATUS_NOT_FOUND)) {
784 continue;
786 else {
787 status = ads_ntstatus(rc);
788 goto done;
791 num_sids++;
794 if (i == 0) {
795 DEBUG(1,("No memberOf for this user?!?\n"));
796 status = NT_STATUS_NO_MEMORY;
797 goto done;
800 for (i=0; i<num_sids; i++) {
802 /* ignore Builtin groups from ADS - Guenther */
803 if (sid_check_is_in_builtin(&group_sids[i])) {
804 continue;
807 status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
808 &num_groups);
809 if (!NT_STATUS_IS_OK(status)) {
810 goto done;
815 *p_num_groups = num_groups;
816 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
818 DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n",
819 user_dn));
821 done:
822 TALLOC_FREE(strings);
823 TALLOC_FREE(group_sids);
825 return status;
829 /* Lookup groups a user is a member of. */
830 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
831 TALLOC_CTX *mem_ctx,
832 const struct dom_sid *sid,
833 uint32 *p_num_groups, struct dom_sid **user_sids)
835 ADS_STRUCT *ads = NULL;
836 const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
837 ADS_STATUS rc;
838 int count;
839 LDAPMessage *msg = NULL;
840 char *user_dn = NULL;
841 struct dom_sid *sids;
842 int i;
843 struct dom_sid primary_group;
844 uint32 primary_group_rid;
845 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
846 uint32_t num_groups = 0;
848 DEBUG(3,("ads: lookup_usergroups\n"));
849 *p_num_groups = 0;
851 status = lookup_usergroups_cached(domain, mem_ctx, sid,
852 p_num_groups, user_sids);
853 if (NT_STATUS_IS_OK(status)) {
854 return NT_STATUS_OK;
857 if ( !winbindd_can_contact_domain( domain ) ) {
858 DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
859 domain->name));
861 /* Tell the cache manager not to remember this one */
863 return NT_STATUS_SYNCHRONIZATION_REQUIRED;
866 ads = ads_cached_connection(domain);
868 if (!ads) {
869 domain->last_status = NT_STATUS_SERVER_DISABLED;
870 status = NT_STATUS_SERVER_DISABLED;
871 goto done;
874 rc = ads_search_retry_sid(ads, &msg, sid, attrs);
876 if (!ADS_ERR_OK(rc)) {
877 status = ads_ntstatus(rc);
878 DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
879 "%s\n", sid_string_dbg(sid), ads_errstr(rc)));
880 goto done;
883 count = ads_count_replies(ads, msg);
884 if (count != 1) {
885 status = NT_STATUS_UNSUCCESSFUL;
886 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
887 "invalid number of results (count=%d)\n",
888 sid_string_dbg(sid), count));
889 goto done;
892 if (!msg) {
893 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
894 sid_string_dbg(sid)));
895 status = NT_STATUS_UNSUCCESSFUL;
896 goto done;
899 user_dn = ads_get_dn(ads, mem_ctx, msg);
900 if (user_dn == NULL) {
901 status = NT_STATUS_NO_MEMORY;
902 goto done;
905 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
906 DEBUG(1,("%s: No primary group for sid=%s !?\n",
907 domain->name, sid_string_dbg(sid)));
908 goto done;
911 sid_compose(&primary_group, &domain->sid, primary_group_rid);
913 count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
915 /* there must always be at least one group in the token,
916 unless we are talking to a buggy Win2k server */
918 /* actually this only happens when the machine account has no read
919 * permissions on the tokenGroup attribute - gd */
921 if (count == 0) {
923 /* no tokenGroups */
925 /* lookup what groups this user is a member of by DN search on
926 * "memberOf" */
928 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
929 &primary_group,
930 &num_groups, user_sids);
931 *p_num_groups = num_groups;
932 if (NT_STATUS_IS_OK(status)) {
933 goto done;
936 /* lookup what groups this user is a member of by DN search on
937 * "member" */
939 status = lookup_usergroups_member(domain, mem_ctx, user_dn,
940 &primary_group,
941 &num_groups, user_sids);
942 *p_num_groups = num_groups;
943 goto done;
946 *user_sids = NULL;
947 num_groups = 0;
949 status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
950 &num_groups);
951 if (!NT_STATUS_IS_OK(status)) {
952 goto done;
955 for (i=0;i<count;i++) {
957 /* ignore Builtin groups from ADS - Guenther */
958 if (sid_check_is_in_builtin(&sids[i])) {
959 continue;
962 status = add_sid_to_array_unique(mem_ctx, &sids[i],
963 user_sids, &num_groups);
964 if (!NT_STATUS_IS_OK(status)) {
965 goto done;
969 *p_num_groups = (uint32)num_groups;
970 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
972 DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
973 sid_string_dbg(sid)));
974 done:
975 TALLOC_FREE(user_dn);
976 ads_msgfree(ads, msg);
977 return status;
980 /* Lookup aliases a user is member of - use rpc methods */
981 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
982 TALLOC_CTX *mem_ctx,
983 uint32 num_sids, const struct dom_sid *sids,
984 uint32 *num_aliases, uint32 **alias_rids)
986 return reconnect_methods.lookup_useraliases(domain, mem_ctx,
987 num_sids, sids,
988 num_aliases,
989 alias_rids);
993 find the members of a group, given a group rid and domain
995 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
996 TALLOC_CTX *mem_ctx,
997 const struct dom_sid *group_sid,
998 enum lsa_SidType type,
999 uint32 *num_names,
1000 struct dom_sid **sid_mem, char ***names,
1001 uint32 **name_types)
1003 ADS_STATUS rc;
1004 ADS_STRUCT *ads = NULL;
1005 char *ldap_exp;
1006 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1007 char *sidbinstr;
1008 char **members = NULL;
1009 int i;
1010 size_t num_members = 0;
1011 ads_control args;
1012 struct dom_sid *sid_mem_nocache = NULL;
1013 char **names_nocache = NULL;
1014 enum lsa_SidType *name_types_nocache = NULL;
1015 char **domains_nocache = NULL; /* only needed for rpccli_lsa_lookup_sids */
1016 uint32 num_nocache = 0;
1017 TALLOC_CTX *tmp_ctx = NULL;
1019 DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
1020 sid_string_dbg(group_sid)));
1022 *num_names = 0;
1024 tmp_ctx = talloc_new(mem_ctx);
1025 if (!tmp_ctx) {
1026 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1027 status = NT_STATUS_NO_MEMORY;
1028 goto done;
1031 if ( !winbindd_can_contact_domain( domain ) ) {
1032 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
1033 domain->name));
1034 return NT_STATUS_OK;
1037 ads = ads_cached_connection(domain);
1039 if (!ads) {
1040 domain->last_status = NT_STATUS_SERVER_DISABLED;
1041 goto done;
1044 if ((sidbinstr = ldap_encode_ndr_dom_sid(talloc_tos(), group_sid)) == NULL) {
1045 status = NT_STATUS_NO_MEMORY;
1046 goto done;
1049 /* search for all members of the group */
1050 ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", sidbinstr);
1051 TALLOC_FREE(sidbinstr);
1052 if (ldap_exp == NULL) {
1053 DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
1054 status = NT_STATUS_NO_MEMORY;
1055 goto done;
1058 args.control = ADS_EXTENDED_DN_OID;
1059 args.val = ADS_EXTENDED_DN_HEX_STRING;
1060 args.critical = True;
1062 rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
1063 ldap_exp, &args, "member", &members, &num_members);
1065 if (!ADS_ERR_OK(rc)) {
1066 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
1067 status = NT_STATUS_UNSUCCESSFUL;
1068 goto done;
1071 DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
1073 /* Now that we have a list of sids, we need to get the
1074 * lists of names and name_types belonging to these sids.
1075 * even though conceptually not quite clean, we use the
1076 * RPC call lsa_lookup_sids for this since it can handle a
1077 * list of sids. ldap calls can just resolve one sid at a time.
1079 * At this stage, the sids are still hidden in the exetended dn
1080 * member output format. We actually do a little better than
1081 * stated above: In extracting the sids from the member strings,
1082 * we try to resolve as many sids as possible from the
1083 * cache. Only the rest is passed to the lsa_lookup_sids call. */
1085 if (num_members) {
1086 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, num_members);
1087 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
1088 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
1089 (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, struct dom_sid, num_members);
1091 if ((members == NULL) || (*sid_mem == NULL) ||
1092 (*names == NULL) || (*name_types == NULL) ||
1093 (sid_mem_nocache == NULL))
1095 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1096 status = NT_STATUS_NO_MEMORY;
1097 goto done;
1100 else {
1101 (*sid_mem) = NULL;
1102 (*names) = NULL;
1103 (*name_types) = NULL;
1106 for (i=0; i<num_members; i++) {
1107 enum lsa_SidType name_type;
1108 char *name, *domain_name;
1109 struct dom_sid sid;
1111 rc = ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val,
1112 &sid);
1113 if (!ADS_ERR_OK(rc)) {
1114 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
1115 NT_STATUS_NOT_FOUND)) {
1116 /* Group members can be objects, like Exchange
1117 * Public Folders, that don't have a SID. Skip
1118 * them. */
1119 continue;
1121 else {
1122 status = ads_ntstatus(rc);
1123 goto done;
1126 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name,
1127 &name_type)) {
1128 DEBUG(10,("ads: lookup_groupmem: got sid %s from "
1129 "cache\n", sid_string_dbg(&sid)));
1130 sid_copy(&(*sid_mem)[*num_names], &sid);
1131 (*names)[*num_names] = fill_domain_username_talloc(
1132 *names,
1133 domain_name,
1134 name,
1135 true);
1137 (*name_types)[*num_names] = name_type;
1138 (*num_names)++;
1140 else {
1141 DEBUG(10, ("ads: lookup_groupmem: sid %s not found in "
1142 "cache\n", sid_string_dbg(&sid)));
1143 sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
1144 num_nocache++;
1148 DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
1149 "%d left for lsa_lookupsids\n", *num_names, num_nocache));
1151 /* handle sids not resolved from cache by lsa_lookup_sids */
1152 if (num_nocache > 0) {
1154 status = winbindd_lookup_sids(tmp_ctx,
1155 domain,
1156 num_nocache,
1157 sid_mem_nocache,
1158 &domains_nocache,
1159 &names_nocache,
1160 &name_types_nocache);
1162 if (!(NT_STATUS_IS_OK(status) ||
1163 NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) ||
1164 NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)))
1166 DEBUG(1, ("lsa_lookupsids call failed with %s "
1167 "- retrying...\n", nt_errstr(status)));
1169 status = winbindd_lookup_sids(tmp_ctx,
1170 domain,
1171 num_nocache,
1172 sid_mem_nocache,
1173 &domains_nocache,
1174 &names_nocache,
1175 &name_types_nocache);
1178 if (NT_STATUS_IS_OK(status) ||
1179 NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
1181 /* Copy the entries over from the "_nocache" arrays
1182 * to the result arrays, skipping the gaps the
1183 * lookup_sids call left. */
1184 for (i=0; i < num_nocache; i++) {
1185 if (((names_nocache)[i] != NULL) &&
1186 ((name_types_nocache)[i] != SID_NAME_UNKNOWN))
1188 sid_copy(&(*sid_mem)[*num_names],
1189 &sid_mem_nocache[i]);
1190 (*names)[*num_names] =
1191 fill_domain_username_talloc(
1192 *names,
1193 domains_nocache[i],
1194 names_nocache[i],
1195 true);
1196 (*name_types)[*num_names] = name_types_nocache[i];
1197 (*num_names)++;
1201 else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1202 DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
1203 "not map any SIDs at all.\n"));
1204 /* Don't handle this as an error here.
1205 * There is nothing left to do with respect to the
1206 * overall result... */
1208 else if (!NT_STATUS_IS_OK(status)) {
1209 DEBUG(10, ("lookup_groupmem: Error looking up %d "
1210 "sids via rpc_lsa_lookup_sids: %s\n",
1211 (int)num_members, nt_errstr(status)));
1212 goto done;
1216 status = NT_STATUS_OK;
1217 DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
1218 sid_string_dbg(group_sid)));
1220 done:
1222 TALLOC_FREE(tmp_ctx);
1224 return status;
1227 /* find the sequence number for a domain */
1228 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1230 ADS_STRUCT *ads = NULL;
1231 ADS_STATUS rc;
1233 DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1235 if ( !winbindd_can_contact_domain( domain ) ) {
1236 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1237 domain->name));
1238 *seq = time(NULL);
1239 return NT_STATUS_OK;
1242 *seq = DOM_SEQUENCE_NONE;
1244 ads = ads_cached_connection(domain);
1246 if (!ads) {
1247 domain->last_status = NT_STATUS_SERVER_DISABLED;
1248 return NT_STATUS_UNSUCCESSFUL;
1251 rc = ads_USN(ads, seq);
1253 if (!ADS_ERR_OK(rc)) {
1255 /* its a dead connection, destroy it */
1257 if (domain->private_data) {
1258 ads = (ADS_STRUCT *)domain->private_data;
1259 ads->is_mine = True;
1260 ads_destroy(&ads);
1261 ads_kdestroy("MEMORY:winbind_ccache");
1262 domain->private_data = NULL;
1265 return ads_ntstatus(rc);
1268 /* find the lockout policy of a domain - use rpc methods */
1269 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1270 TALLOC_CTX *mem_ctx,
1271 struct samr_DomInfo12 *policy)
1273 return reconnect_methods.lockout_policy(domain, mem_ctx, policy);
1276 /* find the password policy of a domain - use rpc methods */
1277 static NTSTATUS password_policy(struct winbindd_domain *domain,
1278 TALLOC_CTX *mem_ctx,
1279 struct samr_DomInfo1 *policy)
1281 return reconnect_methods.password_policy(domain, mem_ctx, policy);
1284 /* get a list of trusted domains */
1285 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1286 TALLOC_CTX *mem_ctx,
1287 struct netr_DomainTrustList *trusts)
1289 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1290 WERROR werr;
1291 int i;
1292 uint32 flags;
1293 struct rpc_pipe_client *cli;
1294 int ret_count;
1295 struct dcerpc_binding_handle *b;
1297 DEBUG(3,("ads: trusted_domains\n"));
1299 ZERO_STRUCTP(trusts);
1301 /* If this is our primary domain or a root in our forest,
1302 query for all trusts. If not, then just look for domain
1303 trusts in the target forest */
1305 if (domain->primary || domain_is_forest_root(domain)) {
1306 flags = NETR_TRUST_FLAG_OUTBOUND |
1307 NETR_TRUST_FLAG_INBOUND |
1308 NETR_TRUST_FLAG_IN_FOREST;
1309 } else {
1310 flags = NETR_TRUST_FLAG_IN_FOREST;
1313 result = cm_connect_netlogon(domain, &cli);
1315 if (!NT_STATUS_IS_OK(result)) {
1316 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1317 "for PIPE_NETLOGON (%s)\n",
1318 domain->name, nt_errstr(result)));
1319 return NT_STATUS_UNSUCCESSFUL;
1322 b = cli->binding_handle;
1324 result = dcerpc_netr_DsrEnumerateDomainTrusts(b, mem_ctx,
1325 cli->desthost,
1326 flags,
1327 trusts,
1328 &werr);
1329 if (!NT_STATUS_IS_OK(result)) {
1330 return result;
1333 if (!W_ERROR_IS_OK(werr)) {
1334 return werror_to_ntstatus(werr);
1336 if (trusts->count == 0) {
1337 return NT_STATUS_OK;
1340 /* Copy across names and sids */
1342 ret_count = 0;
1343 for (i = 0; i < trusts->count; i++) {
1344 struct netr_DomainTrust *trust = &trusts->array[i];
1345 struct winbindd_domain d;
1347 ZERO_STRUCT(d);
1350 * drop external trusts if this is not our primary
1351 * domain. This means that the returned number of
1352 * domains may be less that the ones actually trusted
1353 * by the DC.
1356 if ((trust->trust_attributes
1357 == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) &&
1358 !domain->primary )
1360 DEBUG(10,("trusted_domains: Skipping external trusted "
1361 "domain %s because it is outside of our "
1362 "primary domain\n",
1363 trust->netbios_name));
1364 continue;
1367 /* add to the trusted domain cache */
1369 fstrcpy(d.name, trust->netbios_name);
1370 fstrcpy(d.alt_name, trust->dns_name);
1371 if (trust->sid) {
1372 sid_copy(&d.sid, trust->sid);
1373 } else {
1374 sid_copy(&d.sid, &global_sid_NULL);
1377 if ( domain->primary ) {
1378 DEBUG(10,("trusted_domains(ads): Searching "
1379 "trusted domain list of %s and storing "
1380 "trust flags for domain %s\n",
1381 domain->name, d.alt_name));
1383 d.domain_flags = trust->trust_flags;
1384 d.domain_type = trust->trust_type;
1385 d.domain_trust_attribs = trust->trust_attributes;
1387 wcache_tdc_add_domain( &d );
1388 ret_count++;
1389 } else if (domain_is_forest_root(domain)) {
1390 /* Check if we already have this record. If
1391 * we are following our forest root that is not
1392 * our primary domain, we want to keep trust
1393 * flags from the perspective of our primary
1394 * domain not our forest root. */
1395 struct winbindd_tdc_domain *exist = NULL;
1397 exist = wcache_tdc_fetch_domain(
1398 talloc_tos(), trust->netbios_name);
1399 if (!exist) {
1400 DEBUG(10,("trusted_domains(ads): Searching "
1401 "trusted domain list of %s and "
1402 "storing trust flags for domain "
1403 "%s\n", domain->name, d.alt_name));
1404 d.domain_flags = trust->trust_flags;
1405 d.domain_type = trust->trust_type;
1406 d.domain_trust_attribs =
1407 trust->trust_attributes;
1409 wcache_tdc_add_domain( &d );
1410 ret_count++;
1412 TALLOC_FREE(exist);
1413 } else {
1414 /* This gets a little tricky. If we are
1415 following a transitive forest trust, then
1416 innerit the flags, type, and attribs from
1417 the domain we queried to make sure we don't
1418 record the view of the trust from the wrong
1419 side. Always view it from the side of our
1420 primary domain. --jerry */
1421 struct winbindd_tdc_domain *parent = NULL;
1423 DEBUG(10,("trusted_domains(ads): Searching "
1424 "trusted domain list of %s and inheriting "
1425 "trust flags for domain %s\n",
1426 domain->name, d.alt_name));
1428 parent = wcache_tdc_fetch_domain(talloc_tos(),
1429 domain->name);
1430 if (parent) {
1431 d.domain_flags = parent->trust_flags;
1432 d.domain_type = parent->trust_type;
1433 d.domain_trust_attribs = parent->trust_attribs;
1434 } else {
1435 d.domain_flags = domain->domain_flags;
1436 d.domain_type = domain->domain_type;
1437 d.domain_trust_attribs =
1438 domain->domain_trust_attribs;
1440 TALLOC_FREE(parent);
1442 wcache_tdc_add_domain( &d );
1443 ret_count++;
1446 return result;
1449 /* the ADS backend methods are exposed via this structure */
1450 struct winbindd_methods ads_methods = {
1451 True,
1452 query_user_list,
1453 enum_dom_groups,
1454 enum_local_groups,
1455 name_to_sid,
1456 sid_to_name,
1457 rids_to_names,
1458 query_user,
1459 lookup_usergroups,
1460 lookup_useraliases,
1461 lookup_groupmem,
1462 sequence_number,
1463 lockout_policy,
1464 password_policy,
1465 trusted_domains,
1468 #endif