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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 #define DBGC_CLASS DBGC_WINBIND
33 extern struct winbindd_methods reconnect_methods;
36 return our ads connections structure for a domain. We keep the connection
37 open to make things faster
39 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
46 DEBUG(10,("ads_cached_connection\n"));
48 if (domain->private_data) {
51 time_t now = time(NULL);
53 /* check for a valid structure */
54 ads = (ADS_STRUCT *)domain->private_data;
56 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
58 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
59 (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
61 if ( ads->config.realm && (expire > now)) {
64 /* we own this ADS_STRUCT so make sure it goes away */
65 DEBUG(7,("Deleting expired krb5 credential cache\n"));
68 ads_kdestroy("MEMORY:winbind_ccache");
69 domain->private_data = NULL;
73 /* we don't want this to affect the users ccache */
74 setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
76 ads = ads_init(domain->alt_name, domain->name, NULL);
78 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
82 /* the machine acct password might have change - fetch it every time */
84 SAFE_FREE(ads->auth.password);
85 SAFE_FREE(ads->auth.realm);
91 if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, &sid, &last_set_time ) ) {
95 ads->auth.realm = SMB_STRDUP( ads->server.realm );
96 strupper_m( ads->auth.realm );
99 struct winbindd_domain *our_domain = domain;
101 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
103 /* always give preference to the alt_name in our
104 primary domain if possible */
106 if ( !domain->primary )
107 our_domain = find_our_domain();
109 if ( our_domain->alt_name[0] != '\0' ) {
110 ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
111 strupper_m( ads->auth.realm );
114 ads->auth.realm = SMB_STRDUP( lp_realm() );
117 ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
119 /* Setup the server affinity cache. We don't reaally care
120 about the name. Just setup affinity and the KRB5_CONFIG
123 get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ip );
125 status = ads_connect(ads);
126 if (!ADS_ERR_OK(status) || !ads->config.realm) {
127 DEBUG(1,("ads_connect for domain %s failed: %s\n",
128 domain->name, ads_errstr(status)));
131 /* if we get ECONNREFUSED then it might be a NT4
132 server, fall back to MSRPC */
133 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
134 status.err.rc == ECONNREFUSED) {
135 /* 'reconnect_methods' is the MS-RPC backend. */
136 DEBUG(1,("Trying MSRPC methods\n"));
137 domain->backend = &reconnect_methods;
142 /* set the flag that says we don't own the memory even
143 though we do so that ads_destroy() won't destroy the
144 structure we pass back by reference */
146 ads->is_mine = False;
148 domain->private_data = (void *)ads;
153 /* Query display info for a realm. This is the basic user list fn */
154 static NTSTATUS query_user_list(struct winbindd_domain *domain,
157 WINBIND_USERINFO **info)
159 ADS_STRUCT *ads = NULL;
160 const char *attrs[] = { "*", NULL };
163 LDAPMessage *res = NULL;
164 LDAPMessage *msg = NULL;
165 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
169 DEBUG(3,("ads: query_user_list\n"));
171 ads = ads_cached_connection(domain);
174 domain->last_status = NT_STATUS_SERVER_DISABLED;
178 rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
179 if (!ADS_ERR_OK(rc) || !res) {
180 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
184 count = ads_count_replies(ads, res);
186 DEBUG(1,("query_user_list: No users found\n"));
190 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count);
192 status = NT_STATUS_NO_MEMORY;
198 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
199 char *name, *gecos = NULL;
200 char *homedir = NULL;
205 gid_t primary_gid = (gid_t)-1;
207 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
208 ads_atype_map(atype) != SID_NAME_USER) {
209 DEBUG(1,("Not a user account? atype=0x%x\n", atype));
213 name = ads_pull_username(ads, mem_ctx, msg);
215 if ( ads_pull_sid( ads, msg, "objectSid", &user_sid ) ) {
216 status = nss_get_info( domain->name, &user_sid, mem_ctx,
217 ads, msg, &homedir, &shell, &gecos,
222 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
225 if (!ads_pull_sid(ads, msg, "objectSid",
226 &(*info)[i].user_sid)) {
227 DEBUG(1,("No sid for %s !?\n", name));
230 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
231 DEBUG(1,("No primary group for %s !?\n", name));
235 (*info)[i].acct_name = name;
236 (*info)[i].full_name = gecos;
237 (*info)[i].homedir = homedir;
238 (*info)[i].shell = shell;
239 (*info)[i].primary_gid = primary_gid;
240 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
245 status = NT_STATUS_OK;
247 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
251 ads_msgfree(ads, res);
256 /* list all domain groups */
257 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
260 struct acct_info **info)
262 ADS_STRUCT *ads = NULL;
263 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
264 "name", "objectSid", NULL};
267 LDAPMessage *res = NULL;
268 LDAPMessage *msg = NULL;
269 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
271 BOOL enum_dom_local_groups = False;
275 DEBUG(3,("ads: enum_dom_groups\n"));
277 /* only grab domain local groups for our domain */
278 if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) {
279 enum_dom_local_groups = True;
282 /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
285 * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
286 * default value, it MUST be absent. In case of extensible matching the
287 * "dnattr" boolean defaults to FALSE and so it must be only be present
290 * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
291 * filter using bitwise matching rule then the buggy AD fails to decode
292 * the extensible match. As a workaround set it to TRUE and thereby add
293 * the dnAttributes "dn" field to cope with those older AD versions.
294 * It should not harm and won't put any additional load on the AD since
295 * none of the dn components have a bitmask-attribute.
297 * Thanks to Ralf Haferkamp for input and testing - Guenther */
299 filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))",
300 ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
301 ADS_LDAP_MATCHING_RULE_BIT_AND,
302 enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
304 if (filter == NULL) {
305 status = NT_STATUS_NO_MEMORY;
309 ads = ads_cached_connection(domain);
312 domain->last_status = NT_STATUS_SERVER_DISABLED;
316 rc = ads_search_retry(ads, &res, filter, attrs);
317 if (!ADS_ERR_OK(rc) || !res) {
318 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
322 count = ads_count_replies(ads, res);
324 DEBUG(1,("enum_dom_groups: No groups found\n"));
328 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
330 status = NT_STATUS_NO_MEMORY;
336 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
341 name = ads_pull_username(ads, mem_ctx, msg);
342 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
343 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
344 DEBUG(1,("No sid for %s !?\n", name));
348 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
349 DEBUG(1,("No rid for %s !?\n", name));
353 fstrcpy((*info)[i].acct_name, name);
354 fstrcpy((*info)[i].acct_desc, gecos);
355 (*info)[i].rid = rid;
361 status = NT_STATUS_OK;
363 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
367 ads_msgfree(ads, res);
372 /* list all domain local groups */
373 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
376 struct acct_info **info)
379 * This is a stub function only as we returned the domain
380 * local groups in enum_dom_groups() if the domain->native field
381 * was true. This is a simple performance optimization when
384 * if we ever need to enumerate domain local groups separately,
385 * then this the optimization in enum_dom_groups() will need
393 /* convert a DN to a name, SID and name type
394 this might become a major speed bottleneck if groups have
395 lots of users, in which case we could cache the results
397 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
399 char **name, uint32 *name_type, DOM_SID *sid)
401 LDAPMessage *res = NULL;
402 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
403 "objectSid", "sAMAccountType", NULL};
406 DEBUG(3,("ads: dn_lookup\n"));
408 rc = ads_search_retry_dn(ads, &res, dn, attrs);
410 if (!ADS_ERR_OK(rc) || !res) {
414 (*name) = ads_pull_username(ads, mem_ctx, res);
416 if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
419 (*name_type) = ads_atype_map(atype);
421 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
426 ads_msgfree(ads, res);
432 ads_msgfree(ads, res);
437 /* Lookup user information from a rid */
438 static NTSTATUS query_user(struct winbindd_domain *domain,
441 WINBIND_USERINFO *info)
443 ADS_STRUCT *ads = NULL;
444 const char *attrs[] = { "*", NULL };
447 LDAPMessage *msg = NULL;
451 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
453 DEBUG(3,("ads: query_user\n"));
455 if ( (ads = ads_cached_connection(domain)) == NULL ) {
456 domain->last_status = NT_STATUS_SERVER_DISABLED;
460 sidstr = sid_binstring(sid);
461 asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
462 rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
465 if (!ADS_ERR_OK(rc) || !msg) {
466 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
467 sid_string_static(sid), ads_errstr(rc)));
471 count = ads_count_replies(ads, msg);
473 DEBUG(1,("query_user(sid=%s): Not found\n",
474 sid_string_static(sid)));
478 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
480 info->primary_gid = (gid_t)-1;
481 nss_get_info( domain->name, sid, mem_ctx, ads, msg,
482 &info->homedir, &info->shell, &info->full_name, &info->primary_gid );
484 if (info->full_name == NULL) {
485 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
488 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
489 DEBUG(1,("No primary group for %s !?\n",
490 sid_string_static(sid)));
494 sid_copy(&info->user_sid, sid);
495 sid_compose(&info->group_sid, &domain->sid, group_rid);
497 status = NT_STATUS_OK;
499 DEBUG(3,("ads query_user gave %s\n", info->acct_name));
502 ads_msgfree(ads, msg);
507 /* Lookup groups a user is a member of - alternate method, for when
508 tokenGroups are not available. */
509 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
512 DOM_SID *primary_group,
513 size_t *p_num_groups, DOM_SID **user_sids)
516 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
518 LDAPMessage *res = NULL;
519 LDAPMessage *msg = NULL;
522 const char *group_attrs[] = {"objectSid", NULL};
524 size_t num_groups = 0;
526 DEBUG(3,("ads: lookup_usergroups_member\n"));
528 ads = ads_cached_connection(domain);
531 domain->last_status = NT_STATUS_SERVER_DISABLED;
535 if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
536 status = NT_STATUS_NO_MEMORY;
540 if (!(ldap_exp = talloc_asprintf(mem_ctx, "(&(member=%s)(objectCategory=group))", escaped_dn))) {
541 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
542 SAFE_FREE(escaped_dn);
543 status = NT_STATUS_NO_MEMORY;
547 SAFE_FREE(escaped_dn);
549 rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
551 if (!ADS_ERR_OK(rc) || !res) {
552 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
553 return ads_ntstatus(rc);
556 count = ads_count_replies(ads, res);
561 /* always add the primary group to the sid array */
562 if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
563 status = NT_STATUS_NO_MEMORY;
568 for (msg = ads_first_entry(ads, res); msg;
569 msg = ads_next_entry(ads, msg)) {
572 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
573 DEBUG(1,("No sid for this group ?!?\n"));
577 /* ignore Builtin groups from ADS - Guenther */
578 if (sid_check_is_in_builtin(&group_sid)) {
582 if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
584 status = NT_STATUS_NO_MEMORY;
591 *p_num_groups = num_groups;
592 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
594 DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
597 ads_msgfree(ads, res);
602 /* Lookup groups a user is a member of - alternate method, for when
603 tokenGroups are not available. */
604 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
607 DOM_SID *primary_group,
608 size_t *p_num_groups, DOM_SID **user_sids)
611 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
613 const char *attrs[] = {"memberOf", NULL};
614 size_t num_groups = 0;
615 DOM_SID *group_sids = NULL;
618 size_t num_strings = 0;
621 DEBUG(3,("ads: lookup_usergroups_memberof\n"));
623 ads = ads_cached_connection(domain);
626 domain->last_status = NT_STATUS_SERVER_DISABLED;
630 rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
631 ADS_EXTENDED_DN_HEX_STRING,
632 &strings, &num_strings);
634 if (!ADS_ERR_OK(rc)) {
635 DEBUG(1,("lookup_usergroups_memberof ads_search member=%s: %s\n",
636 user_dn, ads_errstr(rc)));
637 return ads_ntstatus(rc);
643 /* always add the primary group to the sid array */
644 if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
645 status = NT_STATUS_NO_MEMORY;
649 group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1);
651 TALLOC_FREE(strings);
652 status = NT_STATUS_NO_MEMORY;
656 for (i=0; i<num_strings; i++) {
658 if (!ads_get_sid_from_extended_dn(mem_ctx, strings[i],
659 ADS_EXTENDED_DN_HEX_STRING,
661 TALLOC_FREE(group_sids);
662 TALLOC_FREE(strings);
663 status = NT_STATUS_NO_MEMORY;
669 DEBUG(1,("No memberOf for this user?!?\n"));
670 status = NT_STATUS_NO_MEMORY;
674 for (i=0; i<num_strings; i++) {
676 /* ignore Builtin groups from ADS - Guenther */
677 if (sid_check_is_in_builtin(&group_sids[i])) {
681 if (!add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
683 status = NT_STATUS_NO_MEMORY;
689 *p_num_groups = num_groups;
690 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
692 DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n", user_dn));
694 TALLOC_FREE(group_sids);
700 /* Lookup groups a user is a member of. */
701 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
704 uint32 *p_num_groups, DOM_SID **user_sids)
706 ADS_STRUCT *ads = NULL;
707 const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
710 LDAPMessage *msg = NULL;
711 char *user_dn = NULL;
714 DOM_SID primary_group;
715 uint32 primary_group_rid;
717 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
718 size_t num_groups = 0;
720 DEBUG(3,("ads: lookup_usergroups\n"));
723 status = lookup_usergroups_cached(domain, mem_ctx, sid,
724 p_num_groups, user_sids);
725 if (NT_STATUS_IS_OK(status)) {
729 ads = ads_cached_connection(domain);
732 domain->last_status = NT_STATUS_SERVER_DISABLED;
733 status = NT_STATUS_SERVER_DISABLED;
737 rc = ads_search_retry_sid(ads, &msg, sid, attrs);
739 if (!ADS_ERR_OK(rc)) {
740 status = ads_ntstatus(rc);
741 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n",
742 sid_to_string(sid_string, sid), ads_errstr(rc)));
746 count = ads_count_replies(ads, msg);
748 status = NT_STATUS_UNSUCCESSFUL;
749 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
750 "invalid number of results (count=%d)\n",
751 sid_to_string(sid_string, sid), count));
756 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
757 sid_to_string(sid_string, sid)));
758 status = NT_STATUS_UNSUCCESSFUL;
762 user_dn = ads_get_dn(ads, msg);
763 if (user_dn == NULL) {
764 status = NT_STATUS_NO_MEMORY;
768 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
769 DEBUG(1,("%s: No primary group for sid=%s !?\n",
770 domain->name, sid_to_string(sid_string, sid)));
774 sid_copy(&primary_group, &domain->sid);
775 sid_append_rid(&primary_group, primary_group_rid);
777 count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
779 /* there must always be at least one group in the token,
780 unless we are talking to a buggy Win2k server */
782 /* actually this only happens when the machine account has no read
783 * permissions on the tokenGroup attribute - gd */
789 /* lookup what groups this user is a member of by DN search on
792 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
794 &num_groups, user_sids);
795 *p_num_groups = (uint32)num_groups;
796 if (NT_STATUS_IS_OK(status)) {
800 /* lookup what groups this user is a member of by DN search on
803 status = lookup_usergroups_member(domain, mem_ctx, user_dn,
805 &num_groups, user_sids);
806 *p_num_groups = (uint32)num_groups;
813 if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
814 status = NT_STATUS_NO_MEMORY;
818 for (i=0;i<count;i++) {
820 /* ignore Builtin groups from ADS - Guenther */
821 if (sid_check_is_in_builtin(&sids[i])) {
825 if (!add_sid_to_array_unique(mem_ctx, &sids[i],
826 user_sids, &num_groups)) {
827 status = NT_STATUS_NO_MEMORY;
832 *p_num_groups = (uint32)num_groups;
833 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
835 DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
836 sid_to_string(sid_string, sid)));
838 ads_memfree(ads, user_dn);
839 ads_msgfree(ads, msg);
844 find the members of a group, given a group rid and domain
846 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
848 const DOM_SID *group_sid, uint32 *num_names,
849 DOM_SID **sid_mem, char ***names,
853 LDAPMessage *res=NULL;
854 ADS_STRUCT *ads = NULL;
856 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
864 DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
865 sid_string_static(group_sid)));
869 ads = ads_cached_connection(domain);
872 domain->last_status = NT_STATUS_SERVER_DISABLED;
876 if ((sidstr = sid_binstring(group_sid)) == NULL) {
877 status = NT_STATUS_NO_MEMORY;
881 /* search for all members of the group */
882 if (!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)",sidstr))) {
884 DEBUG(1, ("ads: lookup_groupmem: tallloc_asprintf for ldap_exp failed!\n"));
885 status = NT_STATUS_NO_MEMORY;
893 args.control = ADS_EXTENDED_DN_OID;
894 args.val = ADS_EXTENDED_DN_HEX_STRING;
895 args.critical = True;
897 rc = ads_ranged_search(ads, mem_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
898 ldap_exp, &args, "member", &members, &num_members);
900 if (!ADS_ERR_OK(rc)) {
901 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
902 status = NT_STATUS_UNSUCCESSFUL;
906 /* now we need to turn a list of members into rids, names and name types
907 the problem is that the members are in the form of distinguised names
911 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
912 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
913 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
915 if ((members == NULL) || (*sid_mem == NULL) ||
916 (*name_types == NULL) || (*names == NULL)) {
917 DEBUG(1, ("talloc failed\n"));
918 status = NT_STATUS_NO_MEMORY;
923 (*name_types) = NULL;
927 for (i=0;i<num_members;i++) {
929 char *name, *domain_name, *dn;
932 if ((!ads_get_sid_from_extended_dn(mem_ctx, members[i], ADS_EXTENDED_DN_HEX_STRING, &sid)) ||
933 (!ads_get_dn_from_extended_dn(mem_ctx, members[i], &dn)))
935 status = NT_STATUS_INVALID_PARAMETER;
939 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name, &name_type)) {
941 DEBUG(10,("ads: lookup_groupmem: got sid %s from cache\n",
942 sid_string_static(&sid)));
944 (*names)[*num_names] = CONST_DISCARD(char *,name);
945 (*name_types)[*num_names] = name_type;
946 sid_copy(&(*sid_mem)[*num_names], &sid);
953 if (dn_lookup(ads, mem_ctx, dn, &name, &name_type, &sid)) {
955 DEBUG(10,("ads: lookup_groupmem: got sid %s from dn_lookup\n",
956 sid_string_static(&sid)));
958 (*names)[*num_names] = name;
959 (*name_types)[*num_names] = name_type;
960 sid_copy(&(*sid_mem)[*num_names], &sid);
967 status = NT_STATUS_OK;
968 DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n", sid_to_string(sid_string, group_sid)));
972 ads_msgfree(ads, res);
977 /* find the sequence number for a domain */
978 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
980 ADS_STRUCT *ads = NULL;
983 DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
985 *seq = DOM_SEQUENCE_NONE;
987 ads = ads_cached_connection(domain);
990 domain->last_status = NT_STATUS_SERVER_DISABLED;
991 return NT_STATUS_UNSUCCESSFUL;
994 rc = ads_USN(ads, seq);
996 if (!ADS_ERR_OK(rc)) {
998 /* its a dead connection, destroy it */
1000 if (domain->private_data) {
1001 ads = (ADS_STRUCT *)domain->private_data;
1002 ads->is_mine = True;
1004 ads_kdestroy("MEMORY:winbind_ccache");
1005 domain->private_data = NULL;
1008 return ads_ntstatus(rc);
1011 /* get a list of trusted domains */
1012 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1013 TALLOC_CTX *mem_ctx,
1014 uint32 *num_domains,
1019 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1020 struct ds_domain_trust *domains = NULL;
1024 struct rpc_pipe_client *cli;
1025 uint32 fr_flags = (DS_DOMAIN_IN_FOREST | DS_DOMAIN_TREE_ROOT);
1028 DEBUG(3,("ads: trusted_domains\n"));
1035 /* If this is our primary domain or a root in our forest,
1036 query for all trusts. If not, then just look for domain
1037 trusts in the target forest */
1039 if ( domain->primary ||
1040 ((domain->domain_flags&fr_flags) == fr_flags) )
1042 flags = DS_DOMAIN_DIRECT_OUTBOUND |
1043 DS_DOMAIN_DIRECT_INBOUND |
1044 DS_DOMAIN_IN_FOREST;
1046 flags = DS_DOMAIN_IN_FOREST;
1049 result = cm_connect_netlogon(domain, &cli);
1051 if (!NT_STATUS_IS_OK(result)) {
1052 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1053 "for PIPE_NETLOGON (%s)\n",
1054 domain->name, nt_errstr(result)));
1055 return NT_STATUS_UNSUCCESSFUL;
1058 if ( NT_STATUS_IS_OK(result) ) {
1059 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
1062 (unsigned int *)&count);
1065 if ( NT_STATUS_IS_OK(result) && count) {
1067 /* Allocate memory for trusted domain names and sids */
1069 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1070 DEBUG(0, ("trusted_domains: out of memory\n"));
1071 return NT_STATUS_NO_MEMORY;
1074 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1075 DEBUG(0, ("trusted_domains: out of memory\n"));
1076 return NT_STATUS_NO_MEMORY;
1079 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) {
1080 DEBUG(0, ("trusted_domains: out of memory\n"));
1081 return NT_STATUS_NO_MEMORY;
1084 /* Copy across names and sids */
1088 for (i = 0; i < count; i++) {
1089 struct winbindd_domain d;
1091 /* drop external trusts if this is not our primary
1092 domain. This means that the returned number of
1093 domains may be less that the ones actually trusted
1096 if ( (domains[i].trust_attributes == DS_DOMAIN_TRUST_ATTRIB_QUARANTINED_DOMAIN) &&
1099 DEBUG(10,("trusted_domains: Skipping external trusted domain "
1100 "%s because it is outside of our primary domain\n",
1101 domains[i].netbios_domain));
1105 (*names)[ret_count] = domains[i].netbios_domain;
1106 (*alt_names)[ret_count] = domains[i].dns_domain;
1107 sid_copy(&(*dom_sids)[ret_count], &domains[i].sid);
1109 /* add to the trusted domain cache */
1111 fstrcpy( d.name, domains[i].netbios_domain );
1112 fstrcpy( d.alt_name, domains[i].dns_domain );
1113 sid_copy( &d.sid, &domains[i].sid );
1115 /* This gets a little tricky. If we are
1116 following a transitive forest trust, then
1117 innerit the flags, type, and attrins from
1118 the domain we queried to make sure we don't
1119 record the view of the trust from the wrong
1120 side. Always view it from the side of our
1121 primary domain. --jerry */
1122 if ( domain->primary ||
1123 ((domain->domain_flags&fr_flags) == fr_flags) )
1125 DEBUG(10,("trusted_domains(ads): Storing trust "
1126 "flags for domain %s\n", d.alt_name));
1128 /* Look this up in cache to make sure
1129 we have the current trust flags and
1132 d.domain_flags = domains[i].flags;
1133 d.domain_type = domains[i].trust_type;
1134 d.domain_trust_attribs = domains[i].trust_attributes;
1136 DEBUG(10,("trusted_domains(ads): Inheriting trust "
1137 "flags for domain %s\n", d.alt_name));
1138 d.domain_flags = domain->domain_flags;
1139 d.domain_type = domain->domain_type;
1140 d.domain_trust_attribs = domain->domain_trust_attribs;
1143 wcache_tdc_add_domain( &d );
1149 *num_domains = ret_count;
1155 /* the ADS backend methods are exposed via this structure */
1156 struct winbindd_methods ads_methods = {
1163 msrpc_rids_to_names,
1166 msrpc_lookup_useraliases,
1169 msrpc_lockout_policy,
1170 msrpc_password_policy,