s3-winbindd_ads: use the reconnect methods instead of the rpc methods directly
[ira/wip.git] / source3 / winbindd / winbindd_ads.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind ADS backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
8    Copyright (C) Gerald (Jerry) Carter 2004
9    
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.
14    
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.
19    
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/>.
22 */
23
24 #include "includes.h"
25 #include "winbindd.h"
26
27 #ifdef HAVE_ADS
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
31
32 extern struct winbindd_methods reconnect_methods;
33
34 /*
35   return our ads connections structure for a domain. We keep the connection
36   open to make things faster
37 */
38 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
39 {
40         ADS_STRUCT *ads;
41         ADS_STATUS status;
42         fstring dc_name;
43         struct sockaddr_storage dc_ss;
44
45         DEBUG(10,("ads_cached_connection\n"));
46
47         if (domain->private_data) {
48
49                 time_t expire;
50                 time_t now = time(NULL);
51
52                 /* check for a valid structure */
53                 ads = (ADS_STRUCT *)domain->private_data;
54
55                 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
56
57                 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
58                           (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
59
60                 if ( ads->config.realm && (expire > now)) {
61                         return ads;
62                 } else {
63                         /* we own this ADS_STRUCT so make sure it goes away */
64                         DEBUG(7,("Deleting expired krb5 credential cache\n"));
65                         ads->is_mine = True;
66                         ads_destroy( &ads );
67                         ads_kdestroy("MEMORY:winbind_ccache");
68                         domain->private_data = NULL;
69                 }
70         }
71
72         /* we don't want this to affect the users ccache */
73         setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
74
75         ads = ads_init(domain->alt_name, domain->name, NULL);
76         if (!ads) {
77                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
78                 return NULL;
79         }
80
81         /* the machine acct password might have change - fetch it every time */
82
83         SAFE_FREE(ads->auth.password);
84         SAFE_FREE(ads->auth.realm);
85
86         if ( IS_DC ) {
87                 DOM_SID sid;
88                 time_t last_set_time;
89
90                 if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, &sid, &last_set_time ) ) {
91                         ads_destroy( &ads );
92                         return NULL;
93                 }
94                 ads->auth.realm = SMB_STRDUP( ads->server.realm );
95                 strupper_m( ads->auth.realm );
96         }
97         else {
98                 struct winbindd_domain *our_domain = domain;
99
100                 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
101
102                 /* always give preference to the alt_name in our
103                    primary domain if possible */
104
105                 if ( !domain->primary )
106                         our_domain = find_our_domain();
107
108                 if ( our_domain->alt_name[0] != '\0' ) {
109                         ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
110                         strupper_m( ads->auth.realm );
111                 }
112                 else
113                         ads->auth.realm = SMB_STRDUP( lp_realm() );
114         }
115
116         ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
117
118         /* Setup the server affinity cache.  We don't reaally care
119            about the name.  Just setup affinity and the KRB5_CONFIG
120            file. */
121
122         get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ss );
123
124         status = ads_connect(ads);
125         if (!ADS_ERR_OK(status) || !ads->config.realm) {
126                 DEBUG(1,("ads_connect for domain %s failed: %s\n",
127                          domain->name, ads_errstr(status)));
128                 ads_destroy(&ads);
129
130                 /* if we get ECONNREFUSED then it might be a NT4
131                    server, fall back to MSRPC */
132                 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
133                     status.err.rc == ECONNREFUSED) {
134                         /* 'reconnect_methods' is the MS-RPC backend. */
135                         DEBUG(1,("Trying MSRPC methods\n"));
136                         domain->backend = &reconnect_methods;
137                 }
138                 return NULL;
139         }
140
141         /* set the flag that says we don't own the memory even
142            though we do so that ads_destroy() won't destroy the
143            structure we pass back by reference */
144
145         ads->is_mine = False;
146
147         domain->private_data = (void *)ads;
148         return ads;
149 }
150
151
152 /* Query display info for a realm. This is the basic user list fn */
153 static NTSTATUS query_user_list(struct winbindd_domain *domain,
154                                TALLOC_CTX *mem_ctx,
155                                uint32 *num_entries, 
156                                WINBIND_USERINFO **info)
157 {
158         ADS_STRUCT *ads = NULL;
159         const char *attrs[] = { "*", NULL };
160         int i, count;
161         ADS_STATUS rc;
162         LDAPMessage *res = NULL;
163         LDAPMessage *msg = NULL;
164         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
165
166         *num_entries = 0;
167
168         DEBUG(3,("ads: query_user_list\n"));
169
170         if ( !winbindd_can_contact_domain( domain ) ) {
171                 DEBUG(10,("query_user_list: No incoming trust for domain %s\n",
172                           domain->name));               
173                 return NT_STATUS_OK;
174         }
175
176         ads = ads_cached_connection(domain);
177         
178         if (!ads) {
179                 domain->last_status = NT_STATUS_SERVER_DISABLED;
180                 goto done;
181         }
182
183         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
184         if (!ADS_ERR_OK(rc) || !res) {
185                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
186                 goto done;
187         }
188
189         count = ads_count_replies(ads, res);
190         if (count == 0) {
191                 DEBUG(1,("query_user_list: No users found\n"));
192                 goto done;
193         }
194
195         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count);
196         if (!*info) {
197                 status = NT_STATUS_NO_MEMORY;
198                 goto done;
199         }
200
201         i = 0;
202
203         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
204                 char *name, *gecos = NULL;
205                 char *homedir = NULL;
206                 char *shell = NULL;
207                 uint32 group;
208                 uint32 atype;
209                 DOM_SID user_sid;
210                 gid_t primary_gid = (gid_t)-1;
211
212                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
213                     ads_atype_map(atype) != SID_NAME_USER) {
214                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
215                         continue;
216                 }
217
218                 name = ads_pull_username(ads, mem_ctx, msg);
219
220                 if ( ads_pull_sid( ads, msg, "objectSid", &user_sid ) ) {
221                         status = nss_get_info_cached( domain, &user_sid, mem_ctx, 
222                                                ads, msg, &homedir, &shell, &gecos,
223                                                &primary_gid );
224                 }
225
226                 if (gecos == NULL) {
227                         gecos = ads_pull_string(ads, mem_ctx, msg, "name");
228                 }
229         
230                 if (!ads_pull_sid(ads, msg, "objectSid",
231                                   &(*info)[i].user_sid)) {
232                         DEBUG(1,("No sid for %s !?\n", name));
233                         continue;
234                 }
235                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
236                         DEBUG(1,("No primary group for %s !?\n", name));
237                         continue;
238                 }
239
240                 (*info)[i].acct_name = name;
241                 (*info)[i].full_name = gecos;
242                 (*info)[i].homedir = homedir;
243                 (*info)[i].shell = shell;
244                 (*info)[i].primary_gid = primary_gid;
245                 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
246                 i++;
247         }
248
249         (*num_entries) = i;
250         status = NT_STATUS_OK;
251
252         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
253
254 done:
255         if (res) 
256                 ads_msgfree(ads, res);
257
258         return status;
259 }
260
261 /* list all domain groups */
262 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
263                                 TALLOC_CTX *mem_ctx,
264                                 uint32 *num_entries, 
265                                 struct acct_info **info)
266 {
267         ADS_STRUCT *ads = NULL;
268         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
269                                "name", "objectSid", NULL};
270         int i, count;
271         ADS_STATUS rc;
272         LDAPMessage *res = NULL;
273         LDAPMessage *msg = NULL;
274         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
275         const char *filter;
276         bool enum_dom_local_groups = False;
277
278         *num_entries = 0;
279
280         DEBUG(3,("ads: enum_dom_groups\n"));
281
282         if ( !winbindd_can_contact_domain( domain ) ) {
283                 DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n",
284                           domain->name));               
285                 return NT_STATUS_OK;
286         }
287
288         /* only grab domain local groups for our domain */
289         if ( domain->active_directory && strequal(lp_realm(), domain->alt_name)  ) {
290                 enum_dom_local_groups = True;
291         }
292
293         /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
294          * rollup-fixes:
295          *
296          * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
297          * default value, it MUST be absent. In case of extensible matching the
298          * "dnattr" boolean defaults to FALSE and so it must be only be present
299          * when set to TRUE. 
300          *
301          * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
302          * filter using bitwise matching rule then the buggy AD fails to decode
303          * the extensible match. As a workaround set it to TRUE and thereby add
304          * the dnAttributes "dn" field to cope with those older AD versions.
305          * It should not harm and won't put any additional load on the AD since
306          * none of the dn components have a bitmask-attribute.
307          *
308          * Thanks to Ralf Haferkamp for input and testing - Guenther */
309
310         filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))", 
311                                  ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
312                                  ADS_LDAP_MATCHING_RULE_BIT_AND, 
313                                  enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
314
315         if (filter == NULL) {
316                 status = NT_STATUS_NO_MEMORY;
317                 goto done;
318         }
319
320         ads = ads_cached_connection(domain);
321
322         if (!ads) {
323                 domain->last_status = NT_STATUS_SERVER_DISABLED;
324                 goto done;
325         }
326
327         rc = ads_search_retry(ads, &res, filter, attrs);
328         if (!ADS_ERR_OK(rc) || !res) {
329                 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
330                 goto done;
331         }
332
333         count = ads_count_replies(ads, res);
334         if (count == 0) {
335                 DEBUG(1,("enum_dom_groups: No groups found\n"));
336                 goto done;
337         }
338
339         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
340         if (!*info) {
341                 status = NT_STATUS_NO_MEMORY;
342                 goto done;
343         }
344
345         i = 0;
346         
347         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
348                 char *name, *gecos;
349                 DOM_SID sid;
350                 uint32 rid;
351
352                 name = ads_pull_username(ads, mem_ctx, msg);
353                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
354                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
355                         DEBUG(1,("No sid for %s !?\n", name));
356                         continue;
357                 }
358
359                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
360                         DEBUG(1,("No rid for %s !?\n", name));
361                         continue;
362                 }
363
364                 fstrcpy((*info)[i].acct_name, name);
365                 fstrcpy((*info)[i].acct_desc, gecos);
366                 (*info)[i].rid = rid;
367                 i++;
368         }
369
370         (*num_entries) = i;
371
372         status = NT_STATUS_OK;
373
374         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
375
376 done:
377         if (res) 
378                 ads_msgfree(ads, res);
379
380         return status;
381 }
382
383 /* list all domain local groups */
384 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
385                                 TALLOC_CTX *mem_ctx,
386                                 uint32 *num_entries, 
387                                 struct acct_info **info)
388 {
389         /*
390          * This is a stub function only as we returned the domain 
391          * local groups in enum_dom_groups() if the domain->native field
392          * was true.  This is a simple performance optimization when
393          * using LDAP.
394          *
395          * if we ever need to enumerate domain local groups separately, 
396          * then this optimization in enum_dom_groups() will need
397          * to be split out
398          */
399         *num_entries = 0;
400         
401         return NT_STATUS_OK;
402 }
403
404 /* convert a single name to a sid in a domain - use rpc methods */
405 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
406                             TALLOC_CTX *mem_ctx,
407                             enum winbindd_cmd orig_cmd,
408                             const char *domain_name,
409                             const char *name,
410                             DOM_SID *sid,
411                             enum lsa_SidType *type)
412 {
413         return reconnect_methods.name_to_sid(domain, mem_ctx, orig_cmd,
414                                              domain_name, name,
415                                              sid, type);
416 }
417
418 /* convert a domain SID to a user or group name - use rpc methods */
419 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
420                             TALLOC_CTX *mem_ctx,
421                             const DOM_SID *sid,
422                             char **domain_name,
423                             char **name,
424                             enum lsa_SidType *type)
425 {
426         return reconnect_methods.sid_to_name(domain, mem_ctx, sid,
427                                              domain_name, name, type);
428 }
429
430 /* convert a list of rids to names - use rpc methods */
431 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
432                               TALLOC_CTX *mem_ctx,
433                               const DOM_SID *sid,
434                               uint32 *rids,
435                               size_t num_rids,
436                               char **domain_name,
437                               char ***names,
438                               enum lsa_SidType **types)
439 {
440         return reconnect_methods.rids_to_names(domain, mem_ctx, sid,
441                                                rids, num_rids,
442                                                domain_name, names, types);
443 }
444
445 /* If you are looking for "dn_lookup": Yes, it used to be here!
446  * It has gone now since it was a major speed bottleneck in
447  * lookup_groupmem (its only use). It has been replaced by
448  * an rpc lookup sids call... R.I.P. */
449
450 /* Lookup user information from a rid */
451 static NTSTATUS query_user(struct winbindd_domain *domain, 
452                            TALLOC_CTX *mem_ctx, 
453                            const DOM_SID *sid, 
454                            WINBIND_USERINFO *info)
455 {
456         ADS_STRUCT *ads = NULL;
457         const char *attrs[] = { "*", NULL };
458         ADS_STATUS rc;
459         int count;
460         LDAPMessage *msg = NULL;
461         char *ldap_exp;
462         char *sidstr;
463         uint32 group_rid;
464         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
465         struct netr_SamInfo3 *user = NULL;
466
467         DEBUG(3,("ads: query_user\n"));
468
469         info->homedir = NULL;
470         info->shell = NULL;
471         info->primary_gid = (gid_t)-1;
472
473         /* try netsamlogon cache first */
474                         
475         if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL ) 
476         {
477                                 
478                 DEBUG(5,("query_user: Cache lookup succeeded for %s\n", 
479                          sid_string_dbg(sid)));
480
481                 sid_compose(&info->user_sid, &domain->sid, user->base.rid);
482                 sid_compose(&info->group_sid, &domain->sid, user->base.primary_gid);
483                                 
484                 info->acct_name = talloc_strdup(mem_ctx, user->base.account_name.string);
485                 info->full_name = talloc_strdup(mem_ctx, user->base.full_name.string);
486                 
487                 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, 
488                               &info->homedir, &info->shell, &info->full_name, 
489                               &info->primary_gid );     
490
491                 TALLOC_FREE(user);
492                                 
493                 return NT_STATUS_OK;
494         }
495
496         if ( !winbindd_can_contact_domain(domain)) {
497                 DEBUG(8,("query_user: No incoming trust from domain %s\n",
498                          domain->name));
499
500                 /* We still need to generate some basic information
501                    about the user even if we cannot contact the 
502                    domain.  Most of this stuff we can deduce. */
503
504                 sid_copy( &info->user_sid, sid );
505
506                 /* Assume "Domain Users" for the primary group */
507
508                 sid_compose(&info->group_sid, &domain->sid, DOMAIN_GROUP_RID_USERS );
509
510                 /* Try to fill in what the nss_info backend can do */
511
512                 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, 
513                               &info->homedir, &info->shell, &info->full_name, 
514                               &info->primary_gid );
515
516                 status = NT_STATUS_OK;
517                 goto done;
518         }
519
520         /* no cache...do the query */
521
522         if ( (ads = ads_cached_connection(domain)) == NULL ) {
523                 domain->last_status = NT_STATUS_SERVER_DISABLED;
524                 goto done;
525         }
526
527         sidstr = sid_binstring(sid);
528         asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
529         rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
530         free(ldap_exp);
531         free(sidstr);
532         if (!ADS_ERR_OK(rc) || !msg) {
533                 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
534                          sid_string_dbg(sid), ads_errstr(rc)));
535                 goto done;
536         }
537
538         count = ads_count_replies(ads, msg);
539         if (count != 1) {
540                 DEBUG(1,("query_user(sid=%s): Not found\n",
541                          sid_string_dbg(sid)));
542                 goto done;
543         }
544
545         info->acct_name = ads_pull_username(ads, mem_ctx, msg);
546
547         nss_get_info_cached( domain, sid, mem_ctx, ads, msg, 
548                       &info->homedir, &info->shell, &info->full_name, 
549                       &info->primary_gid );     
550
551         if (info->full_name == NULL) {
552                 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
553         }
554
555         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
556                 DEBUG(1,("No primary group for %s !?\n",
557                          sid_string_dbg(sid)));
558                 goto done;
559         }
560
561         sid_copy(&info->user_sid, sid);
562         sid_compose(&info->group_sid, &domain->sid, group_rid);
563
564         status = NT_STATUS_OK;
565
566         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
567 done:
568         if (msg) 
569                 ads_msgfree(ads, msg);
570
571         return status;
572 }
573
574 /* Lookup groups a user is a member of - alternate method, for when
575    tokenGroups are not available. */
576 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
577                                          TALLOC_CTX *mem_ctx,
578                                          const char *user_dn, 
579                                          DOM_SID *primary_group,
580                                          size_t *p_num_groups, DOM_SID **user_sids)
581 {
582         ADS_STATUS rc;
583         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
584         int count;
585         LDAPMessage *res = NULL;
586         LDAPMessage *msg = NULL;
587         char *ldap_exp;
588         ADS_STRUCT *ads;
589         const char *group_attrs[] = {"objectSid", NULL};
590         char *escaped_dn;
591         size_t num_groups = 0;
592
593         DEBUG(3,("ads: lookup_usergroups_member\n"));
594
595         if ( !winbindd_can_contact_domain( domain ) ) {
596                 DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
597                           domain->name));               
598                 return NT_STATUS_OK;
599         }
600
601         ads = ads_cached_connection(domain);
602
603         if (!ads) {
604                 domain->last_status = NT_STATUS_SERVER_DISABLED;
605                 goto done;
606         }
607
608         if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
609                 status = NT_STATUS_NO_MEMORY;
610                 goto done;
611         }
612
613         ldap_exp = talloc_asprintf(mem_ctx,
614                 "(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))",
615                 escaped_dn,
616                 ADS_LDAP_MATCHING_RULE_BIT_AND,
617                 GROUP_TYPE_SECURITY_ENABLED);
618         if (!ldap_exp) {
619                 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
620                 SAFE_FREE(escaped_dn);
621                 status = NT_STATUS_NO_MEMORY;
622                 goto done;
623         }
624
625         SAFE_FREE(escaped_dn);
626
627         rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
628         
629         if (!ADS_ERR_OK(rc) || !res) {
630                 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
631                 return ads_ntstatus(rc);
632         }
633         
634         count = ads_count_replies(ads, res);
635         
636         *user_sids = NULL;
637         num_groups = 0;
638
639         /* always add the primary group to the sid array */
640         status = add_sid_to_array(mem_ctx, primary_group, user_sids,
641                                   &num_groups);
642         if (!NT_STATUS_IS_OK(status)) {
643                 goto done;
644         }
645
646         if (count > 0) {
647                 for (msg = ads_first_entry(ads, res); msg;
648                      msg = ads_next_entry(ads, msg)) {
649                         DOM_SID group_sid;
650                 
651                         if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
652                                 DEBUG(1,("No sid for this group ?!?\n"));
653                                 continue;
654                         }
655         
656                         /* ignore Builtin groups from ADS - Guenther */
657                         if (sid_check_is_in_builtin(&group_sid)) {
658                                 continue;
659                         }
660
661                         status = add_sid_to_array(mem_ctx, &group_sid,
662                                                   user_sids, &num_groups);
663                         if (!NT_STATUS_IS_OK(status)) {
664                                 goto done;
665                         }
666                 }
667
668         }
669
670         *p_num_groups = num_groups;
671         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
672
673         DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
674 done:
675         if (res) 
676                 ads_msgfree(ads, res);
677
678         return status;
679 }
680
681 /* Lookup groups a user is a member of - alternate method, for when
682    tokenGroups are not available. */
683 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
684                                            TALLOC_CTX *mem_ctx,
685                                            const char *user_dn,
686                                            DOM_SID *primary_group,
687                                            size_t *p_num_groups,
688                                            DOM_SID **user_sids)
689 {
690         ADS_STATUS rc;
691         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
692         ADS_STRUCT *ads;
693         const char *attrs[] = {"memberOf", NULL};
694         size_t num_groups = 0;
695         DOM_SID *group_sids = NULL;
696         int i;
697         char **strings = NULL;
698         size_t num_strings = 0, num_sids = 0;
699
700
701         DEBUG(3,("ads: lookup_usergroups_memberof\n"));
702
703         if ( !winbindd_can_contact_domain( domain ) ) {
704                 DEBUG(10,("lookup_usergroups_memberof: No incoming trust for "
705                           "domain %s\n", domain->name));
706                 return NT_STATUS_OK;
707         }
708
709         ads = ads_cached_connection(domain);
710
711         if (!ads) {
712                 domain->last_status = NT_STATUS_SERVER_DISABLED;
713                 return NT_STATUS_UNSUCCESSFUL;
714         }
715
716         rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
717                                                  ADS_EXTENDED_DN_HEX_STRING,
718                                                  &strings, &num_strings);
719
720         if (!ADS_ERR_OK(rc)) {
721                 DEBUG(1,("lookup_usergroups_memberof ads_search "
722                         "member=%s: %s\n", user_dn, ads_errstr(rc)));
723                 return ads_ntstatus(rc);
724         }
725
726         *user_sids = NULL;
727         num_groups = 0;
728
729         /* always add the primary group to the sid array */
730         status = add_sid_to_array(mem_ctx, primary_group, user_sids,
731                                   &num_groups);
732         if (!NT_STATUS_IS_OK(status)) {
733                 goto done;
734         }
735
736         group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1);
737         if (!group_sids) {
738                 status = NT_STATUS_NO_MEMORY;
739                 goto done;
740         }
741
742         for (i=0; i<num_strings; i++) {
743                 rc = ads_get_sid_from_extended_dn(mem_ctx, strings[i],
744                                                   ADS_EXTENDED_DN_HEX_STRING,
745                                                   &(group_sids)[i]);
746                 if (!ADS_ERR_OK(rc)) {
747                         /* ignore members without SIDs */
748                         if (NT_STATUS_EQUAL(ads_ntstatus(rc),
749                             NT_STATUS_NOT_FOUND)) {
750                                 continue;
751                         }
752                         else {
753                                 status = ads_ntstatus(rc);
754                                 goto done;
755                         }
756                 }
757                 num_sids++;
758         }
759
760         if (i == 0) {
761                 DEBUG(1,("No memberOf for this user?!?\n"));
762                 status = NT_STATUS_NO_MEMORY;
763                 goto done;
764         }
765
766         for (i=0; i<num_sids; i++) {
767
768                 /* ignore Builtin groups from ADS - Guenther */
769                 if (sid_check_is_in_builtin(&group_sids[i])) {
770                         continue;
771                 }
772
773                 status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
774                                           &num_groups);
775                 if (!NT_STATUS_IS_OK(status)) {
776                         goto done;
777                 }
778
779         }
780
781         *p_num_groups = num_groups;
782         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
783
784         DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n",
785                 user_dn));
786
787 done:
788         TALLOC_FREE(strings);
789         TALLOC_FREE(group_sids);
790
791         return status;
792 }
793
794
795 /* Lookup groups a user is a member of. */
796 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
797                                   TALLOC_CTX *mem_ctx,
798                                   const DOM_SID *sid, 
799                                   uint32 *p_num_groups, DOM_SID **user_sids)
800 {
801         ADS_STRUCT *ads = NULL;
802         const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
803         ADS_STATUS rc;
804         int count;
805         LDAPMessage *msg = NULL;
806         char *user_dn = NULL;
807         DOM_SID *sids;
808         int i;
809         DOM_SID primary_group;
810         uint32 primary_group_rid;
811         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
812         size_t num_groups = 0;
813
814         DEBUG(3,("ads: lookup_usergroups\n"));
815         *p_num_groups = 0;
816
817         status = lookup_usergroups_cached(domain, mem_ctx, sid, 
818                                           p_num_groups, user_sids);
819         if (NT_STATUS_IS_OK(status)) {
820                 return NT_STATUS_OK;
821         }
822
823         if ( !winbindd_can_contact_domain( domain ) ) {
824                 DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
825                           domain->name));
826
827                 /* Tell the cache manager not to remember this one */
828
829                 return NT_STATUS_SYNCHRONIZATION_REQUIRED;
830         }
831
832         ads = ads_cached_connection(domain);
833         
834         if (!ads) {
835                 domain->last_status = NT_STATUS_SERVER_DISABLED;
836                 status = NT_STATUS_SERVER_DISABLED;
837                 goto done;
838         }
839
840         rc = ads_search_retry_sid(ads, &msg, sid, attrs);
841
842         if (!ADS_ERR_OK(rc)) {
843                 status = ads_ntstatus(rc);
844                 DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
845                           "%s\n", sid_string_dbg(sid), ads_errstr(rc)));
846                 goto done;
847         }
848         
849         count = ads_count_replies(ads, msg);
850         if (count != 1) {
851                 status = NT_STATUS_UNSUCCESSFUL;
852                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
853                          "invalid number of results (count=%d)\n", 
854                          sid_string_dbg(sid), count));
855                 goto done;
856         }
857
858         if (!msg) {
859                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", 
860                          sid_string_dbg(sid)));
861                 status = NT_STATUS_UNSUCCESSFUL;
862                 goto done;
863         }
864
865         user_dn = ads_get_dn(ads, msg);
866         if (user_dn == NULL) {
867                 status = NT_STATUS_NO_MEMORY;
868                 goto done;
869         }
870
871         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
872                 DEBUG(1,("%s: No primary group for sid=%s !?\n", 
873                          domain->name, sid_string_dbg(sid)));
874                 goto done;
875         }
876
877         sid_copy(&primary_group, &domain->sid);
878         sid_append_rid(&primary_group, primary_group_rid);
879
880         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
881
882         /* there must always be at least one group in the token, 
883            unless we are talking to a buggy Win2k server */
884
885         /* actually this only happens when the machine account has no read
886          * permissions on the tokenGroup attribute - gd */
887
888         if (count == 0) {
889
890                 /* no tokenGroups */
891                 
892                 /* lookup what groups this user is a member of by DN search on
893                  * "memberOf" */
894
895                 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
896                                                     &primary_group,
897                                                     &num_groups, user_sids);
898                 *p_num_groups = (uint32)num_groups;
899                 if (NT_STATUS_IS_OK(status)) {
900                         goto done;
901                 }
902
903                 /* lookup what groups this user is a member of by DN search on
904                  * "member" */
905
906                 status = lookup_usergroups_member(domain, mem_ctx, user_dn, 
907                                                   &primary_group,
908                                                   &num_groups, user_sids);
909                 *p_num_groups = (uint32)num_groups;
910                 goto done;
911         }
912
913         *user_sids = NULL;
914         num_groups = 0;
915
916         status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
917                                   &num_groups);
918         if (!NT_STATUS_IS_OK(status)) {
919                 goto done;
920         }
921         
922         for (i=0;i<count;i++) {
923
924                 /* ignore Builtin groups from ADS - Guenther */
925                 if (sid_check_is_in_builtin(&sids[i])) {
926                         continue;
927                 }
928
929                 status = add_sid_to_array_unique(mem_ctx, &sids[i],
930                                                  user_sids, &num_groups);
931                 if (!NT_STATUS_IS_OK(status)) {
932                         goto done;
933                 }
934         }
935
936         *p_num_groups = (uint32)num_groups;
937         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
938
939         DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
940                  sid_string_dbg(sid)));
941 done:
942         ads_memfree(ads, user_dn);
943         ads_msgfree(ads, msg);
944         return status;
945 }
946
947 /* Lookup aliases a user is member of - use rpc methods */
948 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
949                                    TALLOC_CTX *mem_ctx,
950                                    uint32 num_sids, const DOM_SID *sids,
951                                    uint32 *num_aliases, uint32 **alias_rids)
952 {
953         return reconnect_methods.lookup_useraliases(domain, mem_ctx,
954                                                     num_sids, sids,
955                                                     num_aliases,
956                                                     alias_rids);
957 }
958
959 /*
960   find the members of a group, given a group rid and domain
961  */
962 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
963                                 TALLOC_CTX *mem_ctx,
964                                 const DOM_SID *group_sid, uint32 *num_names,
965                                 DOM_SID **sid_mem, char ***names,
966                                 uint32 **name_types)
967 {
968         ADS_STATUS rc;
969         ADS_STRUCT *ads = NULL;
970         char *ldap_exp;
971         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
972         char *sidbinstr;
973         char **members = NULL;
974         int i;
975         size_t num_members = 0;
976         ads_control args;
977         struct rpc_pipe_client *cli;
978         POLICY_HND lsa_policy;
979         DOM_SID *sid_mem_nocache = NULL;
980         char **names_nocache = NULL;
981         enum lsa_SidType *name_types_nocache = NULL;
982         char **domains_nocache = NULL;     /* only needed for rpccli_lsa_lookup_sids */
983         uint32 num_nocache = 0;
984         TALLOC_CTX *tmp_ctx = NULL;
985
986         DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
987                   sid_string_dbg(group_sid)));
988
989         *num_names = 0;
990
991         tmp_ctx = talloc_new(mem_ctx);
992         if (!tmp_ctx) {
993                 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
994                 status = NT_STATUS_NO_MEMORY;
995                 goto done;
996         }
997
998         if ( !winbindd_can_contact_domain( domain ) ) {
999                 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
1000                           domain->name));
1001                 return NT_STATUS_OK;
1002         }
1003
1004         ads = ads_cached_connection(domain);
1005
1006         if (!ads) {
1007                 domain->last_status = NT_STATUS_SERVER_DISABLED;
1008                 goto done;
1009         }
1010
1011         if ((sidbinstr = sid_binstring(group_sid)) == NULL) {
1012                 status = NT_STATUS_NO_MEMORY;
1013                 goto done;
1014         }
1015
1016         /* search for all members of the group */
1017         if (!(ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)",
1018                                          sidbinstr)))
1019         {
1020                 SAFE_FREE(sidbinstr);
1021                 DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
1022                 status = NT_STATUS_NO_MEMORY;
1023                 goto done;
1024         }
1025         SAFE_FREE(sidbinstr);
1026
1027         args.control = ADS_EXTENDED_DN_OID;
1028         args.val = ADS_EXTENDED_DN_HEX_STRING;
1029         args.critical = True;
1030
1031         rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
1032                                ldap_exp, &args, "member", &members, &num_members);
1033
1034         if (!ADS_ERR_OK(rc)) {
1035                 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
1036                 status = NT_STATUS_UNSUCCESSFUL;
1037                 goto done;
1038         }
1039
1040         DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
1041
1042         /* Now that we have a list of sids, we need to get the
1043          * lists of names and name_types belonging to these sids.
1044          * even though conceptually not quite clean,  we use the
1045          * RPC call lsa_lookup_sids for this since it can handle a
1046          * list of sids. ldap calls can just resolve one sid at a time.
1047          *
1048          * At this stage, the sids are still hidden in the exetended dn
1049          * member output format. We actually do a little better than
1050          * stated above: In extracting the sids from the member strings,
1051          * we try to resolve as many sids as possible from the
1052          * cache. Only the rest is passed to the lsa_lookup_sids call. */
1053
1054         if (num_members) {
1055                 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
1056                 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
1057                 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
1058                 (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, DOM_SID, num_members);
1059
1060                 if ((members == NULL) || (*sid_mem == NULL) ||
1061                     (*names == NULL) || (*name_types == NULL) ||
1062                     (sid_mem_nocache == NULL))
1063                 {
1064                         DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1065                         status = NT_STATUS_NO_MEMORY;
1066                         goto done;
1067                 }
1068         }
1069         else {
1070                 (*sid_mem) = NULL;
1071                 (*names) = NULL;
1072                 (*name_types) = NULL;
1073         }
1074
1075         for (i=0; i<num_members; i++) {
1076                 enum lsa_SidType name_type;
1077                 char *name, *domain_name;
1078                 DOM_SID sid;
1079
1080                 rc = ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val,
1081                     &sid);
1082                 if (!ADS_ERR_OK(rc)) {
1083                         if (NT_STATUS_EQUAL(ads_ntstatus(rc),
1084                             NT_STATUS_NOT_FOUND)) {
1085                                 /* Group members can be objects, like Exchange
1086                                  * Public Folders, that don't have a SID.  Skip
1087                                  * them. */
1088                                 continue;
1089                         }
1090                         else {
1091                                 status = ads_ntstatus(rc);
1092                                 goto done;
1093                         }
1094                 }
1095                 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name,
1096                     &name_type)) {
1097                         DEBUG(10,("ads: lookup_groupmem: got sid %s from "
1098                                   "cache\n", sid_string_dbg(&sid)));
1099                         sid_copy(&(*sid_mem)[*num_names], &sid);
1100                         (*names)[*num_names] = fill_domain_username_talloc(
1101                                                         *names,
1102                                                         domain_name,
1103                                                         name,
1104                                                         true);
1105
1106                         (*name_types)[*num_names] = name_type;
1107                         (*num_names)++;
1108                 }
1109                 else {
1110                         DEBUG(10, ("ads: lookup_groupmem: sid %s not found in "
1111                                    "cache\n", sid_string_dbg(&sid)));
1112                         sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
1113                         num_nocache++;
1114                 }
1115         }
1116
1117         DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
1118                   "%d left for lsa_lookupsids\n", *num_names, num_nocache));
1119
1120         /* handle sids not resolved from cache by lsa_lookup_sids */
1121         if (num_nocache > 0) {
1122
1123                 status = cm_connect_lsa(domain, tmp_ctx, &cli, &lsa_policy);
1124
1125                 if (!NT_STATUS_IS_OK(status)) {
1126                         goto done;
1127                 }
1128
1129                 status = rpccli_lsa_lookup_sids(cli, tmp_ctx,
1130                                                 &lsa_policy,
1131                                                 num_nocache,
1132                                                 sid_mem_nocache,
1133                                                 &domains_nocache,
1134                                                 &names_nocache,
1135                                                 &name_types_nocache);
1136
1137                 if (!(NT_STATUS_IS_OK(status) ||
1138                       NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) ||
1139                       NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)))
1140                 {
1141                         DEBUG(1, ("lsa_lookupsids call failed with %s "
1142                                   "- retrying...\n", nt_errstr(status)));
1143
1144                         status = cm_connect_lsa(domain, tmp_ctx, &cli,
1145                                                 &lsa_policy);
1146
1147                         if (!NT_STATUS_IS_OK(status)) {
1148                                 goto done;
1149                         }
1150
1151                         status = rpccli_lsa_lookup_sids(cli, tmp_ctx,
1152                                                         &lsa_policy,
1153                                                         num_nocache,
1154                                                         sid_mem_nocache,
1155                                                         &domains_nocache,
1156                                                         &names_nocache,
1157                                                         &name_types_nocache);
1158                 }
1159
1160                 if (NT_STATUS_IS_OK(status) ||
1161                     NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
1162                 {
1163                         /* Copy the entries over from the "_nocache" arrays
1164                          * to the result arrays, skipping the gaps the
1165                          * lookup_sids call left. */
1166                         for (i=0; i < num_nocache; i++) {
1167                                 if (((names_nocache)[i] != NULL) &&
1168                                     ((name_types_nocache)[i] != SID_NAME_UNKNOWN))
1169                                 {
1170                                         sid_copy(&(*sid_mem)[*num_names],
1171                                                  &sid_mem_nocache[i]);
1172                                         (*names)[*num_names] =
1173                                                 fill_domain_username_talloc(
1174                                                         *names,
1175                                                         domains_nocache[i],
1176                                                         names_nocache[i],
1177                                                         true);
1178                                         (*name_types)[*num_names] = name_types_nocache[i];
1179                                         (*num_names)++;
1180                                 }
1181                         }
1182                 }
1183                 else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1184                         DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
1185                                    "not map any SIDs at all.\n"));
1186                         /* Don't handle this as an error here.
1187                          * There is nothing left to do with respect to the 
1188                          * overall result... */
1189                 }
1190                 else if (!NT_STATUS_IS_OK(status)) {
1191                         DEBUG(10, ("lookup_groupmem: Error looking up %d "
1192                                    "sids via rpc_lsa_lookup_sids: %s\n",
1193                                    (int)num_members, nt_errstr(status)));
1194                         goto done;
1195                 }
1196         }
1197
1198         status = NT_STATUS_OK;
1199         DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
1200                  sid_string_dbg(group_sid)));
1201
1202 done:
1203
1204         TALLOC_FREE(tmp_ctx);
1205
1206         return status;
1207 }
1208
1209 /* find the sequence number for a domain */
1210 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1211 {
1212         ADS_STRUCT *ads = NULL;
1213         ADS_STATUS rc;
1214
1215         DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1216
1217         if ( !winbindd_can_contact_domain( domain ) ) {
1218                 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1219                           domain->name));
1220                 *seq = time(NULL);              
1221                 return NT_STATUS_OK;
1222         }
1223
1224         *seq = DOM_SEQUENCE_NONE;
1225
1226         ads = ads_cached_connection(domain);
1227         
1228         if (!ads) {
1229                 domain->last_status = NT_STATUS_SERVER_DISABLED;
1230                 return NT_STATUS_UNSUCCESSFUL;
1231         }
1232
1233         rc = ads_USN(ads, seq);
1234         
1235         if (!ADS_ERR_OK(rc)) {
1236         
1237                 /* its a dead connection, destroy it */
1238
1239                 if (domain->private_data) {
1240                         ads = (ADS_STRUCT *)domain->private_data;
1241                         ads->is_mine = True;
1242                         ads_destroy(&ads);
1243                         ads_kdestroy("MEMORY:winbind_ccache");
1244                         domain->private_data = NULL;
1245                 }
1246         }
1247         return ads_ntstatus(rc);
1248 }
1249
1250 /* find the lockout policy of a domain - use rpc methods */
1251 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1252                                TALLOC_CTX *mem_ctx,
1253                                struct samr_DomInfo12 *policy)
1254 {
1255         return reconnect_methods.lockout_policy(domain, mem_ctx, policy);
1256 }
1257
1258 /* find the password policy of a domain - use rpc methods */
1259 static NTSTATUS password_policy(struct winbindd_domain *domain,
1260                                 TALLOC_CTX *mem_ctx,
1261                                 struct samr_DomInfo1 *policy)
1262 {
1263         return reconnect_methods.password_policy(domain, mem_ctx, policy);
1264 }
1265
1266 /* get a list of trusted domains */
1267 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1268                                 TALLOC_CTX *mem_ctx,
1269                                 uint32 *num_domains,
1270                                 char ***names,
1271                                 char ***alt_names,
1272                                 DOM_SID **dom_sids)
1273 {
1274         NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
1275         struct netr_DomainTrustList trusts;
1276         int                     i;
1277         uint32                  flags;  
1278         struct rpc_pipe_client *cli;
1279         uint32                 fr_flags = (NETR_TRUST_FLAG_IN_FOREST | NETR_TRUST_FLAG_TREEROOT);
1280         int ret_count;
1281         
1282         DEBUG(3,("ads: trusted_domains\n"));
1283
1284         *num_domains = 0;
1285         *alt_names   = NULL;
1286         *names       = NULL;
1287         *dom_sids    = NULL;
1288
1289         /* If this is our primary domain or a root in our forest,
1290            query for all trusts.  If not, then just look for domain
1291            trusts in the target forest */
1292
1293         if ( domain->primary ||
1294                 ((domain->domain_flags&fr_flags) == fr_flags) ) 
1295         {
1296                 flags = NETR_TRUST_FLAG_OUTBOUND |
1297                         NETR_TRUST_FLAG_INBOUND |
1298                         NETR_TRUST_FLAG_IN_FOREST;
1299         } else {
1300                 flags = NETR_TRUST_FLAG_IN_FOREST;
1301         }       
1302
1303         result = cm_connect_netlogon(domain, &cli);
1304
1305         if (!NT_STATUS_IS_OK(result)) {
1306                 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1307                           "for PIPE_NETLOGON (%s)\n", 
1308                           domain->name, nt_errstr(result)));
1309                 return NT_STATUS_UNSUCCESSFUL;
1310         }
1311
1312         result = rpccli_netr_DsrEnumerateDomainTrusts(cli, mem_ctx,
1313                                                       cli->desthost,
1314                                                       flags,
1315                                                       &trusts,
1316                                                       NULL);
1317         if ( NT_STATUS_IS_OK(result) && trusts.count) {
1318
1319                 /* Allocate memory for trusted domain names and sids */
1320
1321                 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, trusts.count)) ) {
1322                         DEBUG(0, ("trusted_domains: out of memory\n"));
1323                         return NT_STATUS_NO_MEMORY;
1324                 }
1325
1326                 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, trusts.count)) ) {
1327                         DEBUG(0, ("trusted_domains: out of memory\n"));
1328                         return NT_STATUS_NO_MEMORY;
1329                 }
1330
1331                 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, trusts.count)) ) {
1332                         DEBUG(0, ("trusted_domains: out of memory\n"));
1333                         return NT_STATUS_NO_MEMORY;
1334                 }
1335
1336                 /* Copy across names and sids */
1337
1338
1339                 ret_count = 0;          
1340                 for (i = 0; i < trusts.count; i++) {
1341                         struct winbindd_domain d;
1342                         
1343                         ZERO_STRUCT(d);
1344
1345                         /* drop external trusts if this is not our primary 
1346                            domain.  This means that the returned number of 
1347                            domains may be less that the ones actually trusted
1348                            by the DC. */
1349
1350                         if ( (trusts.array[i].trust_attributes == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) &&
1351                              !domain->primary ) 
1352                         {
1353                                 DEBUG(10,("trusted_domains: Skipping external trusted domain "
1354                                           "%s because it is outside of our primary domain\n",
1355                                           trusts.array[i].netbios_name));
1356                                 continue;                               
1357                         }
1358                         
1359                         (*names)[ret_count] = CONST_DISCARD(char *, trusts.array[i].netbios_name);
1360                         (*alt_names)[ret_count] = CONST_DISCARD(char *, trusts.array[i].dns_name);
1361                         if (trusts.array[i].sid) {
1362                                 sid_copy(&(*dom_sids)[ret_count], trusts.array[i].sid);
1363                         } else {
1364                                 sid_copy(&(*dom_sids)[ret_count], &global_sid_NULL);
1365                         }
1366
1367                         /* add to the trusted domain cache */
1368
1369                         fstrcpy( d.name,  trusts.array[i].netbios_name);
1370                         fstrcpy( d.alt_name, trusts.array[i].dns_name);
1371                         if (trusts.array[i].sid) {
1372                                 sid_copy( &d.sid, trusts.array[i].sid);
1373                         } else {
1374                                 sid_copy(&d.sid, &global_sid_NULL);
1375                         }
1376
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));
1382
1383                                 d.domain_flags = trusts.array[i].trust_flags;
1384                                 d.domain_type = trusts.array[i].trust_type;
1385                                 d.domain_trust_attribs = trusts.array[i].trust_attributes;
1386
1387                                 wcache_tdc_add_domain( &d );
1388                                 ret_count++;
1389                         } else if ( (domain->domain_flags&fr_flags) == fr_flags ) {
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;
1396
1397                                 exist = 
1398                                     wcache_tdc_fetch_domain(NULL, trusts.array[i].netbios_name);
1399                                 if (!exist) {
1400                                         DEBUG(10,("trusted_domains(ads):  Searching "
1401                                                   "trusted domain list of %s and storing "
1402                                                   "trust flags for domain %s\n", 
1403                                                   domain->name, d.alt_name));
1404                                         d.domain_flags = trusts.array[i].trust_flags;
1405                                         d.domain_type = trusts.array[i].trust_type;
1406                                         d.domain_trust_attribs = trusts.array[i].trust_attributes;
1407
1408                                         wcache_tdc_add_domain( &d );
1409                                         ret_count++;
1410                                 }
1411                                 TALLOC_FREE(exist);
1412                         } else {
1413                                 /* This gets a little tricky.  If we are
1414                                    following a transitive forest trust, then
1415                                    innerit the flags, type, and attribs from
1416                                    the domain we queried to make sure we don't
1417                                    record the view of the trust from the wrong
1418                                    side.  Always view it from the side of our
1419                                    primary domain.   --jerry */
1420                                 struct winbindd_tdc_domain *parent = NULL;
1421
1422                                 DEBUG(10,("trusted_domains(ads):  Searching "
1423                                           "trusted domain list of %s and inheriting "
1424                                           "trust flags for domain %s\n", 
1425                                           domain->name, d.alt_name));
1426
1427                                 parent = wcache_tdc_fetch_domain(NULL, domain->name);
1428                                 if (parent) {
1429                                         d.domain_flags = parent->trust_flags;
1430                                         d.domain_type  = parent->trust_type;
1431                                         d.domain_trust_attribs = parent->trust_attribs;
1432                                 } else {
1433                                         d.domain_flags = domain->domain_flags;
1434                                         d.domain_type  = domain->domain_type;
1435                                         d.domain_trust_attribs = domain->domain_trust_attribs;
1436                                 }
1437                                 TALLOC_FREE(parent);
1438                                 
1439                                 wcache_tdc_add_domain( &d );
1440                                 ret_count++;
1441                         }
1442                 }
1443
1444                 *num_domains = ret_count;       
1445         }
1446
1447         return result;
1448 }
1449
1450 /* the ADS backend methods are exposed via this structure */
1451 struct winbindd_methods ads_methods = {
1452         True,
1453         query_user_list,
1454         enum_dom_groups,
1455         enum_local_groups,
1456         name_to_sid,
1457         sid_to_name,
1458         rids_to_names,
1459         query_user,
1460         lookup_usergroups,
1461         lookup_useraliases,
1462         lookup_groupmem,
1463         sequence_number,
1464         lockout_policy,
1465         password_policy,
1466         trusted_domains,
1467 };
1468
1469 #endif