r6627: Fix debug message
[vlendec/samba-autobuild/.git] / source3 / nsswitch / winbindd_group.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon for ntdom nss module
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jeremy Allison 2001.
8    Copyright (C) Gerald (Jerry) Carter 2003.
9    Copyright (C) Volker Lendecke 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
29 extern BOOL opt_nocache;
30
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
33
34 /*********************************************************************
35 *********************************************************************/
36
37 static int gr_mem_buffer( char **buffer, char **members, int num_members )
38 {
39         int i;
40         int len = 0;
41         int idx = 0;
42
43         if ( num_members == 0 ) {
44                 *buffer = NULL;
45                 return 0;
46         }
47         
48         for ( i=0; i<num_members; i++ )
49                 len += strlen(members[i])+1;
50
51         *buffer = SMB_XMALLOC_ARRAY(char, len);
52         for ( i=0; i<num_members; i++ ) {
53                 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
54                 idx += strlen(members[i])+1;
55         }
56         /* terminate with NULL */
57         (*buffer)[len-1] = '\0';
58         
59         return len;     
60 }
61
62 /***************************************************************
63  Empty static struct for negative caching.
64 ****************************************************************/
65
66 /* Fill a grent structure from various other information */
67
68 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name, 
69                        const char *gr_name, gid_t unix_gid)
70 {
71         fstring full_group_name;
72         /* Fill in uid/gid */
73         fill_domain_username(full_group_name, dom_name, gr_name);
74
75         gr->gr_gid = unix_gid;
76     
77         /* Group name and password */
78     
79         safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
80         safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
81
82         return True;
83 }
84
85 /* Fill in the group membership field of a NT group given by group_sid */
86
87 static BOOL fill_grent_mem(struct winbindd_domain *domain,
88                            DOM_SID *group_sid, 
89                            enum SID_NAME_USE group_name_type, 
90                            int *num_gr_mem, char **gr_mem, int *gr_mem_len)
91 {
92         DOM_SID **sid_mem = NULL;
93         uint32 num_names = 0;
94         uint32 *name_types = NULL;
95         unsigned int buf_len, buf_ndx, i;
96         char **names = NULL, *buf;
97         BOOL result = False;
98         TALLOC_CTX *mem_ctx;
99         NTSTATUS status;
100         fstring sid_string;
101
102         if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
103                 return False;
104
105         /* Initialise group membership information */
106         
107         DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
108
109         *num_gr_mem = 0;
110
111         /* HACK ALERT!! This whole routine does not cope with group members
112          * from more than one domain, ie aliases. Thus we have to work it out
113          * ourselves in a special routine. */
114
115         if (domain->internal)
116                 return fill_passdb_alias_grmem(domain, group_sid,
117                                                num_gr_mem,
118                                                gr_mem, gr_mem_len);
119         
120         if ( !((group_name_type==SID_NAME_DOM_GRP) ||
121                 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
122         {
123                 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", 
124                           sid_to_string(sid_string, group_sid), domain->name, 
125                           group_name_type));
126                 goto done;
127         }
128
129         /* Lookup group members */
130         status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names, 
131                                                   &sid_mem, &names, &name_types);
132         if (!NT_STATUS_IS_OK(status)) {
133                 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n", 
134                           sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
135
136                 goto done;
137         }
138
139         DEBUG(10, ("looked up %d names\n", num_names));
140
141         if (DEBUGLEVEL >= 10) {
142                 for (i = 0; i < num_names; i++)
143                         DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
144                                    name_types[i]));
145         }
146
147         /* Add members to list */
148
149         buf = NULL;
150         buf_len = buf_ndx = 0;
151
152  again:
153
154         for (i = 0; i < num_names; i++) {
155                 char *the_name;
156                 fstring name;
157                 int len;
158                         
159                 the_name = names[i];
160
161                 DEBUG(10, ("processing name %s\n", the_name));
162
163                 /* FIXME: need to cope with groups within groups.  These
164                    occur in Universal groups on a Windows 2000 native mode
165                    server. */
166
167                 /* make sure to allow machine accounts */
168
169                 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
170                         DEBUG(3, ("name %s isn't a domain user\n", the_name));
171                         continue;
172                 }
173
174                 /* Append domain name */
175
176                 fill_domain_username(name, domain->name, the_name);
177
178                 len = strlen(name);
179                 
180                 /* Add to list or calculate buffer length */
181
182                 if (!buf) {
183                         buf_len += len + 1; /* List is comma separated */
184                         (*num_gr_mem)++;
185                         DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
186                 } else {
187                         DEBUG(10, ("appending %s at ndx %d\n", name, len));
188                         safe_strcpy(&buf[buf_ndx], name, len);
189                         buf_ndx += len;
190                         buf[buf_ndx] = ',';
191                         buf_ndx++;
192                 }
193         }
194
195         /* Allocate buffer */
196
197         if (!buf && buf_len != 0) {
198                 if (!(buf = SMB_MALLOC(buf_len))) {
199                         DEBUG(1, ("out of memory\n"));
200                         result = False;
201                         goto done;
202                 }
203                 memset(buf, 0, buf_len);
204                 goto again;
205         }
206
207         if (buf && buf_ndx > 0) {
208                 buf[buf_ndx - 1] = '\0';
209         }
210
211         *gr_mem = buf;
212         *gr_mem_len = buf_len;
213
214         DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem, 
215                    buf_len, *num_gr_mem ? buf : "NULL")); 
216         result = True;
217
218 done:
219
220         talloc_destroy(mem_ctx);
221         
222         DEBUG(10, ("fill_grent_mem returning %d\n", result));
223
224         return result;
225 }
226
227 /* Return a group structure from a group name */
228
229 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
230 {
231         DOM_SID group_sid;
232         WINBINDD_GR *grp;
233         struct winbindd_domain *domain;
234         enum SID_NAME_USE name_type;
235         fstring name_domain, name_group;
236         char *tmp, *gr_mem;
237         int gr_mem_len;
238         gid_t gid;
239         
240         /* Ensure null termination */
241         state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
242
243         DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
244                   state->request.data.groupname));
245
246         /* Parse domain and groupname */
247         
248         memset(name_group, 0, sizeof(fstring));
249
250         tmp = state->request.data.groupname;
251         
252         parse_domain_user(tmp, name_domain, name_group);
253
254         /* if no domain or our local domain, then do a local tdb search */
255         
256         if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) &&
257              ((grp = wb_getgrnam(name_group)) != NULL) ) {
258
259                 char *buffer = NULL;
260                 
261                 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
262
263                 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
264                 
265                 state->response.data.gr.gr_mem_ofs = 0;
266                 state->response.length += gr_mem_len;
267                 state->response.extra_data = buffer;    /* give the memory away */
268                 
269                 return WINBINDD_OK;
270         }
271
272         /* if no domain or our local domain and no local tdb group, default to
273          * our local domain for aliases */
274
275         if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
276                 fstrcpy(name_domain, get_global_sam_name());
277         }
278
279         /* Get info for the domain */
280
281         if ((domain = find_domain_from_name(name_domain)) == NULL) {
282                 DEBUG(3, ("could not get domain sid for domain %s\n",
283                           name_domain));
284                 return WINBINDD_ERROR;
285         }
286         /* should we deal with users for our domain? */
287         
288         if ( lp_winbind_trusted_domains_only() && domain->primary) {
289                 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n", 
290                         name_domain, name_group));
291                 return WINBINDD_ERROR;
292         }
293
294         /* Get rid and name type from name */
295         
296         if (!winbindd_lookup_sid_by_name(domain, domain->name, name_group, &group_sid, 
297                                          &name_type)) {
298                 DEBUG(1, ("group %s in domain %s does not exist\n", 
299                           name_group, name_domain));
300                 return WINBINDD_ERROR;
301         }
302
303         if ( !((name_type==SID_NAME_DOM_GRP) ||
304                ((name_type==SID_NAME_ALIAS) && domain->primary) ||
305                ((name_type==SID_NAME_ALIAS) && domain->internal)) )
306         {
307                 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
308                           name_group, name_type));
309                 return WINBINDD_ERROR;
310         }
311
312         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
313                 DEBUG(1, ("error converting unix gid to sid\n"));
314                 return WINBINDD_ERROR;
315         }
316
317         if (!fill_grent(&state->response.data.gr, name_domain,
318                         name_group, gid) ||
319             !fill_grent_mem(domain, &group_sid, name_type,
320                             &state->response.data.gr.num_gr_mem,
321                             &gr_mem, &gr_mem_len)) {
322                 return WINBINDD_ERROR;
323         }
324
325         /* Group membership lives at start of extra data */
326
327         state->response.data.gr.gr_mem_ofs = 0;
328
329         state->response.length += gr_mem_len;
330         state->response.extra_data = gr_mem;
331
332         return WINBINDD_OK;
333 }
334
335 /* Return a group structure from a gid number */
336
337 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
338 {
339         struct winbindd_domain *domain;
340         WINBINDD_GR *grp;
341         DOM_SID group_sid;
342         enum SID_NAME_USE name_type;
343         fstring dom_name;
344         fstring group_name;
345         int gr_mem_len;
346         char *gr_mem;
347
348         DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, 
349                   (unsigned long)state->request.data.gid));
350
351         /* Bug out if the gid isn't in the winbind range */
352
353         if ((state->request.data.gid < server_state.gid_low) ||
354             (state->request.data.gid > server_state.gid_high))
355                 return WINBINDD_ERROR;
356
357         /* alway try local tdb lookup first */
358         if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
359                 char *buffer = NULL;
360                 
361                 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
362                 
363                 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
364                 
365                 state->response.data.gr.gr_mem_ofs = 0;
366                 state->response.length += gr_mem_len;
367                 state->response.extra_data = buffer;    /* give away the memory */
368                 
369                 return WINBINDD_OK;
370         }
371
372         /* Get rid from gid */
373         if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
374                 DEBUG(1, ("could not convert gid %lu to rid\n", 
375                           (unsigned long)state->request.data.gid));
376                 return WINBINDD_ERROR;
377         }
378
379         /* Get name from sid */
380
381         if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
382                 DEBUG(1, ("could not lookup sid\n"));
383                 return WINBINDD_ERROR;
384         }
385
386         /* Fill in group structure */
387
388         domain = find_domain_from_sid(&group_sid);
389
390         if (!domain) {
391                 DEBUG(1,("Can't find domain from sid\n"));
392                 return WINBINDD_ERROR;
393         }
394
395         if ( !((name_type==SID_NAME_DOM_GRP) ||
396                ((name_type==SID_NAME_ALIAS) && domain->primary) ||
397                ((name_type==SID_NAME_ALIAS) && domain->internal)) )
398         {
399                 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
400                           group_name, name_type));
401                 return WINBINDD_ERROR;
402         }
403
404         if (!fill_grent(&state->response.data.gr, dom_name, group_name, 
405                         state->request.data.gid) ||
406             !fill_grent_mem(domain, &group_sid, name_type,
407                             &state->response.data.gr.num_gr_mem,
408                             &gr_mem, &gr_mem_len))
409                 return WINBINDD_ERROR;
410
411         /* Group membership lives at start of extra data */
412
413         state->response.data.gr.gr_mem_ofs = 0;
414
415         state->response.length += gr_mem_len;
416         state->response.extra_data = gr_mem;
417
418         return WINBINDD_OK;
419 }
420
421 /*
422  * set/get/endgrent functions
423  */
424
425 /* "Rewind" file pointer for group database enumeration */
426
427 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
428 {
429         struct winbindd_domain *domain;
430
431         DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
432
433         /* Check user has enabled this */
434
435         if (!lp_winbind_enum_groups())
436                 return WINBINDD_ERROR;
437
438         /* Free old static data if it exists */
439         
440         if (state->getgrent_state != NULL) {
441                 free_getent_state(state->getgrent_state);
442                 state->getgrent_state = NULL;
443         }
444         
445         /* Create sam pipes for each domain we know about */
446         
447         for (domain = domain_list(); domain != NULL; domain = domain->next) {
448                 struct getent_state *domain_state;
449                 
450                 /* Create a state record for this domain */
451
452                 /* don't add our domaina if we are a PDC or if we 
453                    are a member of a Samba domain */
454                 
455                 if ( lp_winbind_trusted_domains_only() && domain->primary )
456                 {
457                         continue;
458                 }
459                                                 
460                 
461                 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
462                         DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
463                         return WINBINDD_ERROR;
464                 }
465                 
466                 ZERO_STRUCTP(domain_state);
467                 
468                 fstrcpy(domain_state->domain_name, domain->name);
469
470                 /* Add to list of open domains */
471                 
472                 DLIST_ADD(state->getgrent_state, domain_state);
473         }
474         
475         state->getgrent_initialized = True;
476
477         return WINBINDD_OK;
478 }
479
480 /* Close file pointer to ntdom group database */
481
482 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
483 {
484         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
485
486         free_getent_state(state->getgrent_state);
487         state->getgrent_initialized = False;
488         state->getgrent_state = NULL;
489         
490         return WINBINDD_OK;
491 }
492
493 /* Get the list of domain groups and domain aliases for a domain.  We fill in
494    the sam_entries and num_sam_entries fields with domain group information.  
495    The dispinfo_ndx field is incremented to the index of the next group to 
496    fetch. Return True if some groups were returned, False otherwise. */
497
498 static BOOL get_sam_group_entries(struct getent_state *ent)
499 {
500         NTSTATUS status;
501         uint32 num_entries;
502         struct acct_info *name_list = NULL, *tmp_name_list = NULL;
503         TALLOC_CTX *mem_ctx;
504         BOOL result = False;
505         struct acct_info *sam_grp_entries = NULL;
506         struct winbindd_domain *domain;
507         
508         if (ent->got_sam_entries)
509                 return False;
510
511         if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
512                                           ent->domain_name))) {
513                 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); 
514                 return False;
515         }
516                 
517         /* Free any existing group info */
518
519         SAFE_FREE(ent->sam_entries);
520         ent->num_sam_entries = 0;
521         ent->got_sam_entries = True;
522
523         /* Enumerate domain groups */
524
525         num_entries = 0;
526
527         if (!(domain = find_domain_from_name(ent->domain_name))) {
528                 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
529                 goto done;
530         }
531
532         /* always get the domain global groups */
533
534         status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
535         
536         if (!NT_STATUS_IS_OK(status)) {
537                 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
538                 result = False;
539                 goto done;
540         }
541
542         /* Copy entries into return buffer */
543
544         if (num_entries) {
545                 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
546                         DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", 
547                                 num_entries));
548                         result = False;
549                         goto done;
550                 }
551                 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
552         }
553         
554         ent->num_sam_entries = num_entries;
555         
556         /* get the domain local groups if we are a member of a native win2k domain
557            and are not using LDAP to get the groups */
558            
559         if ( ( lp_security() != SEC_ADS && domain->native_mode 
560                 && domain->primary) || domain->internal )
561         {
562                 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
563                 
564                 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
565                 
566                 if ( !NT_STATUS_IS_OK(status) ) { 
567                         DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
568                         num_entries = 0;
569                 }
570                 else
571                         DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
572                 
573                 /* Copy entries into return buffer */
574
575                 if ( num_entries ) {
576                         if ( !(tmp_name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
577                         {
578                                 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", 
579                                         num_entries));
580                                 result = False;
581                                 SAFE_FREE( name_list );
582                                 goto done;
583                         }
584                         
585                         name_list = tmp_name_list;
586                                 
587                         memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, 
588                                 num_entries * sizeof(struct acct_info) );
589                 }
590         
591                 ent->num_sam_entries += num_entries;
592         }
593         
594                 
595         /* Fill in remaining fields */
596
597         ent->sam_entries = name_list;
598         ent->sam_entry_index = 0;
599
600         result = (ent->num_sam_entries > 0);
601
602  done:
603         talloc_destroy(mem_ctx);
604
605         return result;
606 }
607
608 /* Fetch next group entry from ntdom database */
609
610 #define MAX_GETGRENT_GROUPS 500
611
612 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
613 {
614         struct getent_state *ent;
615         struct winbindd_gr *group_list = NULL;
616         int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
617         char *new_extra_data, *gr_mem_list = NULL;
618
619         DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
620
621         /* Check user has enabled this */
622
623         if (!lp_winbind_enum_groups())
624                 return WINBINDD_ERROR;
625
626         num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
627
628         if ((state->response.extra_data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL)
629                 return WINBINDD_ERROR;
630
631         memset(state->response.extra_data, '\0',
632                 num_groups * sizeof(struct winbindd_gr) );
633
634         state->response.data.num_entries = 0;
635
636         group_list = (struct winbindd_gr *)state->response.extra_data;
637
638         if (!state->getgrent_initialized)
639                 winbindd_setgrent(state);
640
641         if (!(ent = state->getgrent_state))
642                 return WINBINDD_ERROR;
643
644         /* Start sending back groups */
645
646         for (i = 0; i < num_groups; i++) {
647                 struct acct_info *name_list = NULL;
648                 fstring domain_group_name;
649                 uint32 result;
650                 gid_t group_gid;
651                 int gr_mem_len;
652                 char *gr_mem, *new_gr_mem_list;
653                 DOM_SID group_sid;
654                 struct winbindd_domain *domain;
655                                 
656                 /* Do we need to fetch another chunk of groups? */
657
658         tryagain:
659
660                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
661                            ent->sam_entry_index, ent->num_sam_entries));
662
663                 if (ent->num_sam_entries == ent->sam_entry_index) {
664
665                         while(ent && !get_sam_group_entries(ent)) {
666                                 struct getent_state *next_ent;
667
668                                 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 
669
670                                 /* Free state information for this domain */
671
672                                 SAFE_FREE(ent->sam_entries);
673
674                                 next_ent = ent->next;
675                                 DLIST_REMOVE(state->getgrent_state, ent);
676                                 
677                                 SAFE_FREE(ent);
678                                 ent = next_ent;
679                         }
680
681                         /* No more domains */
682
683                         if (!ent) 
684                                 break;
685                 }
686                 
687                 name_list = ent->sam_entries;
688                 
689                 if (!(domain = 
690                       find_domain_from_name(ent->domain_name))) {
691                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
692                         result = False;
693                         goto done;
694                 }
695
696                 /* Lookup group info */
697                 
698                 sid_copy(&group_sid, &domain->sid);
699                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
700
701                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
702                         
703                         DEBUG(1, ("could not look up gid for group %s\n", 
704                                   name_list[ent->sam_entry_index].acct_name));
705                         
706                         ent->sam_entry_index++;
707                         goto tryagain;
708                 }
709
710                 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
711                            (unsigned long)name_list[ent->sam_entry_index].rid));
712                 
713                 /* Fill in group entry */
714
715                 fill_domain_username(domain_group_name, ent->domain_name, 
716                          name_list[ent->sam_entry_index].acct_name);
717
718                 result = fill_grent(&group_list[group_list_ndx], 
719                                     ent->domain_name,
720                                     name_list[ent->sam_entry_index].acct_name,
721                                     group_gid);
722
723                 /* Fill in group membership entry */
724
725                 if (result) {
726                         DOM_SID member_sid;
727                         group_list[group_list_ndx].num_gr_mem = 0;
728                         gr_mem = NULL;
729                         gr_mem_len = 0;
730                         
731                         /* Get group membership */                      
732                         if (state->request.cmd == WINBINDD_GETGRLST) {
733                                 result = True;
734                         } else {
735                                 sid_copy(&member_sid, &domain->sid);
736                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
737                                 result = fill_grent_mem(
738                                         domain,
739                                         &member_sid,
740                                         SID_NAME_DOM_GRP,
741                                         &group_list[group_list_ndx].num_gr_mem, 
742                                         &gr_mem, &gr_mem_len);
743                         }
744                 }
745
746                 if (result) {
747                         /* Append to group membership list */
748                         new_gr_mem_list = SMB_REALLOC( gr_mem_list, gr_mem_list_len + gr_mem_len);
749
750                         if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
751                                 DEBUG(0, ("out of memory\n"));
752                                 SAFE_FREE(gr_mem_list);
753                                 gr_mem_list_len = 0;
754                                 break;
755                         }
756
757                         DEBUG(10, ("list_len = %d, mem_len = %d\n",
758                                    gr_mem_list_len, gr_mem_len));
759
760                         gr_mem_list = new_gr_mem_list;
761
762                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
763                                gr_mem_len);
764
765                         SAFE_FREE(gr_mem);
766
767                         group_list[group_list_ndx].gr_mem_ofs = 
768                                 gr_mem_list_len;
769
770                         gr_mem_list_len += gr_mem_len;
771                 }
772
773                 ent->sam_entry_index++;
774                 
775                 /* Add group to return list */
776                 
777                 if (result) {
778
779                         DEBUG(10, ("adding group num_entries = %d\n",
780                                    state->response.data.num_entries));
781
782                         group_list_ndx++;
783                         state->response.data.num_entries++;
784                         
785                         state->response.length +=
786                                 sizeof(struct winbindd_gr);
787                         
788                 } else {
789                         DEBUG(0, ("could not lookup domain group %s\n", 
790                                   domain_group_name));
791                 }
792         }
793
794         /* Copy the list of group memberships to the end of the extra data */
795
796         if (group_list_ndx == 0)
797                 goto done;
798
799         new_extra_data = SMB_REALLOC(
800                 state->response.extra_data,
801                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
802
803         if (!new_extra_data) {
804                 DEBUG(0, ("out of memory\n"));
805                 group_list_ndx = 0;
806                 SAFE_FREE(state->response.extra_data);
807                 SAFE_FREE(gr_mem_list);
808
809                 return WINBINDD_ERROR;
810         }
811
812         state->response.extra_data = new_extra_data;
813
814         memcpy(&((char *)state->response.extra_data)
815                [group_list_ndx * sizeof(struct winbindd_gr)], 
816                gr_mem_list, gr_mem_list_len);
817
818         SAFE_FREE(gr_mem_list);
819
820         state->response.length += gr_mem_list_len;
821
822         DEBUG(10, ("returning %d groups, length = %d\n",
823                    group_list_ndx, gr_mem_list_len));
824
825         /* Out of domains */
826
827  done:
828
829         return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
830 }
831
832 /* List domain groups without mapping to unix ids */
833
834 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
835 {
836         uint32 total_entries = 0;
837         struct winbindd_domain *domain;
838         const char *which_domain;
839         char *extra_data = NULL;
840         char *ted = NULL;
841         unsigned int extra_data_len = 0, i;
842
843         DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
844
845         /* Ensure null termination */
846         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
847         which_domain = state->request.domain_name;
848         
849         /* Enumerate over trusted domains */
850
851         for (domain = domain_list(); domain; domain = domain->next) {
852                 struct getent_state groups;
853
854                 /* if we have a domain name restricting the request and this
855                    one in the list doesn't match, then just bypass the remainder
856                    of the loop */
857                    
858                 if ( *which_domain && !strequal(which_domain, domain->name) )
859                         continue;
860
861                 if ( !domain->initialized )
862                         set_dc_type_and_flags( domain );
863
864                         
865                 ZERO_STRUCT(groups);
866
867                 /* Get list of sam groups */
868                 
869                 fstrcpy(groups.domain_name, domain->name);
870
871                 get_sam_group_entries(&groups);
872                         
873                 if (groups.num_sam_entries == 0) {
874                         /* this domain is empty or in an error state */
875                         continue;
876                 }
877
878                 /* keep track the of the total number of groups seen so 
879                    far over all domains */
880                 total_entries += groups.num_sam_entries;
881                 
882                 /* Allocate some memory for extra data.  Note that we limit
883                    account names to sizeof(fstring) = 128 characters.  */               
884                 ted = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
885  
886                 if (!ted) {
887                         DEBUG(0,("failed to enlarge buffer!\n"));
888                         SAFE_FREE(extra_data);
889                         return WINBINDD_ERROR;
890                 } else
891                         extra_data = ted;
892
893                 /* Pack group list into extra data fields */
894                 for (i = 0; i < groups.num_sam_entries; i++) {
895                         char *group_name = ((struct acct_info *)
896                                             groups.sam_entries)[i].acct_name; 
897                         fstring name;
898
899                         fill_domain_username(name, domain->name, group_name);
900                         /* Append to extra data */                      
901                         memcpy(&extra_data[extra_data_len], name, 
902                                strlen(name));
903                         extra_data_len += strlen(name);
904                         extra_data[extra_data_len++] = ',';
905                 }
906
907                 SAFE_FREE(groups.sam_entries);
908         }
909
910         /* Assign extra_data fields in response structure */
911         if (extra_data) {
912                 extra_data[extra_data_len - 1] = '\0';
913                 state->response.extra_data = extra_data;
914                 state->response.length += extra_data_len;
915         }
916
917         /* No domains may have responded but that's still OK so don't
918            return an error. */
919
920         return WINBINDD_OK;
921 }
922
923 static BOOL enum_alias_memberships(const DOM_SID *member_sid,
924                                    DOM_SID **aliases, int *num_aliases)
925 {
926         TALLOC_CTX *mem_ctx = talloc_init("enum_alias_memberships");
927
928         uint32 *rids = NULL;
929         int i, num_rids = 0;
930
931         BOOL result = False;
932
933         if (mem_ctx == NULL)
934                 return False;
935         
936         *aliases = NULL;
937         *num_aliases = 0;
938
939         if (!pdb_enum_alias_memberships(mem_ctx, get_global_sam_sid(),
940                                         member_sid, 1, &rids, &num_rids))
941                 goto done;
942
943         for (i=0; i<num_rids; i++) {
944                 DOM_SID alias_sid;
945                 sid_copy(&alias_sid, get_global_sam_sid());
946                 sid_append_rid(&alias_sid, rids[i]);
947                 add_sid_to_array(NULL, &alias_sid, aliases, num_aliases);
948         }
949
950         if (!pdb_enum_alias_memberships(mem_ctx, &global_sid_Builtin,
951                                         member_sid, 1, &rids, &num_rids))
952                 goto done;
953
954         for (i=0; i<num_rids; i++) {
955                 DOM_SID alias_sid;
956                 sid_copy(&alias_sid, &global_sid_Builtin);
957                 sid_append_rid(&alias_sid, rids[i]);
958                 add_sid_to_array(NULL, &alias_sid, aliases, num_aliases);
959         }
960
961         result = True;
962  done:
963         if (mem_ctx != NULL)
964                 talloc_destroy(mem_ctx);
965
966         return result;
967 }
968
969 static void add_local_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
970 {
971         gid_t gid;
972         DOM_SID *aliases;
973         int j, num_aliases;
974
975         DEBUG(10, ("Adding local gids from SID: %s\n",
976                    sid_string_static(sid)));
977
978         /* Don't expand aliases if not explicitly activated -- for now
979            -- jerry */
980
981         if (!lp_winbind_nested_groups())
982                 return;
983
984         /* Add nested group memberships */
985
986         if (!enum_alias_memberships(sid, &aliases, &num_aliases))
987                 return;
988
989         for (j=0; j<num_aliases; j++) {
990                 enum SID_NAME_USE type;
991
992                 if (!local_sid_to_gid(&gid, &aliases[j], &type)) {
993                         DEBUG(1, ("Got an alias membership with no alias\n"));
994                         continue;
995                 }
996
997                 if ((type != SID_NAME_ALIAS) && (type != SID_NAME_WKN_GRP)) {
998                         DEBUG(1, ("Got an alias membership in a non-alias\n"));
999                         continue;
1000                 }
1001
1002                 add_gid_to_array_unique(NULL, gid, gids, num);
1003         }
1004         SAFE_FREE(aliases);
1005 }
1006
1007 static void add_gids_from_user_sid(DOM_SID *sid, gid_t **gids, int *num)
1008 {
1009         DEBUG(10, ("Adding gids from user SID: %s\n",
1010                    sid_string_static(sid)));
1011
1012         add_local_gids_from_sid(sid, gids, num);
1013 }
1014
1015 static void add_gids_from_group_sid(DOM_SID *sid, gid_t **gids, int *num)
1016 {
1017         gid_t gid;
1018
1019         DEBUG(10, ("Adding gids from group SID: %s\n",
1020                    sid_string_static(sid)));
1021
1022         if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
1023                 add_gid_to_array_unique(NULL, gid, gids, num);
1024
1025         add_local_gids_from_sid(sid, gids, num);
1026 }
1027
1028 /* Get user supplementary groups.  This is much quicker than trying to
1029    invert the groups database.  We merge the groups from the gids and
1030    other_sids info3 fields as trusted domain, universal group
1031    memberships, and nested groups (win2k native mode only) are not
1032    returned by the getgroups RPC call but are present in the info3. */
1033
1034 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
1035 {
1036         fstring name_domain, name_user;
1037         DOM_SID user_sid, group_sid;
1038         enum SID_NAME_USE name_type;
1039         uint32 num_groups = 0;
1040         uint32 num_gids = 0;
1041         NTSTATUS status;
1042         DOM_SID **user_grpsids;
1043         struct winbindd_domain *domain;
1044         enum winbindd_result result = WINBINDD_ERROR;
1045         gid_t *gid_list = NULL;
1046         unsigned int i;
1047         TALLOC_CTX *mem_ctx;
1048         NET_USER_INFO_3 *info3 = NULL;
1049         
1050         /* Ensure null termination */
1051         state->request.data.username[sizeof(state->request.data.username)-1]='\0';
1052
1053         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1054                   state->request.data.username));
1055
1056         if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
1057                                           state->request.data.username)))
1058                 return WINBINDD_ERROR;
1059
1060         /* Parse domain and username */
1061
1062         parse_domain_user(state->request.data.username, 
1063                           name_domain, name_user);
1064         
1065         /* Get info for the domain */
1066         
1067         if ((domain = find_domain_from_name(name_domain)) == NULL) {
1068                 DEBUG(7, ("could not find domain entry for domain %s\n", 
1069                           name_domain));
1070                 goto done;
1071         }
1072
1073         if ( domain->primary && lp_winbind_trusted_domains_only()) {
1074                 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1075                          "getgroups() for %s\\%s.\n", name_domain, name_user));
1076                 return WINBINDD_ERROR;
1077         }       
1078         
1079         /* Get rid and name type from name.  The following costs 1 packet */
1080
1081         if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid, 
1082                                          &name_type)) {
1083                 DEBUG(4, ("user '%s' does not exist\n", name_user));
1084                 goto done;
1085         }
1086
1087         if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1088                 DEBUG(1, ("name '%s' is not a user name: %d\n", 
1089                           name_user, name_type));
1090                 goto done;
1091         }
1092
1093         add_gids_from_user_sid(&user_sid, &gid_list, &num_gids);
1094
1095         /* Treat the info3 cache as authoritative as the
1096            lookup_usergroups() function may return cached data. */
1097
1098         if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1099
1100                 struct winbindd_domain *our_domain = find_our_domain();
1101
1102                 if (our_domain == NULL) {
1103                         DEBUG(0, ("Could not find our domain\n"));
1104                         goto done;
1105                 }
1106
1107                 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1108                            info3->num_groups2, info3->num_other_sids));
1109
1110                 num_groups = info3->num_other_sids + info3->num_groups2;
1111
1112                 /* Go through each other sid and convert it to a gid */
1113
1114                 for (i = 0; i < info3->num_other_sids; i++) {
1115                         DOM_SID *sid = &info3->other_sids[i].sid;
1116                         fstring name;
1117                         fstring dom_name;
1118                         enum SID_NAME_USE sid_type;
1119
1120                         /* Is this sid known to us?  It can either be
1121                            a trusted domain sid or a foreign sid. */
1122
1123                         if (!winbindd_lookup_name_by_sid( sid, dom_name,
1124                                                           name, &sid_type)) {
1125                                 DEBUG(10, ("winbindd_getgroups: could not "
1126                                            "lookup name for %s\n", 
1127                                            sid_string_static(sid)));
1128                                 continue;
1129                         }
1130
1131                         /* Check it is a domain group or an alias (domain
1132                            local group) in a win2k native mode domain. */
1133                         
1134                         if (!((sid_type==SID_NAME_DOM_GRP) ||
1135                               ((sid_type==SID_NAME_ALIAS) &&
1136                                (our_domain->active_directory) &&
1137                                (our_domain->native_mode) &&
1138                                (sid_compare_domain(sid, &our_domain->sid)
1139                                 == 0)))) {
1140                                 DEBUG(10, ("winbindd_getgroups: sid type %d "
1141                                            "for %s is not a domain group\n",
1142                                            sid_type, sid_string_static(sid)));
1143                                 continue;
1144                         }
1145
1146                         add_gids_from_group_sid(sid, &gid_list, &num_gids);
1147                 }
1148
1149                 for (i = 0; i < info3->num_groups2; i++) {
1150                 
1151                         /* create the group SID */
1152                         
1153                         sid_copy( &group_sid, &domain->sid );
1154                         sid_append_rid( &group_sid, info3->gids[i].g_rid );
1155
1156                         add_gids_from_group_sid(&group_sid, &gid_list,
1157                                                 &num_gids);
1158                 }
1159
1160                 SAFE_FREE(info3);
1161
1162         } else {
1163                 status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1164                                                     &user_sid, &num_groups, 
1165                                                     &user_grpsids);
1166                 if (!NT_STATUS_IS_OK(status)) 
1167                         goto done;
1168
1169                 if (state->response.extra_data)
1170                         goto done;
1171
1172                 for (i = 0; i < num_groups; i++) {
1173                         add_gids_from_group_sid(user_grpsids[i],
1174                                                 &gid_list, &num_gids);
1175                 }
1176         }
1177
1178         /* We want at least one group... */
1179         if (gid_list == NULL)
1180                 goto done;
1181
1182         remove_duplicate_gids( &num_gids, gid_list );
1183
1184         /* Send data back to client */
1185
1186         state->response.data.num_entries = num_gids;
1187         state->response.extra_data = gid_list;
1188         state->response.length += num_gids * sizeof(gid_t);
1189
1190         result = WINBINDD_OK;
1191
1192  done:
1193
1194         talloc_destroy(mem_ctx);
1195
1196         return result;
1197 }
1198
1199 static void add_sid_to_parray_unique(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1200                                     DOM_SID ***sids, int *num_sids)
1201 {
1202         int i;
1203
1204         for (i=0; i<(*num_sids); i++) {
1205                 if (sid_compare(sid, (*sids)[i]) == 0)
1206                         return;
1207         }
1208
1209         *sids = TALLOC_REALLOC_ARRAY(mem_ctx, *sids, DOM_SID *, *num_sids+1);
1210
1211         if (*sids == NULL)
1212                 return;
1213
1214         (*sids)[*num_sids] = TALLOC_P(mem_ctx, DOM_SID);
1215         sid_copy((*sids)[*num_sids], sid);
1216         *num_sids += 1;
1217         return;
1218 }
1219
1220 static void add_local_sids_from_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1221                                     DOM_SID ***user_grpsids,
1222                                     int *num_groups)
1223 {
1224         DOM_SID *aliases = NULL;
1225         int i, num_aliases = 0;
1226
1227         if (!enum_alias_memberships(sid, &aliases, &num_aliases))
1228                 return;
1229
1230         if (num_aliases == 0)
1231                 return;
1232
1233         for (i=0; i<num_aliases; i++)
1234                 add_sid_to_parray_unique(mem_ctx, &aliases[i], user_grpsids,
1235                                          num_groups);
1236
1237         SAFE_FREE(aliases);
1238
1239         return;
1240 }
1241
1242 /* Get user supplementary sids. This is equivalent to the
1243    winbindd_getgroups() function but it involves a SID->SIDs mapping
1244    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1245    idmap. This call is designed to be used with applications that need
1246    to do ACL evaluation themselves. Note that the cached info3 data is
1247    not used 
1248
1249    this function assumes that the SID that comes in is a user SID. If
1250    you pass in another type of SID then you may get unpredictable
1251    results.
1252 */
1253 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1254 {
1255         DOM_SID user_sid;
1256         NTSTATUS status;
1257         DOM_SID **user_grpsids;
1258         struct winbindd_domain *domain;
1259         enum winbindd_result result = WINBINDD_ERROR;
1260         unsigned int i;
1261         TALLOC_CTX *mem_ctx;
1262         char *ret = NULL;
1263         uint32 num_groups;
1264         unsigned ofs, ret_size = 0;
1265
1266         /* Ensure null termination */
1267         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1268
1269         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1270                 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1271                 return WINBINDD_ERROR;
1272         }
1273
1274         if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1275                                     state->request.data.username))) {
1276                 return WINBINDD_ERROR;
1277         }
1278
1279         /* Get info for the domain */   
1280         if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1281                 DEBUG(0,("could not find domain entry for sid %s\n", 
1282                           sid_string_static(&user_sid)));
1283                 goto done;
1284         }
1285         
1286         status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1287                                                     &user_sid, &num_groups, 
1288                                                     &user_grpsids);
1289         if (!NT_STATUS_IS_OK(status)) 
1290                 goto done;
1291
1292         if (num_groups == 0) {
1293                 goto no_groups;
1294         }
1295
1296         domain = find_our_domain();
1297
1298         if (domain == NULL) {
1299                 DEBUG(0, ("Could not find our domain\n"));
1300                 goto done;
1301         }
1302
1303         /* Note that I do not check for AD or its mode. XP in a real NT4
1304          * domain also asks for this info. -- vl */
1305
1306         if (!IS_DC) {
1307                 uint32 *alias_rids = NULL;
1308                 int num_aliases;
1309
1310                 /* We need to include the user SID to expand */
1311                 user_grpsids = TALLOC_REALLOC_ARRAY(mem_ctx, user_grpsids,
1312                                                     DOM_SID *, num_groups+1);
1313                 user_grpsids[num_groups] = &user_sid;
1314
1315                 status = domain->methods->lookup_useraliases(domain, mem_ctx,
1316                                                              num_groups+1,
1317                                                              user_grpsids,
1318                                                              &num_aliases,
1319                                                              &alias_rids);
1320
1321                 if (!NT_STATUS_IS_OK(status)) {
1322                         DEBUG(3, ("Could not expand alias sids: %s\n",
1323                                   nt_errstr(status)));
1324                         goto done;
1325                 }
1326
1327                 for (i=0; i<num_aliases; i++) {
1328                         DOM_SID sid;
1329                         sid_copy(&sid, &domain->sid);
1330                         sid_append_rid(&sid, alias_rids[i]);
1331                         add_sid_to_parray_unique(mem_ctx, &sid, &user_grpsids,
1332                                                  &num_groups);
1333                 }
1334         }
1335
1336         if (lp_winbind_nested_groups()) {
1337                 int k;
1338                 /* num_groups is changed during the loop, that's why we have
1339                    to count down here.*/
1340
1341                 for (k=num_groups-1; k>=0; k--) {
1342                         add_local_sids_from_sid(mem_ctx, user_grpsids[k],
1343                                                 &user_grpsids, &num_groups);
1344                 }
1345
1346                 add_local_sids_from_sid(mem_ctx, &user_sid, &user_grpsids,
1347                                         &num_groups);
1348         }
1349
1350         /* work out the response size */
1351         for (i = 0; i < num_groups; i++) {
1352                 const char *s = sid_string_static(user_grpsids[i]);
1353                 ret_size += strlen(s) + 1;
1354         }
1355
1356         /* build the reply */
1357         ret = SMB_MALLOC(ret_size);
1358         if (!ret) goto done;
1359         ofs = 0;
1360         for (i = 0; i < num_groups; i++) {
1361                 const char *s = sid_string_static(user_grpsids[i]);
1362                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1363                 ofs += strlen(ret+ofs) + 1;
1364         }
1365
1366 no_groups:
1367         /* Send data back to client */
1368         state->response.data.num_entries = num_groups;
1369         state->response.extra_data = ret;
1370         state->response.length += ret_size;
1371         result = WINBINDD_OK;
1372
1373  done:
1374         talloc_destroy(mem_ctx);
1375
1376         return result;
1377 }
1378