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