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
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #define DBGC_CLASS DBGC_WINBIND
31 /* the realm of our primary LDAP server */
32 static char *primary_realm
;
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
)
44 if (domain
->private) {
45 return (ADS_STRUCT
*)domain
->private;
48 /* we don't want this to affect the users ccache */
49 setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
51 ads
= ads_init(domain
->alt_name
, domain
->name
, NULL
);
53 DEBUG(1,("ads_init for domain %s failed\n", domain
->name
));
57 /* the machine acct password might have change - fetch it every time */
58 SAFE_FREE(ads
->auth
.password
);
59 ads
->auth
.password
= secrets_fetch_machine_password();
62 SAFE_FREE(ads
->auth
.realm
);
63 ads
->auth
.realm
= strdup(primary_realm
);
66 status
= ads_connect(ads
);
67 if (!ADS_ERR_OK(status
) || !ads
->config
.realm
) {
68 extern struct winbindd_methods msrpc_methods
;
69 DEBUG(1,("ads_connect for domain %s failed: %s\n",
70 domain
->name
, ads_errstr(status
)));
73 /* if we get ECONNREFUSED then it might be a NT4
74 server, fall back to MSRPC */
75 if (status
.error_type
== ADS_ERROR_SYSTEM
&&
76 status
.err
.rc
== ECONNREFUSED
) {
77 DEBUG(1,("Trying MSRPC methods\n"));
78 domain
->methods
= &msrpc_methods
;
83 /* remember our primary realm for trusted domain support */
85 primary_realm
= strdup(ads
->config
.realm
);
88 domain
->private = (void *)ads
;
93 /* Query display info for a realm. This is the basic user list fn */
94 static NTSTATUS
query_user_list(struct winbindd_domain
*domain
,
97 WINBIND_USERINFO
**info
)
99 ADS_STRUCT
*ads
= NULL
;
100 const char *attrs
[] = {"userPrincipalName",
102 "name", "objectSid", "primaryGroupID",
103 "sAMAccountType", NULL
};
108 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
112 DEBUG(3,("ads: query_user_list\n"));
114 ads
= ads_cached_connection(domain
);
117 rc
= ads_search_retry(ads
, &res
, "(objectCategory=user)", attrs
);
118 if (!ADS_ERR_OK(rc
)) {
119 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc
)));
123 count
= ads_count_replies(ads
, res
);
125 DEBUG(1,("query_user_list: No users found\n"));
129 (*info
) = talloc_zero(mem_ctx
, count
* sizeof(**info
));
131 status
= NT_STATUS_NO_MEMORY
;
137 for (msg
= ads_first_entry(ads
, res
); msg
; msg
= ads_next_entry(ads
, msg
)) {
145 if (!ads_pull_uint32(ads
, msg
, "sAMAccountType", &atype
) ||
146 ads_atype_map(atype
) != SID_NAME_USER
) {
147 DEBUG(1,("Not a user account? atype=0x%x\n", atype
));
151 name
= ads_pull_username(ads
, mem_ctx
, msg
);
152 gecos
= ads_pull_string(ads
, mem_ctx
, msg
, "name");
153 if (!ads_pull_sid(ads
, msg
, "objectSid", &sid
)) {
154 DEBUG(1,("No sid for %s !?\n", name
));
157 if (!ads_pull_uint32(ads
, msg
, "primaryGroupID", &group
)) {
158 DEBUG(1,("No primary group for %s !?\n", name
));
162 sid2
= talloc(mem_ctx
, sizeof(*sid2
));
164 status
= NT_STATUS_NO_MEMORY
;
168 sid_copy(sid2
, &sid
);
170 group_sid
= rid_to_talloced_sid(domain
, mem_ctx
, group
);
172 (*info
)[i
].acct_name
= name
;
173 (*info
)[i
].full_name
= gecos
;
174 (*info
)[i
].user_sid
= sid2
;
175 (*info
)[i
].group_sid
= group_sid
;
180 status
= NT_STATUS_OK
;
182 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries
)));
185 if (res
) ads_msgfree(ads
, res
);
190 /* list all domain groups */
191 static NTSTATUS
enum_dom_groups(struct winbindd_domain
*domain
,
194 struct acct_info
**info
)
196 ADS_STRUCT
*ads
= NULL
;
197 const char *attrs
[] = {"userPrincipalName", "sAMAccountName",
199 "sAMAccountType", NULL
};
204 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
209 DEBUG(3,("ads: enum_dom_groups\n"));
211 ads
= ads_cached_connection(domain
);
214 rc
= ads_search_retry(ads
, &res
, "(objectCategory=group)", attrs
);
215 if (!ADS_ERR_OK(rc
)) {
216 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc
)));
220 count
= ads_count_replies(ads
, res
);
222 DEBUG(1,("enum_dom_groups: No groups found\n"));
226 (*info
) = talloc_zero(mem_ctx
, count
* sizeof(**info
));
228 status
= NT_STATUS_NO_MEMORY
;
234 group_flags
= ATYPE_GLOBAL_GROUP
;
235 if ( domain
->native_mode
)
236 group_flags
|= ATYPE_LOCAL_GROUP
;
238 for (msg
= ads_first_entry(ads
, res
); msg
; msg
= ads_next_entry(ads
, msg
)) {
244 if (!ads_pull_uint32(ads
, msg
, "sAMAccountType", &account_type
) || !(account_type
& group_flags
) )
247 name
= ads_pull_username(ads
, mem_ctx
, msg
);
248 gecos
= ads_pull_string(ads
, mem_ctx
, msg
, "name");
249 if (!ads_pull_sid(ads
, msg
, "objectSid", &sid
)) {
250 DEBUG(1,("No sid for %s !?\n", name
));
254 if (!sid_peek_check_rid(&domain
->sid
, &sid
, &rid
)) {
255 DEBUG(1,("No rid for %s !?\n", name
));
259 fstrcpy((*info
)[i
].acct_name
, name
);
260 fstrcpy((*info
)[i
].acct_desc
, gecos
);
261 (*info
)[i
].rid
= rid
;
267 status
= NT_STATUS_OK
;
269 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries
)));
272 if (res
) ads_msgfree(ads
, res
);
277 /* list all domain local groups */
278 static NTSTATUS
enum_local_groups(struct winbindd_domain
*domain
,
281 struct acct_info
**info
)
284 * This is a stub function only as we returned the domain
285 * ocal groups in enum_dom_groups() if the domain->native field
286 * was true. This is a simple performance optimization when
289 * if we ever need to enumerate domain local groups separately,
290 * then this the optimization in enum_dom_groups() will need
298 /* convert a single name to a sid in a domain */
299 static NTSTATUS
name_to_sid(struct winbindd_domain
*domain
,
303 enum SID_NAME_USE
*type
)
307 DEBUG(3,("ads: name_to_sid\n"));
309 ads
= ads_cached_connection(domain
);
311 return NT_STATUS_UNSUCCESSFUL
;
313 return ads_name_to_sid(ads
, name
, sid
, type
);
316 /* convert a sid to a user or group name */
317 static NTSTATUS
sid_to_name(struct winbindd_domain
*domain
,
321 enum SID_NAME_USE
*type
)
323 ADS_STRUCT
*ads
= NULL
;
324 DEBUG(3,("ads: sid_to_name\n"));
325 ads
= ads_cached_connection(domain
);
327 return NT_STATUS_UNSUCCESSFUL
;
329 return ads_sid_to_name(ads
, mem_ctx
, sid
, name
, type
);
333 /* convert a DN to a name, SID and name type
334 this might become a major speed bottleneck if groups have
335 lots of users, in which case we could cache the results
337 static BOOL
dn_lookup(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
339 char **name
, uint32
*name_type
, DOM_SID
*sid
)
343 const char *attrs
[] = {"userPrincipalName", "sAMAccountName",
344 "objectSid", "sAMAccountType", NULL
};
347 char *escaped_dn
= escape_ldap_string_alloc(dn
);
353 asprintf(&exp
, "(distinguishedName=%s)", dn
);
354 rc
= ads_search_retry(ads
, &res
, exp
, attrs
);
356 SAFE_FREE(escaped_dn
);
358 if (!ADS_ERR_OK(rc
)) {
362 (*name
) = ads_pull_username(ads
, mem_ctx
, res
);
364 if (!ads_pull_uint32(ads
, res
, "sAMAccountType", &atype
)) {
367 (*name_type
) = ads_atype_map(atype
);
369 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
373 if (res
) ads_msgfree(ads
, res
);
377 if (res
) ads_msgfree(ads
, res
);
381 /* Lookup user information from a rid */
382 static NTSTATUS
query_user(struct winbindd_domain
*domain
,
385 WINBIND_USERINFO
*info
)
387 ADS_STRUCT
*ads
= NULL
;
388 const char *attrs
[] = {"userPrincipalName",
391 "primaryGroupID", NULL
};
398 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
402 DEBUG(3,("ads: query_user\n"));
404 ads
= ads_cached_connection(domain
);
407 sidstr
= sid_binstring(sid
);
408 asprintf(&exp
, "(objectSid=%s)", sidstr
);
409 rc
= ads_search_retry(ads
, &msg
, exp
, attrs
);
412 if (!ADS_ERR_OK(rc
)) {
413 DEBUG(1,("query_user(sid=%s) ads_search: %s\n", sid_to_string(sid_string
, sid
), ads_errstr(rc
)));
417 count
= ads_count_replies(ads
, msg
);
419 DEBUG(1,("query_user(sid=%s): Not found\n", sid_to_string(sid_string
, sid
)));
423 info
->acct_name
= ads_pull_username(ads
, mem_ctx
, msg
);
424 info
->full_name
= ads_pull_string(ads
, mem_ctx
, msg
, "name");
426 if (!ads_pull_uint32(ads
, msg
, "primaryGroupID", &group_rid
)) {
427 DEBUG(1,("No primary group for %s !?\n", sid_to_string(sid_string
, sid
)));
431 sid2
= talloc(mem_ctx
, sizeof(*sid2
));
433 status
= NT_STATUS_NO_MEMORY
;
438 info
->user_sid
= sid2
;
440 info
->group_sid
= rid_to_talloced_sid(domain
, mem_ctx
, group_rid
);
442 status
= NT_STATUS_OK
;
444 DEBUG(3,("ads query_user gave %s\n", info
->acct_name
));
446 if (msg
) ads_msgfree(ads
, msg
);
451 /* Lookup groups a user is a member of - alternate method, for when
452 tokenGroups are not available. */
453 static NTSTATUS
lookup_usergroups_alt(struct winbindd_domain
*domain
,
456 DOM_SID
*primary_group
,
457 uint32
*num_groups
, DOM_SID
***user_gids
)
460 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
466 const char *group_attrs
[] = {"objectSid", NULL
};
468 ads
= ads_cached_connection(domain
);
471 /* buggy server, no tokenGroups. Instead lookup what groups this user
472 is a member of by DN search on member*/
473 if (asprintf(&exp
, "(&(member=%s)(objectClass=group))", user_dn
) == -1) {
474 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn
));
475 return NT_STATUS_NO_MEMORY
;
478 rc
= ads_search_retry(ads
, &res
, exp
, group_attrs
);
481 if (!ADS_ERR_OK(rc
)) {
482 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn
, ads_errstr(rc
)));
483 return ads_ntstatus(rc
);
486 count
= ads_count_replies(ads
, res
);
488 DEBUG(5,("lookup_usergroups: No supp groups found\n"));
490 status
= ads_ntstatus(rc
);
494 (*user_gids
) = talloc_zero(mem_ctx
, sizeof(**user_gids
) * (count
+ 1));
495 (*user_gids
)[0] = primary_group
;
499 for (msg
= ads_first_entry(ads
, res
); msg
; msg
= ads_next_entry(ads
, msg
)) {
502 if (!ads_pull_sid(ads
, msg
, "objectSid", &group_sid
)) {
503 DEBUG(1,("No sid for this group ?!?\n"));
507 if (sid_equal(&group_sid
, primary_group
)) continue;
509 (*user_gids
)[*num_groups
] = talloc(mem_ctx
, sizeof(***user_gids
));
510 if (!(*user_gids
)[*num_groups
]) {
511 status
= NT_STATUS_NO_MEMORY
;
515 sid_copy((*user_gids
)[*num_groups
], &group_sid
);
521 status
= NT_STATUS_OK
;
523 DEBUG(3,("ads lookup_usergroups (alt) for dn=%s\n", user_dn
));
525 if (res
) ads_msgfree(ads
, res
);
526 if (msg
) ads_msgfree(ads
, msg
);
531 /* Lookup groups a user is a member of. */
532 static NTSTATUS
lookup_usergroups(struct winbindd_domain
*domain
,
535 uint32
*num_groups
, DOM_SID
***user_gids
)
537 ADS_STRUCT
*ads
= NULL
;
538 const char *attrs
[] = {"distinguishedName", NULL
};
539 const char *attrs2
[] = {"tokenGroups", "primaryGroupID", NULL
};
547 DOM_SID
*primary_group
;
548 uint32 primary_group_rid
;
551 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
553 DEBUG(3,("ads: lookup_usergroups\n"));
556 ads
= ads_cached_connection(domain
);
559 if (!(sidstr
= sid_binstring(sid
))) {
560 DEBUG(1,("lookup_usergroups(sid=%s) sid_binstring returned NULL\n", sid_to_string(sid_string
, sid
)));
561 status
= NT_STATUS_NO_MEMORY
;
564 if (asprintf(&exp
, "(objectSid=%s)", sidstr
) == -1) {
566 DEBUG(1,("lookup_usergroups(sid=%s) asprintf failed!\n", sid_to_string(sid_string
, sid
)));
567 status
= NT_STATUS_NO_MEMORY
;
571 rc
= ads_search_retry(ads
, &msg
, exp
, attrs
);
575 if (!ADS_ERR_OK(rc
)) {
576 DEBUG(1,("lookup_usergroups(sid=%s) ads_search: %s\n", sid_to_string(sid_string
, sid
), ads_errstr(rc
)));
580 user_dn
= ads_pull_string(ads
, mem_ctx
, msg
, "distinguishedName");
582 DEBUG(1,("lookup_usergroups(sid=%s) ads_search did not return a a distinguishedName!\n", sid_to_string(sid_string
, sid
)));
583 if (msg
) ads_msgfree(ads
, msg
);
587 if (msg
) ads_msgfree(ads
, msg
);
589 rc
= ads_search_retry_dn(ads
, &msg
, user_dn
, attrs2
);
590 if (!ADS_ERR_OK(rc
)) {
591 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", sid_to_string(sid_string
, sid
), ads_errstr(rc
)));
595 if (!ads_pull_uint32(ads
, msg
, "primaryGroupID", &primary_group_rid
)) {
596 DEBUG(1,("%s: No primary group for sid=%s !?\n", domain
->name
, sid_to_string(sid_string
, sid
)));
600 primary_group
= rid_to_talloced_sid(domain
, mem_ctx
, primary_group_rid
);
602 count
= ads_pull_sids(ads
, mem_ctx
, msg
, "tokenGroups", &sids
);
604 if (msg
) ads_msgfree(ads
, msg
);
606 /* there must always be at least one group in the token,
607 unless we are talking to a buggy Win2k server */
609 return lookup_usergroups_alt(domain
, mem_ctx
, user_dn
,
611 num_groups
, user_gids
);
614 (*user_gids
) = talloc_zero(mem_ctx
, sizeof(**user_gids
) * (count
+ 1));
615 (*user_gids
)[0] = primary_group
;
619 for (i
=0;i
<count
;i
++) {
620 if (sid_equal(&sids
[i
], primary_group
)) continue;
622 (*user_gids
)[*num_groups
] = talloc(mem_ctx
, sizeof(***user_gids
));
623 if (!(*user_gids
)[*num_groups
]) {
624 status
= NT_STATUS_NO_MEMORY
;
628 sid_copy((*user_gids
)[*num_groups
], &sids
[i
]);
632 status
= NT_STATUS_OK
;
633 DEBUG(3,("ads lookup_usergroups for sid=%s\n", sid_to_string(sid_string
, sid
)));
639 find the members of a group, given a group rid and domain
641 static NTSTATUS
lookup_groupmem(struct winbindd_domain
*domain
,
643 DOM_SID
*group_sid
, uint32
*num_names
,
644 DOM_SID
***sid_mem
, char ***names
,
650 ADS_STRUCT
*ads
= NULL
;
652 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
654 const char *attrs
[] = {"member", NULL
};
661 ads
= ads_cached_connection(domain
);
664 sidstr
= sid_binstring(group_sid
);
666 /* search for all members of the group */
667 asprintf(&exp
, "(objectSid=%s)",sidstr
);
668 rc
= ads_search_retry(ads
, &res
, exp
, attrs
);
672 if (!ADS_ERR_OK(rc
)) {
673 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc
)));
677 count
= ads_count_replies(ads
, res
);
679 status
= NT_STATUS_OK
;
683 members
= ads_pull_strings(ads
, mem_ctx
, res
, "member");
685 /* no members? ok ... */
686 status
= NT_STATUS_OK
;
690 /* now we need to turn a list of members into rids, names and name types
691 the problem is that the members are in the form of distinguised names
693 for (i
=0;members
[i
];i
++) /* noop */ ;
696 (*sid_mem
) = talloc_zero(mem_ctx
, sizeof(**sid_mem
) * num_members
);
697 (*name_types
) = talloc_zero(mem_ctx
, sizeof(**name_types
) * num_members
);
698 (*names
) = talloc_zero(mem_ctx
, sizeof(**names
) * num_members
);
700 for (i
=0;i
<num_members
;i
++) {
705 if (dn_lookup(ads
, mem_ctx
, members
[i
], &name
, &name_type
, &sid
)) {
706 (*names
)[*num_names
] = name
;
707 (*name_types
)[*num_names
] = name_type
;
708 (*sid_mem
)[*num_names
] = talloc(mem_ctx
, sizeof(***sid_mem
));
709 if (!(*sid_mem
)[*num_names
]) {
710 status
= NT_STATUS_NO_MEMORY
;
713 sid_copy((*sid_mem
)[*num_names
], &sid
);
718 status
= NT_STATUS_OK
;
719 DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string
, group_sid
)));
721 if (res
) ads_msgfree(ads
, res
);
727 /* find the sequence number for a domain */
728 static NTSTATUS
sequence_number(struct winbindd_domain
*domain
, uint32
*seq
)
730 ADS_STRUCT
*ads
= NULL
;
733 *seq
= DOM_SEQUENCE_NONE
;
735 ads
= ads_cached_connection(domain
);
736 if (!ads
) return NT_STATUS_UNSUCCESSFUL
;
738 rc
= ads_USN(ads
, seq
);
739 if (!ADS_ERR_OK(rc
)) {
740 /* its a dead connection */
742 domain
->private = NULL
;
744 return ads_ntstatus(rc
);
747 /* get a list of trusted domains */
748 static NTSTATUS
trusted_domains(struct winbindd_domain
*domain
,
761 ads
= ads_cached_connection(domain
);
762 if (!ads
) return NT_STATUS_UNSUCCESSFUL
;
764 rc
= ads_trusted_domains(ads
, mem_ctx
, num_domains
, names
, alt_names
, dom_sids
);
766 return ads_ntstatus(rc
);
769 /* find the domain sid for a domain */
770 static NTSTATUS
domain_sid(struct winbindd_domain
*domain
, DOM_SID
*sid
)
775 ads
= ads_cached_connection(domain
);
776 if (!ads
) return NT_STATUS_UNSUCCESSFUL
;
778 rc
= ads_domain_sid(ads
, sid
);
780 if (!ADS_ERR_OK(rc
)) {
781 /* its a dead connection */
783 domain
->private = NULL
;
786 return ads_ntstatus(rc
);
790 /* find alternate names list for the domain - for ADS this is the
792 static NTSTATUS
alternate_name(struct winbindd_domain
*domain
)
799 ads
= ads_cached_connection(domain
);
800 if (!ads
) return NT_STATUS_UNSUCCESSFUL
;
802 if (!(ctx
= talloc_init("alternate_name"))) {
803 return NT_STATUS_NO_MEMORY
;
806 rc
= ads_workgroup_name(ads
, ctx
, &workgroup
);
808 if (ADS_ERR_OK(rc
)) {
809 fstrcpy(domain
->name
, workgroup
);
810 fstrcpy(domain
->alt_name
, ads
->config
.realm
);
811 strupper(domain
->alt_name
);
812 strupper(domain
->name
);
817 return ads_ntstatus(rc
);
820 /* the ADS backend methods are exposed via this structure */
821 struct winbindd_methods ads_methods
= {