s3-rpc: Avoid including every pipe's client and server stubs everywhere in samba.
[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) || !res) {
184                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
185                 goto done;
186         }
187
188         count = ads_count_replies(ads, res);
189         if (count == 0) {
190                 DEBUG(1,("query_user_list: No users found\n"));
191                 goto done;
192         }
193
194         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct wbint_userinfo, count);
195         if (!*info) {
196                 status = NT_STATUS_NO_MEMORY;
197                 goto done;
198         }
199
200         i = 0;
201
202         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
203                 const char *name;
204                 const char *gecos = NULL;
205                 const char *homedir = NULL;
206                 const 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                     ds_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                             const char *domain_name,
408                             const char *name,
409                             uint32_t flags,
410                             DOM_SID *sid,
411                             enum lsa_SidType *type)
412 {
413         return reconnect_methods.name_to_sid(domain, mem_ctx,
414                                              domain_name, name, flags,
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                            struct wbint_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         gid_t gid;
467
468         DEBUG(3,("ads: query_user\n"));
469
470         info->homedir = NULL;
471         info->shell = NULL;
472         info->primary_gid = (gid_t)-1;
473
474         /* try netsamlogon cache first */
475
476         if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL ) 
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                               &gid );
490                 info->primary_gid = gid;
491
492                 TALLOC_FREE(user);
493
494                 return NT_STATUS_OK;
495         }
496
497         if ( !winbindd_can_contact_domain(domain)) {
498                 DEBUG(8,("query_user: No incoming trust from domain %s\n",
499                          domain->name));
500
501                 /* We still need to generate some basic information
502                    about the user even if we cannot contact the 
503                    domain.  Most of this stuff we can deduce. */
504
505                 sid_copy( &info->user_sid, sid );
506
507                 /* Assume "Domain Users" for the primary group */
508
509                 sid_compose(&info->group_sid, &domain->sid, DOMAIN_GROUP_RID_USERS );
510
511                 /* Try to fill in what the nss_info backend can do */
512
513                 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, 
514                               &info->homedir, &info->shell, &info->full_name, 
515                               &gid);
516                 info->primary_gid = gid;
517
518                 status = NT_STATUS_OK;
519                 goto done;
520         }
521
522         /* no cache...do the query */
523
524         if ( (ads = ads_cached_connection(domain)) == NULL ) {
525                 domain->last_status = NT_STATUS_SERVER_DISABLED;
526                 goto done;
527         }
528
529         sidstr = sid_binstring(talloc_tos(), sid);
530         if (asprintf(&ldap_exp, "(objectSid=%s)", sidstr) == -1) {
531                 status = NT_STATUS_NO_MEMORY;
532                 goto done;
533         }
534         rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
535         free(ldap_exp);
536         TALLOC_FREE(sidstr);
537         if (!ADS_ERR_OK(rc) || !msg) {
538                 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
539                          sid_string_dbg(sid), ads_errstr(rc)));
540                 goto done;
541         }
542
543         count = ads_count_replies(ads, msg);
544         if (count != 1) {
545                 DEBUG(1,("query_user(sid=%s): Not found\n",
546                          sid_string_dbg(sid)));
547                 goto done;
548         }
549
550         info->acct_name = ads_pull_username(ads, mem_ctx, msg);
551
552         nss_get_info_cached( domain, sid, mem_ctx, ads, msg, 
553                       &info->homedir, &info->shell, &info->full_name, 
554                       &gid);
555         info->primary_gid = gid;
556
557         if (info->full_name == NULL) {
558                 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
559         }
560
561         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
562                 DEBUG(1,("No primary group for %s !?\n",
563                          sid_string_dbg(sid)));
564                 goto done;
565         }
566
567         sid_copy(&info->user_sid, sid);
568         sid_compose(&info->group_sid, &domain->sid, group_rid);
569
570         status = NT_STATUS_OK;
571
572         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
573 done:
574         if (msg) 
575                 ads_msgfree(ads, msg);
576
577         return status;
578 }
579
580 /* Lookup groups a user is a member of - alternate method, for when
581    tokenGroups are not available. */
582 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
583                                          TALLOC_CTX *mem_ctx,
584                                          const char *user_dn, 
585                                          DOM_SID *primary_group,
586                                          size_t *p_num_groups, DOM_SID **user_sids)
587 {
588         ADS_STATUS rc;
589         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
590         int count;
591         LDAPMessage *res = NULL;
592         LDAPMessage *msg = NULL;
593         char *ldap_exp;
594         ADS_STRUCT *ads;
595         const char *group_attrs[] = {"objectSid", NULL};
596         char *escaped_dn;
597         size_t num_groups = 0;
598
599         DEBUG(3,("ads: lookup_usergroups_member\n"));
600
601         if ( !winbindd_can_contact_domain( domain ) ) {
602                 DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
603                           domain->name));               
604                 return NT_STATUS_OK;
605         }
606
607         ads = ads_cached_connection(domain);
608
609         if (!ads) {
610                 domain->last_status = NT_STATUS_SERVER_DISABLED;
611                 goto done;
612         }
613
614         if (!(escaped_dn = escape_ldap_string(talloc_tos(), user_dn))) {
615                 status = NT_STATUS_NO_MEMORY;
616                 goto done;
617         }
618
619         ldap_exp = talloc_asprintf(mem_ctx,
620                 "(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))",
621                 escaped_dn,
622                 ADS_LDAP_MATCHING_RULE_BIT_AND,
623                 GROUP_TYPE_SECURITY_ENABLED);
624         if (!ldap_exp) {
625                 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
626                 TALLOC_FREE(escaped_dn);
627                 status = NT_STATUS_NO_MEMORY;
628                 goto done;
629         }
630
631         TALLOC_FREE(escaped_dn);
632
633         rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
634
635         if (!ADS_ERR_OK(rc) || !res) {
636                 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
637                 return ads_ntstatus(rc);
638         }
639
640         count = ads_count_replies(ads, res);
641
642         *user_sids = NULL;
643         num_groups = 0;
644
645         /* always add the primary group to the sid array */
646         status = add_sid_to_array(mem_ctx, primary_group, user_sids,
647                                   &num_groups);
648         if (!NT_STATUS_IS_OK(status)) {
649                 goto done;
650         }
651
652         if (count > 0) {
653                 for (msg = ads_first_entry(ads, res); msg;
654                      msg = ads_next_entry(ads, msg)) {
655                         DOM_SID group_sid;
656
657                         if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
658                                 DEBUG(1,("No sid for this group ?!?\n"));
659                                 continue;
660                         }
661
662                         /* ignore Builtin groups from ADS - Guenther */
663                         if (sid_check_is_in_builtin(&group_sid)) {
664                                 continue;
665                         }
666
667                         status = add_sid_to_array(mem_ctx, &group_sid,
668                                                   user_sids, &num_groups);
669                         if (!NT_STATUS_IS_OK(status)) {
670                                 goto done;
671                         }
672                 }
673
674         }
675
676         *p_num_groups = num_groups;
677         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
678
679         DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
680 done:
681         if (res) 
682                 ads_msgfree(ads, res);
683
684         return status;
685 }
686
687 /* Lookup groups a user is a member of - alternate method, for when
688    tokenGroups are not available. */
689 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
690                                            TALLOC_CTX *mem_ctx,
691                                            const char *user_dn,
692                                            DOM_SID *primary_group,
693                                            size_t *p_num_groups,
694                                            DOM_SID **user_sids)
695 {
696         ADS_STATUS rc;
697         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
698         ADS_STRUCT *ads;
699         const char *attrs[] = {"memberOf", NULL};
700         size_t num_groups = 0;
701         DOM_SID *group_sids = NULL;
702         int i;
703         char **strings = NULL;
704         size_t num_strings = 0, num_sids = 0;
705
706
707         DEBUG(3,("ads: lookup_usergroups_memberof\n"));
708
709         if ( !winbindd_can_contact_domain( domain ) ) {
710                 DEBUG(10,("lookup_usergroups_memberof: No incoming trust for "
711                           "domain %s\n", domain->name));
712                 return NT_STATUS_OK;
713         }
714
715         ads = ads_cached_connection(domain);
716
717         if (!ads) {
718                 domain->last_status = NT_STATUS_SERVER_DISABLED;
719                 return NT_STATUS_UNSUCCESSFUL;
720         }
721
722         rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
723                                                  ADS_EXTENDED_DN_HEX_STRING,
724                                                  &strings, &num_strings);
725
726         if (!ADS_ERR_OK(rc)) {
727                 DEBUG(1,("lookup_usergroups_memberof ads_search "
728                         "member=%s: %s\n", user_dn, ads_errstr(rc)));
729                 return ads_ntstatus(rc);
730         }
731
732         *user_sids = NULL;
733         num_groups = 0;
734
735         /* always add the primary group to the sid array */
736         status = add_sid_to_array(mem_ctx, primary_group, user_sids,
737                                   &num_groups);
738         if (!NT_STATUS_IS_OK(status)) {
739                 goto done;
740         }
741
742         group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1);
743         if (!group_sids) {
744                 status = NT_STATUS_NO_MEMORY;
745                 goto done;
746         }
747
748         for (i=0; i<num_strings; i++) {
749                 rc = ads_get_sid_from_extended_dn(mem_ctx, strings[i],
750                                                   ADS_EXTENDED_DN_HEX_STRING,
751                                                   &(group_sids)[i]);
752                 if (!ADS_ERR_OK(rc)) {
753                         /* ignore members without SIDs */
754                         if (NT_STATUS_EQUAL(ads_ntstatus(rc),
755                             NT_STATUS_NOT_FOUND)) {
756                                 continue;
757                         }
758                         else {
759                                 status = ads_ntstatus(rc);
760                                 goto done;
761                         }
762                 }
763                 num_sids++;
764         }
765
766         if (i == 0) {
767                 DEBUG(1,("No memberOf for this user?!?\n"));
768                 status = NT_STATUS_NO_MEMORY;
769                 goto done;
770         }
771
772         for (i=0; i<num_sids; i++) {
773
774                 /* ignore Builtin groups from ADS - Guenther */
775                 if (sid_check_is_in_builtin(&group_sids[i])) {
776                         continue;
777                 }
778
779                 status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
780                                           &num_groups);
781                 if (!NT_STATUS_IS_OK(status)) {
782                         goto done;
783                 }
784
785         }
786
787         *p_num_groups = num_groups;
788         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
789
790         DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n",
791                 user_dn));
792
793 done:
794         TALLOC_FREE(strings);
795         TALLOC_FREE(group_sids);
796
797         return status;
798 }
799
800
801 /* Lookup groups a user is a member of. */
802 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
803                                   TALLOC_CTX *mem_ctx,
804                                   const DOM_SID *sid, 
805                                   uint32 *p_num_groups, DOM_SID **user_sids)
806 {
807         ADS_STRUCT *ads = NULL;
808         const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
809         ADS_STATUS rc;
810         int count;
811         LDAPMessage *msg = NULL;
812         char *user_dn = NULL;
813         DOM_SID *sids;
814         int i;
815         DOM_SID primary_group;
816         uint32 primary_group_rid;
817         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
818         size_t num_groups = 0;
819
820         DEBUG(3,("ads: lookup_usergroups\n"));
821         *p_num_groups = 0;
822
823         status = lookup_usergroups_cached(domain, mem_ctx, sid, 
824                                           p_num_groups, user_sids);
825         if (NT_STATUS_IS_OK(status)) {
826                 return NT_STATUS_OK;
827         }
828
829         if ( !winbindd_can_contact_domain( domain ) ) {
830                 DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
831                           domain->name));
832
833                 /* Tell the cache manager not to remember this one */
834
835                 return NT_STATUS_SYNCHRONIZATION_REQUIRED;
836         }
837
838         ads = ads_cached_connection(domain);
839
840         if (!ads) {
841                 domain->last_status = NT_STATUS_SERVER_DISABLED;
842                 status = NT_STATUS_SERVER_DISABLED;
843                 goto done;
844         }
845
846         rc = ads_search_retry_sid(ads, &msg, sid, attrs);
847
848         if (!ADS_ERR_OK(rc)) {
849                 status = ads_ntstatus(rc);
850                 DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
851                           "%s\n", sid_string_dbg(sid), ads_errstr(rc)));
852                 goto done;
853         }
854
855         count = ads_count_replies(ads, msg);
856         if (count != 1) {
857                 status = NT_STATUS_UNSUCCESSFUL;
858                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
859                          "invalid number of results (count=%d)\n", 
860                          sid_string_dbg(sid), count));
861                 goto done;
862         }
863
864         if (!msg) {
865                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", 
866                          sid_string_dbg(sid)));
867                 status = NT_STATUS_UNSUCCESSFUL;
868                 goto done;
869         }
870
871         user_dn = ads_get_dn(ads, mem_ctx, msg);
872         if (user_dn == NULL) {
873                 status = NT_STATUS_NO_MEMORY;
874                 goto done;
875         }
876
877         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
878                 DEBUG(1,("%s: No primary group for sid=%s !?\n", 
879                          domain->name, sid_string_dbg(sid)));
880                 goto done;
881         }
882
883         sid_copy(&primary_group, &domain->sid);
884         sid_append_rid(&primary_group, primary_group_rid);
885
886         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
887
888         /* there must always be at least one group in the token, 
889            unless we are talking to a buggy Win2k server */
890
891         /* actually this only happens when the machine account has no read
892          * permissions on the tokenGroup attribute - gd */
893
894         if (count == 0) {
895
896                 /* no tokenGroups */
897
898                 /* lookup what groups this user is a member of by DN search on
899                  * "memberOf" */
900
901                 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
902                                                     &primary_group,
903                                                     &num_groups, user_sids);
904                 *p_num_groups = (uint32)num_groups;
905                 if (NT_STATUS_IS_OK(status)) {
906                         goto done;
907                 }
908
909                 /* lookup what groups this user is a member of by DN search on
910                  * "member" */
911
912                 status = lookup_usergroups_member(domain, mem_ctx, user_dn, 
913                                                   &primary_group,
914                                                   &num_groups, user_sids);
915                 *p_num_groups = (uint32)num_groups;
916                 goto done;
917         }
918
919         *user_sids = NULL;
920         num_groups = 0;
921
922         status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
923                                   &num_groups);
924         if (!NT_STATUS_IS_OK(status)) {
925                 goto done;
926         }
927
928         for (i=0;i<count;i++) {
929
930                 /* ignore Builtin groups from ADS - Guenther */
931                 if (sid_check_is_in_builtin(&sids[i])) {
932                         continue;
933                 }
934
935                 status = add_sid_to_array_unique(mem_ctx, &sids[i],
936                                                  user_sids, &num_groups);
937                 if (!NT_STATUS_IS_OK(status)) {
938                         goto done;
939                 }
940         }
941
942         *p_num_groups = (uint32)num_groups;
943         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
944
945         DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
946                  sid_string_dbg(sid)));
947 done:
948         TALLOC_FREE(user_dn);
949         ads_msgfree(ads, msg);
950         return status;
951 }
952
953 /* Lookup aliases a user is member of - use rpc methods */
954 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
955                                    TALLOC_CTX *mem_ctx,
956                                    uint32 num_sids, const DOM_SID *sids,
957                                    uint32 *num_aliases, uint32 **alias_rids)
958 {
959         return reconnect_methods.lookup_useraliases(domain, mem_ctx,
960                                                     num_sids, sids,
961                                                     num_aliases,
962                                                     alias_rids);
963 }
964
965 /*
966   find the members of a group, given a group rid and domain
967  */
968 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
969                                 TALLOC_CTX *mem_ctx,
970                                 const DOM_SID *group_sid,
971                                 enum lsa_SidType type,
972                                 uint32 *num_names,
973                                 DOM_SID **sid_mem, char ***names,
974                                 uint32 **name_types)
975 {
976         ADS_STATUS rc;
977         ADS_STRUCT *ads = NULL;
978         char *ldap_exp;
979         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
980         char *sidbinstr;
981         char **members = NULL;
982         int i;
983         size_t num_members = 0;
984         ads_control args;
985         DOM_SID *sid_mem_nocache = NULL;
986         char **names_nocache = NULL;
987         enum lsa_SidType *name_types_nocache = NULL;
988         char **domains_nocache = NULL;     /* only needed for rpccli_lsa_lookup_sids */
989         uint32 num_nocache = 0;
990         TALLOC_CTX *tmp_ctx = NULL;
991
992         DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
993                   sid_string_dbg(group_sid)));
994
995         *num_names = 0;
996
997         tmp_ctx = talloc_new(mem_ctx);
998         if (!tmp_ctx) {
999                 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1000                 status = NT_STATUS_NO_MEMORY;
1001                 goto done;
1002         }
1003
1004         if ( !winbindd_can_contact_domain( domain ) ) {
1005                 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
1006                           domain->name));
1007                 return NT_STATUS_OK;
1008         }
1009
1010         ads = ads_cached_connection(domain);
1011
1012         if (!ads) {
1013                 domain->last_status = NT_STATUS_SERVER_DISABLED;
1014                 goto done;
1015         }
1016
1017         if ((sidbinstr = sid_binstring(talloc_tos(), group_sid)) == NULL) {
1018                 status = NT_STATUS_NO_MEMORY;
1019                 goto done;
1020         }
1021
1022         /* search for all members of the group */
1023         ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", sidbinstr);
1024         TALLOC_FREE(sidbinstr);
1025         if (ldap_exp == NULL) {
1026                 DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
1027                 status = NT_STATUS_NO_MEMORY;
1028                 goto done;
1029         }
1030
1031         args.control = ADS_EXTENDED_DN_OID;
1032         args.val = ADS_EXTENDED_DN_HEX_STRING;
1033         args.critical = True;
1034
1035         rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
1036                                ldap_exp, &args, "member", &members, &num_members);
1037
1038         if (!ADS_ERR_OK(rc)) {
1039                 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
1040                 status = NT_STATUS_UNSUCCESSFUL;
1041                 goto done;
1042         }
1043
1044         DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
1045
1046         /* Now that we have a list of sids, we need to get the
1047          * lists of names and name_types belonging to these sids.
1048          * even though conceptually not quite clean,  we use the
1049          * RPC call lsa_lookup_sids for this since it can handle a
1050          * list of sids. ldap calls can just resolve one sid at a time.
1051          *
1052          * At this stage, the sids are still hidden in the exetended dn
1053          * member output format. We actually do a little better than
1054          * stated above: In extracting the sids from the member strings,
1055          * we try to resolve as many sids as possible from the
1056          * cache. Only the rest is passed to the lsa_lookup_sids call. */
1057
1058         if (num_members) {
1059                 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
1060                 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
1061                 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
1062                 (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, DOM_SID, num_members);
1063
1064                 if ((members == NULL) || (*sid_mem == NULL) ||
1065                     (*names == NULL) || (*name_types == NULL) ||
1066                     (sid_mem_nocache == NULL))
1067                 {
1068                         DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1069                         status = NT_STATUS_NO_MEMORY;
1070                         goto done;
1071                 }
1072         }
1073         else {
1074                 (*sid_mem) = NULL;
1075                 (*names) = NULL;
1076                 (*name_types) = NULL;
1077         }
1078
1079         for (i=0; i<num_members; i++) {
1080                 enum lsa_SidType name_type;
1081                 char *name, *domain_name;
1082                 DOM_SID sid;
1083
1084                 rc = ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val,
1085                     &sid);
1086                 if (!ADS_ERR_OK(rc)) {
1087                         if (NT_STATUS_EQUAL(ads_ntstatus(rc),
1088                             NT_STATUS_NOT_FOUND)) {
1089                                 /* Group members can be objects, like Exchange
1090                                  * Public Folders, that don't have a SID.  Skip
1091                                  * them. */
1092                                 continue;
1093                         }
1094                         else {
1095                                 status = ads_ntstatus(rc);
1096                                 goto done;
1097                         }
1098                 }
1099                 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name,
1100                     &name_type)) {
1101                         DEBUG(10,("ads: lookup_groupmem: got sid %s from "
1102                                   "cache\n", sid_string_dbg(&sid)));
1103                         sid_copy(&(*sid_mem)[*num_names], &sid);
1104                         (*names)[*num_names] = fill_domain_username_talloc(
1105                                                         *names,
1106                                                         domain_name,
1107                                                         name,
1108                                                         true);
1109
1110                         (*name_types)[*num_names] = name_type;
1111                         (*num_names)++;
1112                 }
1113                 else {
1114                         DEBUG(10, ("ads: lookup_groupmem: sid %s not found in "
1115                                    "cache\n", sid_string_dbg(&sid)));
1116                         sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
1117                         num_nocache++;
1118                 }
1119         }
1120
1121         DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
1122                   "%d left for lsa_lookupsids\n", *num_names, num_nocache));
1123
1124         /* handle sids not resolved from cache by lsa_lookup_sids */
1125         if (num_nocache > 0) {
1126
1127                 status = winbindd_lookup_sids(tmp_ctx,
1128                                               domain,
1129                                               num_nocache,
1130                                               sid_mem_nocache,
1131                                               &domains_nocache,
1132                                               &names_nocache,
1133                                               &name_types_nocache);
1134
1135                 if (!(NT_STATUS_IS_OK(status) ||
1136                       NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) ||
1137                       NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)))
1138                 {
1139                         DEBUG(1, ("lsa_lookupsids call failed with %s "
1140                                   "- retrying...\n", nt_errstr(status)));
1141
1142                         status = winbindd_lookup_sids(tmp_ctx,
1143                                                       domain,
1144                                                       num_nocache,
1145                                                       sid_mem_nocache,
1146                                                       &domains_nocache,
1147                                                       &names_nocache,
1148                                                       &name_types_nocache);
1149                 }
1150
1151                 if (NT_STATUS_IS_OK(status) ||
1152                     NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
1153                 {
1154                         /* Copy the entries over from the "_nocache" arrays
1155                          * to the result arrays, skipping the gaps the
1156                          * lookup_sids call left. */
1157                         for (i=0; i < num_nocache; i++) {
1158                                 if (((names_nocache)[i] != NULL) &&
1159                                     ((name_types_nocache)[i] != SID_NAME_UNKNOWN))
1160                                 {
1161                                         sid_copy(&(*sid_mem)[*num_names],
1162                                                  &sid_mem_nocache[i]);
1163                                         (*names)[*num_names] =
1164                                                 fill_domain_username_talloc(
1165                                                         *names,
1166                                                         domains_nocache[i],
1167                                                         names_nocache[i],
1168                                                         true);
1169                                         (*name_types)[*num_names] = name_types_nocache[i];
1170                                         (*num_names)++;
1171                                 }
1172                         }
1173                 }
1174                 else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1175                         DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
1176                                    "not map any SIDs at all.\n"));
1177                         /* Don't handle this as an error here.
1178                          * There is nothing left to do with respect to the 
1179                          * overall result... */
1180                 }
1181                 else if (!NT_STATUS_IS_OK(status)) {
1182                         DEBUG(10, ("lookup_groupmem: Error looking up %d "
1183                                    "sids via rpc_lsa_lookup_sids: %s\n",
1184                                    (int)num_members, nt_errstr(status)));
1185                         goto done;
1186                 }
1187         }
1188
1189         status = NT_STATUS_OK;
1190         DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
1191                  sid_string_dbg(group_sid)));
1192
1193 done:
1194
1195         TALLOC_FREE(tmp_ctx);
1196
1197         return status;
1198 }
1199
1200 /* find the sequence number for a domain */
1201 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1202 {
1203         ADS_STRUCT *ads = NULL;
1204         ADS_STATUS rc;
1205
1206         DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1207
1208         if ( !winbindd_can_contact_domain( domain ) ) {
1209                 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1210                           domain->name));
1211                 *seq = time(NULL);              
1212                 return NT_STATUS_OK;
1213         }
1214
1215         *seq = DOM_SEQUENCE_NONE;
1216
1217         ads = ads_cached_connection(domain);
1218
1219         if (!ads) {
1220                 domain->last_status = NT_STATUS_SERVER_DISABLED;
1221                 return NT_STATUS_UNSUCCESSFUL;
1222         }
1223
1224         rc = ads_USN(ads, seq);
1225
1226         if (!ADS_ERR_OK(rc)) {
1227
1228                 /* its a dead connection, destroy it */
1229
1230                 if (domain->private_data) {
1231                         ads = (ADS_STRUCT *)domain->private_data;
1232                         ads->is_mine = True;
1233                         ads_destroy(&ads);
1234                         ads_kdestroy("MEMORY:winbind_ccache");
1235                         domain->private_data = NULL;
1236                 }
1237         }
1238         return ads_ntstatus(rc);
1239 }
1240
1241 /* find the lockout policy of a domain - use rpc methods */
1242 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1243                                TALLOC_CTX *mem_ctx,
1244                                struct samr_DomInfo12 *policy)
1245 {
1246         return reconnect_methods.lockout_policy(domain, mem_ctx, policy);
1247 }
1248
1249 /* find the password policy of a domain - use rpc methods */
1250 static NTSTATUS password_policy(struct winbindd_domain *domain,
1251                                 TALLOC_CTX *mem_ctx,
1252                                 struct samr_DomInfo1 *policy)
1253 {
1254         return reconnect_methods.password_policy(domain, mem_ctx, policy);
1255 }
1256
1257 /* get a list of trusted domains */
1258 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1259                                 TALLOC_CTX *mem_ctx,
1260                                 uint32 *num_domains,
1261                                 char ***names,
1262                                 char ***alt_names,
1263                                 DOM_SID **dom_sids)
1264 {
1265         NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
1266         struct netr_DomainTrustList trusts;
1267         int                     i;
1268         uint32                  flags;  
1269         struct rpc_pipe_client *cli;
1270         uint32                 fr_flags = (NETR_TRUST_FLAG_IN_FOREST | NETR_TRUST_FLAG_TREEROOT);
1271         int ret_count;
1272
1273         DEBUG(3,("ads: trusted_domains\n"));
1274
1275         *num_domains = 0;
1276         *alt_names   = NULL;
1277         *names       = NULL;
1278         *dom_sids    = NULL;
1279
1280         /* If this is our primary domain or a root in our forest,
1281            query for all trusts.  If not, then just look for domain
1282            trusts in the target forest */
1283
1284         if ( domain->primary ||
1285                 ((domain->domain_flags&fr_flags) == fr_flags) ) 
1286         {
1287                 flags = NETR_TRUST_FLAG_OUTBOUND |
1288                         NETR_TRUST_FLAG_INBOUND |
1289                         NETR_TRUST_FLAG_IN_FOREST;
1290         } else {
1291                 flags = NETR_TRUST_FLAG_IN_FOREST;
1292         }       
1293
1294         result = cm_connect_netlogon(domain, &cli);
1295
1296         if (!NT_STATUS_IS_OK(result)) {
1297                 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1298                           "for PIPE_NETLOGON (%s)\n", 
1299                           domain->name, nt_errstr(result)));
1300                 return NT_STATUS_UNSUCCESSFUL;
1301         }
1302
1303         result = rpccli_netr_DsrEnumerateDomainTrusts(cli, mem_ctx,
1304                                                       cli->desthost,
1305                                                       flags,
1306                                                       &trusts,
1307                                                       NULL);
1308         if ( NT_STATUS_IS_OK(result) && trusts.count) {
1309
1310                 /* Allocate memory for trusted domain names and sids */
1311
1312                 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, trusts.count)) ) {
1313                         DEBUG(0, ("trusted_domains: out of memory\n"));
1314                         return NT_STATUS_NO_MEMORY;
1315                 }
1316
1317                 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, trusts.count)) ) {
1318                         DEBUG(0, ("trusted_domains: out of memory\n"));
1319                         return NT_STATUS_NO_MEMORY;
1320                 }
1321
1322                 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, trusts.count)) ) {
1323                         DEBUG(0, ("trusted_domains: out of memory\n"));
1324                         return NT_STATUS_NO_MEMORY;
1325                 }
1326
1327                 /* Copy across names and sids */
1328
1329
1330                 ret_count = 0;          
1331                 for (i = 0; i < trusts.count; i++) {
1332                         struct winbindd_domain d;
1333
1334                         ZERO_STRUCT(d);
1335
1336                         /* drop external trusts if this is not our primary 
1337                            domain.  This means that the returned number of 
1338                            domains may be less that the ones actually trusted
1339                            by the DC. */
1340
1341                         if ( (trusts.array[i].trust_attributes == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) &&
1342                              !domain->primary ) 
1343                         {
1344                                 DEBUG(10,("trusted_domains: Skipping external trusted domain "
1345                                           "%s because it is outside of our primary domain\n",
1346                                           trusts.array[i].netbios_name));
1347                                 continue;
1348                         }
1349
1350                         /* We must check that the SID of each trusted domain
1351                          * was returned to work around a bug in Windows:
1352                          * http://support.microsoft.com/kb/922832 */
1353
1354                         (*names)[ret_count] = CONST_DISCARD(char *, trusts.array[i].netbios_name);
1355                         (*alt_names)[ret_count] = CONST_DISCARD(char *, trusts.array[i].dns_name);
1356                         if (trusts.array[i].sid) {
1357                                 sid_copy(&(*dom_sids)[ret_count], trusts.array[i].sid);
1358                         } else {
1359                                 sid_copy(&(*dom_sids)[ret_count], &global_sid_NULL);
1360                         }
1361
1362                         /* add to the trusted domain cache */
1363
1364                         fstrcpy( d.name,  trusts.array[i].netbios_name);
1365                         fstrcpy( d.alt_name, trusts.array[i].dns_name);
1366                         if (trusts.array[i].sid) {
1367                                 sid_copy( &d.sid, trusts.array[i].sid);
1368                         } else {
1369                                 sid_copy(&d.sid, &global_sid_NULL);
1370                         }
1371
1372                         if ( domain->primary ) {
1373                                 DEBUG(10,("trusted_domains(ads):  Searching "
1374                                           "trusted domain list of %s and storing "
1375                                           "trust flags for domain %s\n", 
1376                                           domain->name, d.alt_name));
1377
1378                                 d.domain_flags = trusts.array[i].trust_flags;
1379                                 d.domain_type = trusts.array[i].trust_type;
1380                                 d.domain_trust_attribs = trusts.array[i].trust_attributes;
1381
1382                                 wcache_tdc_add_domain( &d );
1383                                 ret_count++;
1384                         } else if ( (domain->domain_flags&fr_flags) == fr_flags ) {
1385                                 /* Check if we already have this record. If
1386                                  * we are following our forest root that is not
1387                                  * our primary domain, we want to keep trust
1388                                  * flags from the perspective of our primary
1389                                  * domain not our forest root. */
1390                                 struct winbindd_tdc_domain *exist = NULL;
1391
1392                                 exist = 
1393                                     wcache_tdc_fetch_domain(NULL, trusts.array[i].netbios_name);
1394                                 if (!exist) {
1395                                         DEBUG(10,("trusted_domains(ads):  Searching "
1396                                                   "trusted domain list of %s and storing "
1397                                                   "trust flags for domain %s\n", 
1398                                                   domain->name, d.alt_name));
1399                                         d.domain_flags = trusts.array[i].trust_flags;
1400                                         d.domain_type = trusts.array[i].trust_type;
1401                                         d.domain_trust_attribs = trusts.array[i].trust_attributes;
1402
1403                                         wcache_tdc_add_domain( &d );
1404                                         ret_count++;
1405                                 }
1406                                 TALLOC_FREE(exist);
1407                         } else {
1408                                 /* This gets a little tricky.  If we are
1409                                    following a transitive forest trust, then
1410                                    innerit the flags, type, and attribs from
1411                                    the domain we queried to make sure we don't
1412                                    record the view of the trust from the wrong
1413                                    side.  Always view it from the side of our
1414                                    primary domain.   --jerry */
1415                                 struct winbindd_tdc_domain *parent = NULL;
1416
1417                                 DEBUG(10,("trusted_domains(ads):  Searching "
1418                                           "trusted domain list of %s and inheriting "
1419                                           "trust flags for domain %s\n", 
1420                                           domain->name, d.alt_name));
1421
1422                                 parent = wcache_tdc_fetch_domain(NULL, domain->name);
1423                                 if (parent) {
1424                                         d.domain_flags = parent->trust_flags;
1425                                         d.domain_type  = parent->trust_type;
1426                                         d.domain_trust_attribs = parent->trust_attribs;
1427                                 } else {
1428                                         d.domain_flags = domain->domain_flags;
1429                                         d.domain_type  = domain->domain_type;
1430                                         d.domain_trust_attribs = domain->domain_trust_attribs;
1431                                 }
1432                                 TALLOC_FREE(parent);
1433
1434                                 wcache_tdc_add_domain( &d );
1435                                 ret_count++;
1436                         }
1437                 }
1438
1439                 *num_domains = ret_count;       
1440         }
1441
1442         return result;
1443 }
1444
1445 /* the ADS backend methods are exposed via this structure */
1446 struct winbindd_methods ads_methods = {
1447         True,
1448         query_user_list,
1449         enum_dom_groups,
1450         enum_local_groups,
1451         name_to_sid,
1452         sid_to_name,
1453         rids_to_names,
1454         query_user,
1455         lookup_usergroups,
1456         lookup_useraliases,
1457         lookup_groupmem,
1458         sequence_number,
1459         lockout_policy,
1460         password_policy,
1461         trusted_domains,
1462 };
1463
1464 #endif