r22711: Fix a compile warnign in query_user(). Ensure that user_rid
[Samba.git] / source / nsswitch / winbindd_ads.c
blob76d12229d963ecd3035ae0181b58e5bb236d887d
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 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.
25 #include "includes.h"
26 #include "winbindd.h"
28 #ifdef HAVE_ADS
30 #undef DBGC_CLASS
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)
41 ADS_STRUCT *ads;
42 ADS_STATUS status;
43 fstring dc_name;
44 struct in_addr dc_ip;
46 DEBUG(10,("ads_cached_connection\n"));
48 if (domain->private_data) {
50 time_t expire;
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)) {
62 return ads;
63 } else {
64 /* we own this ADS_STRUCT so make sure it goes away */
65 DEBUG(7,("Deleting expired krb5 credential cache\n"));
66 ads->is_mine = True;
67 ads_destroy( &ads );
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);
77 if (!ads) {
78 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
79 return NULL;
82 /* the machine acct password might have change - fetch it every time */
84 SAFE_FREE(ads->auth.password);
85 SAFE_FREE(ads->auth.realm);
87 if ( IS_DC ) {
88 DOM_SID sid;
89 time_t last_set_time;
91 if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, &sid, &last_set_time ) ) {
92 ads_destroy( &ads );
93 return NULL;
95 ads->auth.realm = SMB_STRDUP( ads->server.realm );
96 strupper_m( ads->auth.realm );
98 else {
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 );
113 else
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
121 file. */
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)));
129 ads_destroy(&ads);
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;
139 return NULL;
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;
149 return 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,
155 TALLOC_CTX *mem_ctx,
156 uint32 *num_entries,
157 WINBIND_USERINFO **info)
159 ADS_STRUCT *ads = NULL;
160 const char *attrs[] = { "*", NULL };
161 int i, count;
162 ADS_STATUS rc;
163 LDAPMessage *res = NULL;
164 LDAPMessage *msg = NULL;
165 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
167 *num_entries = 0;
169 DEBUG(3,("ads: query_user_list\n"));
171 if ( !winbindd_can_contact_domain( domain ) ) {
172 DEBUG(10,("query_user_list: No incoming trust for domain %s\n",
173 domain->name));
174 return NT_STATUS_OK;
177 ads = ads_cached_connection(domain);
179 if (!ads) {
180 domain->last_status = NT_STATUS_SERVER_DISABLED;
181 goto done;
184 rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
185 if (!ADS_ERR_OK(rc) || !res) {
186 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
187 goto done;
190 count = ads_count_replies(ads, res);
191 if (count == 0) {
192 DEBUG(1,("query_user_list: No users found\n"));
193 goto done;
196 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count);
197 if (!*info) {
198 status = NT_STATUS_NO_MEMORY;
199 goto done;
202 i = 0;
204 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
205 char *name, *gecos = NULL;
206 char *homedir = NULL;
207 char *shell = NULL;
208 uint32 group;
209 uint32 atype;
210 DOM_SID user_sid;
211 gid_t primary_gid = (gid_t)-1;
213 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
214 ads_atype_map(atype) != SID_NAME_USER) {
215 DEBUG(1,("Not a user account? atype=0x%x\n", atype));
216 continue;
219 name = ads_pull_username(ads, mem_ctx, msg);
221 if ( ads_pull_sid( ads, msg, "objectSid", &user_sid ) ) {
222 status = nss_get_info_cached( domain, &user_sid, mem_ctx,
223 ads, msg, &homedir, &shell, &gecos,
224 &primary_gid );
227 if (gecos == NULL) {
228 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
231 if (!ads_pull_sid(ads, msg, "objectSid",
232 &(*info)[i].user_sid)) {
233 DEBUG(1,("No sid for %s !?\n", name));
234 continue;
236 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
237 DEBUG(1,("No primary group for %s !?\n", name));
238 continue;
241 (*info)[i].acct_name = name;
242 (*info)[i].full_name = gecos;
243 (*info)[i].homedir = homedir;
244 (*info)[i].shell = shell;
245 (*info)[i].primary_gid = primary_gid;
246 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
247 i++;
250 (*num_entries) = i;
251 status = NT_STATUS_OK;
253 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
255 done:
256 if (res)
257 ads_msgfree(ads, res);
259 return status;
262 /* list all domain groups */
263 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
264 TALLOC_CTX *mem_ctx,
265 uint32 *num_entries,
266 struct acct_info **info)
268 ADS_STRUCT *ads = NULL;
269 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
270 "name", "objectSid", NULL};
271 int i, count;
272 ADS_STATUS rc;
273 LDAPMessage *res = NULL;
274 LDAPMessage *msg = NULL;
275 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
276 const char *filter;
277 BOOL enum_dom_local_groups = False;
279 *num_entries = 0;
281 DEBUG(3,("ads: enum_dom_groups\n"));
283 if ( !winbindd_can_contact_domain( domain ) ) {
284 DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n",
285 domain->name));
286 return NT_STATUS_OK;
289 /* only grab domain local groups for our domain */
290 if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) {
291 enum_dom_local_groups = True;
294 /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
295 * rollup-fixes:
297 * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
298 * default value, it MUST be absent. In case of extensible matching the
299 * "dnattr" boolean defaults to FALSE and so it must be only be present
300 * when set to TRUE.
302 * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
303 * filter using bitwise matching rule then the buggy AD fails to decode
304 * the extensible match. As a workaround set it to TRUE and thereby add
305 * the dnAttributes "dn" field to cope with those older AD versions.
306 * It should not harm and won't put any additional load on the AD since
307 * none of the dn components have a bitmask-attribute.
309 * Thanks to Ralf Haferkamp for input and testing - Guenther */
311 filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))",
312 ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
313 ADS_LDAP_MATCHING_RULE_BIT_AND,
314 enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
316 if (filter == NULL) {
317 status = NT_STATUS_NO_MEMORY;
318 goto done;
321 ads = ads_cached_connection(domain);
323 if (!ads) {
324 domain->last_status = NT_STATUS_SERVER_DISABLED;
325 goto done;
328 rc = ads_search_retry(ads, &res, filter, attrs);
329 if (!ADS_ERR_OK(rc) || !res) {
330 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
331 goto done;
334 count = ads_count_replies(ads, res);
335 if (count == 0) {
336 DEBUG(1,("enum_dom_groups: No groups found\n"));
337 goto done;
340 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
341 if (!*info) {
342 status = NT_STATUS_NO_MEMORY;
343 goto done;
346 i = 0;
348 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
349 char *name, *gecos;
350 DOM_SID sid;
351 uint32 rid;
353 name = ads_pull_username(ads, mem_ctx, msg);
354 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
355 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
356 DEBUG(1,("No sid for %s !?\n", name));
357 continue;
360 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
361 DEBUG(1,("No rid for %s !?\n", name));
362 continue;
365 fstrcpy((*info)[i].acct_name, name);
366 fstrcpy((*info)[i].acct_desc, gecos);
367 (*info)[i].rid = rid;
368 i++;
371 (*num_entries) = i;
373 status = NT_STATUS_OK;
375 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
377 done:
378 if (res)
379 ads_msgfree(ads, res);
381 return status;
384 /* list all domain local groups */
385 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
386 TALLOC_CTX *mem_ctx,
387 uint32 *num_entries,
388 struct acct_info **info)
391 * This is a stub function only as we returned the domain
392 * local groups in enum_dom_groups() if the domain->native field
393 * was true. This is a simple performance optimization when
394 * using LDAP.
396 * if we ever need to enumerate domain local groups separately,
397 * then this the optimization in enum_dom_groups() will need
398 * to be split out
400 *num_entries = 0;
402 return NT_STATUS_OK;
405 /* convert a DN to a name, SID and name type
406 this might become a major speed bottleneck if groups have
407 lots of users, in which case we could cache the results
409 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
410 const char *dn,
411 char **name, uint32 *name_type, DOM_SID *sid)
413 LDAPMessage *res = NULL;
414 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
415 "objectSid", "sAMAccountType", NULL};
416 ADS_STATUS rc;
417 uint32 atype;
418 DEBUG(3,("ads: dn_lookup\n"));
420 rc = ads_search_retry_dn(ads, &res, dn, attrs);
422 if (!ADS_ERR_OK(rc) || !res) {
423 goto failed;
426 (*name) = ads_pull_username(ads, mem_ctx, res);
428 if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
429 goto failed;
431 (*name_type) = ads_atype_map(atype);
433 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
434 goto failed;
437 if (res)
438 ads_msgfree(ads, res);
440 return True;
442 failed:
443 if (res)
444 ads_msgfree(ads, res);
446 return False;
449 /* Lookup user information from a rid */
450 static NTSTATUS query_user(struct winbindd_domain *domain,
451 TALLOC_CTX *mem_ctx,
452 const DOM_SID *sid,
453 WINBIND_USERINFO *info)
455 ADS_STRUCT *ads = NULL;
456 const char *attrs[] = { "*", NULL };
457 ADS_STATUS rc;
458 int count;
459 LDAPMessage *msg = NULL;
460 char *ldap_exp;
461 char *sidstr;
462 uint32 group_rid;
463 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
464 NET_USER_INFO_3 *user;
466 DEBUG(3,("ads: query_user\n"));
468 info->homedir = NULL;
469 info->shell = NULL;
470 info->primary_gid = (gid_t)-1;
472 /* try netsamlogon cache first */
474 if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL )
477 DEBUG(5,("query_user: Cache lookup succeeded for %s\n",
478 sid_string_static(sid)));
480 sid_compose(&info->user_sid, &domain->sid, user->user_rid);
481 sid_compose(&info->group_sid, &domain->sid, user->group_rid);
483 info->acct_name = unistr2_tdup(mem_ctx, &user->uni_user_name);
484 info->full_name = unistr2_tdup(mem_ctx, &user->uni_full_name);
486 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL,
487 &info->homedir, &info->shell, &info->full_name,
488 &info->primary_gid );
490 SAFE_FREE(user);
492 return NT_STATUS_OK;
495 if ( !winbindd_can_contact_domain(domain)) {
496 DEBUG(8,("query_user: No incoming trust from domain %s\n",
497 domain->name));
499 /* We still need to generate some basic information
500 about the user even if we cannot contact the
501 domain. Most of this stuff we can deduce. */
503 sid_copy( &info->user_sid, sid );
505 /* Assume "Domain Users" for the primary group */
507 sid_compose(&info->group_sid, &domain->sid, DOMAIN_GROUP_RID_USERS );
509 /* Try to fill in what the nss_info backend can do */
511 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL,
512 &info->homedir, &info->shell, &info->full_name,
513 &info->primary_gid );
515 status = NT_STATUS_OK;
516 goto done;
519 /* no cache...do the query */
521 if ( (ads = ads_cached_connection(domain)) == NULL ) {
522 domain->last_status = NT_STATUS_SERVER_DISABLED;
523 goto done;
526 sidstr = sid_binstring(sid);
527 asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
528 rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
529 free(ldap_exp);
530 free(sidstr);
531 if (!ADS_ERR_OK(rc) || !msg) {
532 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
533 sid_string_static(sid), ads_errstr(rc)));
534 goto done;
537 count = ads_count_replies(ads, msg);
538 if (count != 1) {
539 DEBUG(1,("query_user(sid=%s): Not found\n",
540 sid_string_static(sid)));
541 goto done;
544 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
546 nss_get_info_cached( domain, sid, mem_ctx, ads, msg,
547 &info->homedir, &info->shell, &info->full_name,
548 &info->primary_gid );
550 if (info->full_name == NULL) {
551 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
554 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
555 DEBUG(1,("No primary group for %s !?\n",
556 sid_string_static(sid)));
557 goto done;
560 sid_copy(&info->user_sid, sid);
561 sid_compose(&info->group_sid, &domain->sid, group_rid);
563 status = NT_STATUS_OK;
565 DEBUG(3,("ads query_user gave %s\n", info->acct_name));
566 done:
567 if (msg)
568 ads_msgfree(ads, msg);
570 return status;
573 /* Lookup groups a user is a member of - alternate method, for when
574 tokenGroups are not available. */
575 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
576 TALLOC_CTX *mem_ctx,
577 const char *user_dn,
578 DOM_SID *primary_group,
579 size_t *p_num_groups, DOM_SID **user_sids)
581 ADS_STATUS rc;
582 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
583 int count;
584 LDAPMessage *res = NULL;
585 LDAPMessage *msg = NULL;
586 char *ldap_exp;
587 ADS_STRUCT *ads;
588 const char *group_attrs[] = {"objectSid", NULL};
589 char *escaped_dn;
590 size_t num_groups = 0;
592 DEBUG(3,("ads: lookup_usergroups_member\n"));
594 if ( !winbindd_can_contact_domain( domain ) ) {
595 DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
596 domain->name));
597 return NT_STATUS_OK;
600 ads = ads_cached_connection(domain);
602 if (!ads) {
603 domain->last_status = NT_STATUS_SERVER_DISABLED;
604 goto done;
607 if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
608 status = NT_STATUS_NO_MEMORY;
609 goto done;
612 if (!(ldap_exp = talloc_asprintf(mem_ctx, "(&(member=%s)(objectCategory=group))", escaped_dn))) {
613 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
614 SAFE_FREE(escaped_dn);
615 status = NT_STATUS_NO_MEMORY;
616 goto done;
619 SAFE_FREE(escaped_dn);
621 rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
623 if (!ADS_ERR_OK(rc) || !res) {
624 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
625 return ads_ntstatus(rc);
628 count = ads_count_replies(ads, res);
630 *user_sids = NULL;
631 num_groups = 0;
633 /* always add the primary group to the sid array */
634 if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
635 status = NT_STATUS_NO_MEMORY;
636 goto done;
639 if (count > 0) {
640 for (msg = ads_first_entry(ads, res); msg;
641 msg = ads_next_entry(ads, msg)) {
642 DOM_SID group_sid;
644 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
645 DEBUG(1,("No sid for this group ?!?\n"));
646 continue;
649 /* ignore Builtin groups from ADS - Guenther */
650 if (sid_check_is_in_builtin(&group_sid)) {
651 continue;
654 if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
655 &num_groups)) {
656 status = NT_STATUS_NO_MEMORY;
657 goto done;
663 *p_num_groups = num_groups;
664 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
666 DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
667 done:
668 if (res)
669 ads_msgfree(ads, res);
671 return status;
674 /* Lookup groups a user is a member of - alternate method, for when
675 tokenGroups are not available. */
676 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
677 TALLOC_CTX *mem_ctx,
678 const char *user_dn,
679 DOM_SID *primary_group,
680 size_t *p_num_groups, DOM_SID **user_sids)
682 ADS_STATUS rc;
683 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
684 ADS_STRUCT *ads;
685 const char *attrs[] = {"memberOf", NULL};
686 size_t num_groups = 0;
687 DOM_SID *group_sids = NULL;
688 int i;
689 char **strings;
690 size_t num_strings = 0;
693 DEBUG(3,("ads: lookup_usergroups_memberof\n"));
695 if ( !winbindd_can_contact_domain( domain ) ) {
696 DEBUG(10,("lookup_usergroups_memberof: No incoming trust for domain %s\n",
697 domain->name));
698 return NT_STATUS_OK;
701 ads = ads_cached_connection(domain);
703 if (!ads) {
704 domain->last_status = NT_STATUS_SERVER_DISABLED;
705 goto done;
708 rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
709 ADS_EXTENDED_DN_HEX_STRING,
710 &strings, &num_strings);
712 if (!ADS_ERR_OK(rc)) {
713 DEBUG(1,("lookup_usergroups_memberof ads_search member=%s: %s\n",
714 user_dn, ads_errstr(rc)));
715 return ads_ntstatus(rc);
718 *user_sids = NULL;
719 num_groups = 0;
721 /* always add the primary group to the sid array */
722 if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
723 status = NT_STATUS_NO_MEMORY;
724 goto done;
727 group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1);
728 if (!group_sids) {
729 TALLOC_FREE(strings);
730 status = NT_STATUS_NO_MEMORY;
731 goto done;
734 for (i=0; i<num_strings; i++) {
736 if (!ads_get_sid_from_extended_dn(mem_ctx, strings[i],
737 ADS_EXTENDED_DN_HEX_STRING,
738 &(group_sids)[i])) {
739 TALLOC_FREE(group_sids);
740 TALLOC_FREE(strings);
741 status = NT_STATUS_NO_MEMORY;
742 goto done;
746 if (i == 0) {
747 DEBUG(1,("No memberOf for this user?!?\n"));
748 status = NT_STATUS_NO_MEMORY;
749 goto done;
752 for (i=0; i<num_strings; i++) {
754 /* ignore Builtin groups from ADS - Guenther */
755 if (sid_check_is_in_builtin(&group_sids[i])) {
756 continue;
759 if (!add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
760 &num_groups)) {
761 status = NT_STATUS_NO_MEMORY;
762 goto done;
767 *p_num_groups = num_groups;
768 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
770 DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n", user_dn));
771 done:
772 TALLOC_FREE(group_sids);
774 return status;
778 /* Lookup groups a user is a member of. */
779 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
780 TALLOC_CTX *mem_ctx,
781 const DOM_SID *sid,
782 uint32 *p_num_groups, DOM_SID **user_sids)
784 ADS_STRUCT *ads = NULL;
785 const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
786 ADS_STATUS rc;
787 int count;
788 LDAPMessage *msg = NULL;
789 char *user_dn = NULL;
790 DOM_SID *sids;
791 int i;
792 DOM_SID primary_group;
793 uint32 primary_group_rid;
794 fstring sid_string;
795 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
796 size_t num_groups = 0;
798 DEBUG(3,("ads: lookup_usergroups\n"));
799 *p_num_groups = 0;
801 status = lookup_usergroups_cached(domain, mem_ctx, sid,
802 p_num_groups, user_sids);
803 if (NT_STATUS_IS_OK(status)) {
804 return NT_STATUS_OK;
807 if ( !winbindd_can_contact_domain( domain ) ) {
808 DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
809 domain->name));
811 /* Tell the cache manager not to remember this one */
813 return NT_STATUS_SYNCHRONIZATION_REQUIRED;
816 ads = ads_cached_connection(domain);
818 if (!ads) {
819 domain->last_status = NT_STATUS_SERVER_DISABLED;
820 status = NT_STATUS_SERVER_DISABLED;
821 goto done;
824 rc = ads_search_retry_sid(ads, &msg, sid, attrs);
826 if (!ADS_ERR_OK(rc)) {
827 status = ads_ntstatus(rc);
828 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n",
829 sid_to_string(sid_string, sid), ads_errstr(rc)));
830 goto done;
833 count = ads_count_replies(ads, msg);
834 if (count != 1) {
835 status = NT_STATUS_UNSUCCESSFUL;
836 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
837 "invalid number of results (count=%d)\n",
838 sid_to_string(sid_string, sid), count));
839 goto done;
842 if (!msg) {
843 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
844 sid_to_string(sid_string, sid)));
845 status = NT_STATUS_UNSUCCESSFUL;
846 goto done;
849 user_dn = ads_get_dn(ads, msg);
850 if (user_dn == NULL) {
851 status = NT_STATUS_NO_MEMORY;
852 goto done;
855 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
856 DEBUG(1,("%s: No primary group for sid=%s !?\n",
857 domain->name, sid_to_string(sid_string, sid)));
858 goto done;
861 sid_copy(&primary_group, &domain->sid);
862 sid_append_rid(&primary_group, primary_group_rid);
864 count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
866 /* there must always be at least one group in the token,
867 unless we are talking to a buggy Win2k server */
869 /* actually this only happens when the machine account has no read
870 * permissions on the tokenGroup attribute - gd */
872 if (count == 0) {
874 /* no tokenGroups */
876 /* lookup what groups this user is a member of by DN search on
877 * "memberOf" */
879 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
880 &primary_group,
881 &num_groups, user_sids);
882 *p_num_groups = (uint32)num_groups;
883 if (NT_STATUS_IS_OK(status)) {
884 goto done;
887 /* lookup what groups this user is a member of by DN search on
888 * "member" */
890 status = lookup_usergroups_member(domain, mem_ctx, user_dn,
891 &primary_group,
892 &num_groups, user_sids);
893 *p_num_groups = (uint32)num_groups;
894 goto done;
897 *user_sids = NULL;
898 num_groups = 0;
900 if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
901 status = NT_STATUS_NO_MEMORY;
902 goto done;
905 for (i=0;i<count;i++) {
907 /* ignore Builtin groups from ADS - Guenther */
908 if (sid_check_is_in_builtin(&sids[i])) {
909 continue;
912 if (!add_sid_to_array_unique(mem_ctx, &sids[i],
913 user_sids, &num_groups)) {
914 status = NT_STATUS_NO_MEMORY;
915 goto done;
919 *p_num_groups = (uint32)num_groups;
920 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
922 DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
923 sid_to_string(sid_string, sid)));
924 done:
925 ads_memfree(ads, user_dn);
926 ads_msgfree(ads, msg);
927 return status;
931 find the members of a group, given a group rid and domain
933 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
934 TALLOC_CTX *mem_ctx,
935 const DOM_SID *group_sid, uint32 *num_names,
936 DOM_SID **sid_mem, char ***names,
937 uint32 **name_types)
939 ADS_STATUS rc;
940 LDAPMessage *res=NULL;
941 ADS_STRUCT *ads = NULL;
942 char *ldap_exp;
943 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
944 char *sidstr;
945 char **members;
946 int i;
947 size_t num_members;
948 fstring sid_string;
949 ads_control args;
951 DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
952 sid_string_static(group_sid)));
954 *num_names = 0;
956 if ( !winbindd_can_contact_domain( domain ) ) {
957 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
958 domain->name));
959 return NT_STATUS_OK;
962 ads = ads_cached_connection(domain);
964 if (!ads) {
965 domain->last_status = NT_STATUS_SERVER_DISABLED;
966 goto done;
969 if ((sidstr = sid_binstring(group_sid)) == NULL) {
970 status = NT_STATUS_NO_MEMORY;
971 goto done;
974 /* search for all members of the group */
975 if (!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)",sidstr))) {
976 SAFE_FREE(sidstr);
977 DEBUG(1, ("ads: lookup_groupmem: tallloc_asprintf for ldap_exp failed!\n"));
978 status = NT_STATUS_NO_MEMORY;
979 goto done;
981 SAFE_FREE(sidstr);
983 members = NULL;
984 num_members = 0;
986 args.control = ADS_EXTENDED_DN_OID;
987 args.val = ADS_EXTENDED_DN_HEX_STRING;
988 args.critical = True;
990 rc = ads_ranged_search(ads, mem_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
991 ldap_exp, &args, "member", &members, &num_members);
993 if (!ADS_ERR_OK(rc)) {
994 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
995 status = NT_STATUS_UNSUCCESSFUL;
996 goto done;
999 /* now we need to turn a list of members into rids, names and name types
1000 the problem is that the members are in the form of distinguised names
1003 if (num_members) {
1004 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
1005 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
1006 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
1008 if ((members == NULL) || (*sid_mem == NULL) ||
1009 (*name_types == NULL) || (*names == NULL)) {
1010 DEBUG(1, ("talloc failed\n"));
1011 status = NT_STATUS_NO_MEMORY;
1012 goto done;
1014 } else {
1015 (*sid_mem) = NULL;
1016 (*name_types) = NULL;
1017 (*names) = NULL;
1020 for (i=0;i<num_members;i++) {
1021 uint32 name_type;
1022 char *name, *domain_name, *dn;
1023 DOM_SID sid;
1025 if ((!ads_get_sid_from_extended_dn(mem_ctx, members[i], ADS_EXTENDED_DN_HEX_STRING, &sid)) ||
1026 (!ads_get_dn_from_extended_dn(mem_ctx, members[i], &dn)))
1028 status = NT_STATUS_INVALID_PARAMETER;
1029 goto done;
1032 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name, &name_type)) {
1034 DEBUG(10,("ads: lookup_groupmem: got sid %s from cache\n",
1035 sid_string_static(&sid)));
1037 (*names)[*num_names] = CONST_DISCARD(char *,name);
1038 (*name_types)[*num_names] = name_type;
1039 sid_copy(&(*sid_mem)[*num_names], &sid);
1041 (*num_names)++;
1043 continue;
1046 if (dn_lookup(ads, mem_ctx, dn, &name, &name_type, &sid)) {
1048 DEBUG(10,("ads: lookup_groupmem: got sid %s from dn_lookup\n",
1049 sid_string_static(&sid)));
1051 (*names)[*num_names] = name;
1052 (*name_types)[*num_names] = name_type;
1053 sid_copy(&(*sid_mem)[*num_names], &sid);
1055 (*num_names)++;
1060 status = NT_STATUS_OK;
1061 DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n", sid_to_string(sid_string, group_sid)));
1062 done:
1064 if (res)
1065 ads_msgfree(ads, res);
1067 return status;
1070 /* find the sequence number for a domain */
1071 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1073 ADS_STRUCT *ads = NULL;
1074 ADS_STATUS rc;
1076 DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1078 if ( !winbindd_can_contact_domain( domain ) ) {
1079 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1080 domain->name));
1081 *seq = time(NULL);
1082 return NT_STATUS_OK;
1085 *seq = DOM_SEQUENCE_NONE;
1087 ads = ads_cached_connection(domain);
1089 if (!ads) {
1090 domain->last_status = NT_STATUS_SERVER_DISABLED;
1091 return NT_STATUS_UNSUCCESSFUL;
1094 rc = ads_USN(ads, seq);
1096 if (!ADS_ERR_OK(rc)) {
1098 /* its a dead connection, destroy it */
1100 if (domain->private_data) {
1101 ads = (ADS_STRUCT *)domain->private_data;
1102 ads->is_mine = True;
1103 ads_destroy(&ads);
1104 ads_kdestroy("MEMORY:winbind_ccache");
1105 domain->private_data = NULL;
1108 return ads_ntstatus(rc);
1111 /* get a list of trusted domains */
1112 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1113 TALLOC_CTX *mem_ctx,
1114 uint32 *num_domains,
1115 char ***names,
1116 char ***alt_names,
1117 DOM_SID **dom_sids)
1119 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1120 struct ds_domain_trust *domains = NULL;
1121 int count = 0;
1122 int i;
1123 uint32 flags;
1124 struct rpc_pipe_client *cli;
1125 uint32 fr_flags = (DS_DOMAIN_IN_FOREST | DS_DOMAIN_TREE_ROOT);
1126 int ret_count;
1128 DEBUG(3,("ads: trusted_domains\n"));
1130 *num_domains = 0;
1131 *alt_names = NULL;
1132 *names = NULL;
1133 *dom_sids = NULL;
1135 /* If this is our primary domain or a root in our forest,
1136 query for all trusts. If not, then just look for domain
1137 trusts in the target forest */
1139 if ( domain->primary ||
1140 ((domain->domain_flags&fr_flags) == fr_flags) )
1142 flags = DS_DOMAIN_DIRECT_OUTBOUND |
1143 DS_DOMAIN_DIRECT_INBOUND |
1144 DS_DOMAIN_IN_FOREST;
1145 } else {
1146 flags = DS_DOMAIN_IN_FOREST;
1149 result = cm_connect_netlogon(domain, &cli);
1151 if (!NT_STATUS_IS_OK(result)) {
1152 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1153 "for PIPE_NETLOGON (%s)\n",
1154 domain->name, nt_errstr(result)));
1155 return NT_STATUS_UNSUCCESSFUL;
1158 if ( NT_STATUS_IS_OK(result) ) {
1159 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
1160 cli->cli->desthost,
1161 flags, &domains,
1162 (unsigned int *)&count);
1165 if ( NT_STATUS_IS_OK(result) && count) {
1167 /* Allocate memory for trusted domain names and sids */
1169 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1170 DEBUG(0, ("trusted_domains: out of memory\n"));
1171 return NT_STATUS_NO_MEMORY;
1174 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1175 DEBUG(0, ("trusted_domains: out of memory\n"));
1176 return NT_STATUS_NO_MEMORY;
1179 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) {
1180 DEBUG(0, ("trusted_domains: out of memory\n"));
1181 return NT_STATUS_NO_MEMORY;
1184 /* Copy across names and sids */
1187 ret_count = 0;
1188 for (i = 0; i < count; i++) {
1189 struct winbindd_domain d;
1191 /* drop external trusts if this is not our primary
1192 domain. This means that the returned number of
1193 domains may be less that the ones actually trusted
1194 by the DC. */
1196 if ( (domains[i].trust_attributes == DS_DOMAIN_TRUST_ATTRIB_QUARANTINED_DOMAIN) &&
1197 !domain->primary )
1199 DEBUG(10,("trusted_domains: Skipping external trusted domain "
1200 "%s because it is outside of our primary domain\n",
1201 domains[i].netbios_domain));
1202 continue;
1205 (*names)[ret_count] = domains[i].netbios_domain;
1206 (*alt_names)[ret_count] = domains[i].dns_domain;
1207 sid_copy(&(*dom_sids)[ret_count], &domains[i].sid);
1209 /* add to the trusted domain cache */
1211 fstrcpy( d.name, domains[i].netbios_domain );
1212 fstrcpy( d.alt_name, domains[i].dns_domain );
1213 sid_copy( &d.sid, &domains[i].sid );
1215 /* This gets a little tricky. If we are
1216 following a transitive forest trust, then
1217 innerit the flags, type, and attrins from
1218 the domain we queried to make sure we don't
1219 record the view of the trust from the wrong
1220 side. Always view it from the side of our
1221 primary domain. --jerry */
1222 if ( domain->primary ||
1223 ((domain->domain_flags&fr_flags) == fr_flags) )
1225 DEBUG(10,("trusted_domains(ads): Storing trust "
1226 "flags for domain %s\n", d.alt_name));
1228 /* Look this up in cache to make sure
1229 we have the current trust flags and
1230 attributes */
1232 d.domain_flags = domains[i].flags;
1233 d.domain_type = domains[i].trust_type;
1234 d.domain_trust_attribs = domains[i].trust_attributes;
1235 } else {
1236 DEBUG(10,("trusted_domains(ads): Inheriting trust "
1237 "flags for domain %s\n", d.alt_name));
1238 d.domain_flags = domain->domain_flags;
1239 d.domain_type = domain->domain_type;
1240 d.domain_trust_attribs = domain->domain_trust_attribs;
1243 wcache_tdc_add_domain( &d );
1245 ret_count++;
1249 *num_domains = ret_count;
1252 return result;
1255 /* the ADS backend methods are exposed via this structure */
1256 struct winbindd_methods ads_methods = {
1257 True,
1258 query_user_list,
1259 enum_dom_groups,
1260 enum_local_groups,
1261 msrpc_name_to_sid,
1262 msrpc_sid_to_name,
1263 msrpc_rids_to_names,
1264 query_user,
1265 lookup_usergroups,
1266 msrpc_lookup_useraliases,
1267 lookup_groupmem,
1268 sequence_number,
1269 msrpc_lockout_policy,
1270 msrpc_password_policy,
1271 trusted_domains,
1274 #endif