r116: volker's patch for local group and group nesting
[ira/wip.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    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
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, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27
28 extern BOOL opt_nocache;
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
32
33 /*********************************************************************
34 *********************************************************************/
35
36 static int gr_mem_buffer( char **buffer, char **members, int num_members )
37 {
38         int i;
39         int len = 0;
40         int idx = 0;
41
42         if ( num_members == 0 ) {
43                 *buffer = NULL;
44                 return 0;
45         }
46         
47         for ( i=0; i<num_members; i++ )
48                 len += strlen(members[i])+1;
49
50         *buffer = (char*)smb_xmalloc(len);
51         for ( i=0; i<num_members; i++ ) {
52                 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
53                 idx += strlen(members[i])+1;
54         }
55         /* terminate with NULL */
56         (*buffer)[len-1] = '\0';
57         
58         return len;     
59 }
60
61 /***************************************************************
62  Empty static struct for negative caching.
63 ****************************************************************/
64
65 /* Fill a grent structure from various other information */
66
67 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name, 
68                        const char *gr_name, gid_t unix_gid)
69 {
70         fstring full_group_name;
71         /* Fill in uid/gid */
72         fill_domain_username(full_group_name, dom_name, gr_name);
73
74         gr->gr_gid = unix_gid;
75     
76         /* Group name and password */
77     
78         safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
79         safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
80
81         return True;
82 }
83
84 /* Fill in the group membership field of a NT group given by group_sid */
85
86 static BOOL fill_grent_mem(struct winbindd_domain *domain,
87                            DOM_SID *group_sid, 
88                            enum SID_NAME_USE group_name_type, 
89                            int *num_gr_mem, char **gr_mem, int *gr_mem_len)
90 {
91         DOM_SID **sid_mem = NULL;
92         uint32 num_names = 0;
93         uint32 *name_types = NULL;
94         unsigned int buf_len, buf_ndx, i;
95         char **names = NULL, *buf;
96         BOOL result = False;
97         TALLOC_CTX *mem_ctx;
98         NTSTATUS status;
99         fstring sid_string;
100
101         if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
102                 return False;
103
104         /* Initialise group membership information */
105         
106         DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
107
108         *num_gr_mem = 0;
109
110         /* HACK ALERT!! This whole routine does not cope with group members
111          * from more than one domain, ie aliases. Thus we have to work it out
112          * ourselves in a special routine. */
113
114         if (domain->internal)
115                 return fill_passdb_alias_grmem(domain, group_sid,
116                                                num_gr_mem,
117                                                gr_mem, gr_mem_len);
118         
119         if ( !((group_name_type==SID_NAME_DOM_GRP) ||
120                 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
121         {
122                 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", 
123                           sid_to_string(sid_string, group_sid), domain->name, 
124                           group_name_type));
125                 goto done;
126         }
127
128         /* Lookup group members */
129         status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names, 
130                                                   &sid_mem, &names, &name_types);
131         if (!NT_STATUS_IS_OK(status)) {
132                 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n", 
133                           sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
134
135                 goto done;
136         }
137
138         DEBUG(10, ("looked up %d names\n", num_names));
139
140         if (DEBUGLEVEL >= 10) {
141                 for (i = 0; i < num_names; i++)
142                         DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
143                                    name_types[i]));
144         }
145
146         /* Add members to list */
147
148         buf = NULL;
149         buf_len = buf_ndx = 0;
150
151  again:
152
153         for (i = 0; i < num_names; i++) {
154                 char *the_name;
155                 fstring name;
156                 int len;
157                         
158                 the_name = names[i];
159
160                 DEBUG(10, ("processing name %s\n", the_name));
161
162                 /* FIXME: need to cope with groups within groups.  These
163                    occur in Universal groups on a Windows 2000 native mode
164                    server. */
165
166                 /* make sure to allow machine accounts */
167
168                 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
169                         DEBUG(3, ("name %s isn't a domain user\n", the_name));
170                         continue;
171                 }
172
173                 /* Append domain name */
174
175                 fill_domain_username(name, domain->name, the_name);
176
177                 len = strlen(name);
178                 
179                 /* Add to list or calculate buffer length */
180
181                 if (!buf) {
182                         buf_len += len + 1; /* List is comma separated */
183                         (*num_gr_mem)++;
184                         DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
185                 } else {
186                         DEBUG(10, ("appending %s at ndx %d\n", name, len));
187                         safe_strcpy(&buf[buf_ndx], name, len);
188                         buf_ndx += len;
189                         buf[buf_ndx] = ',';
190                         buf_ndx++;
191                 }
192         }
193
194         /* Allocate buffer */
195
196         if (!buf && buf_len != 0) {
197                 if (!(buf = malloc(buf_len))) {
198                         DEBUG(1, ("out of memory\n"));
199                         result = False;
200                         goto done;
201                 }
202                 memset(buf, 0, buf_len);
203                 goto again;
204         }
205
206         if (buf && buf_ndx > 0) {
207                 buf[buf_ndx - 1] = '\0';
208         }
209
210         *gr_mem = buf;
211         *gr_mem_len = buf_len;
212
213         DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem, 
214                    buf_len, *num_gr_mem ? buf : "NULL")); 
215         result = True;
216
217 done:
218
219         talloc_destroy(mem_ctx);
220         
221         DEBUG(10, ("fill_grent_mem returning %d\n", result));
222
223         return result;
224 }
225
226 /* Return a group structure from a group name */
227
228 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
229 {
230         DOM_SID group_sid;
231         WINBINDD_GR *grp;
232         struct winbindd_domain *domain;
233         enum SID_NAME_USE name_type;
234         fstring name_domain, name_group;
235         char *tmp, *gr_mem;
236         int gr_mem_len;
237         gid_t gid;
238         
239         /* Ensure null termination */
240         state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
241
242         DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
243                   state->request.data.groupname));
244
245         /* Parse domain and groupname */
246         
247         memset(name_group, 0, sizeof(fstring));
248
249         tmp = state->request.data.groupname;
250         
251         parse_domain_user(tmp, name_domain, name_group);
252
253         /* if no domain or our local domain, then do a local tdb search */
254         
255         if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) &&
256              ((grp = wb_getgrnam(name_group)) != NULL) ) {
257
258                 char *buffer = NULL;
259                 
260                 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
261
262                 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
263                 
264                 state->response.data.gr.gr_mem_ofs = 0;
265                 state->response.length += gr_mem_len;
266                 state->response.extra_data = buffer;    /* give the memory away */
267                 
268                 return WINBINDD_OK;
269         }
270
271         /* if no domain or our local domain and no local tdb group, default to
272          * our local domain for aliases */
273
274         if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
275                 fstrcpy(name_domain, get_global_sam_name());
276         }
277
278         /* Get info for the domain */
279
280         if ((domain = find_domain_from_name(name_domain)) == NULL) {
281                 DEBUG(3, ("could not get domain sid for domain %s\n",
282                           name_domain));
283                 return WINBINDD_ERROR;
284         }
285         /* should we deal with users for our domain? */
286         
287         if ( lp_winbind_trusted_domains_only() && domain->primary) {
288                 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n", 
289                         name_domain, name_group));
290                 return WINBINDD_ERROR;
291         }
292
293         /* Get rid and name type from name */
294         
295         if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid, 
296                                          &name_type)) {
297                 DEBUG(1, ("group %s in domain %s does not exist\n", 
298                           name_group, name_domain));
299                 return WINBINDD_ERROR;
300         }
301
302         if ( !((name_type==SID_NAME_DOM_GRP) ||
303                ((name_type==SID_NAME_ALIAS) && domain->primary) ||
304                ((name_type==SID_NAME_ALIAS) && domain->internal)) )
305         {
306                 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
307                           name_group, name_type));
308                 return WINBINDD_ERROR;
309         }
310
311         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
312                 DEBUG(1, ("error converting unix gid to sid\n"));
313                 return WINBINDD_ERROR;
314         }
315
316         if (!fill_grent(&state->response.data.gr, name_domain,
317                         name_group, gid) ||
318             !fill_grent_mem(domain, &group_sid, name_type,
319                             &state->response.data.gr.num_gr_mem,
320                             &gr_mem, &gr_mem_len)) {
321                 return WINBINDD_ERROR;
322         }
323
324         /* Group membership lives at start of extra data */
325
326         state->response.data.gr.gr_mem_ofs = 0;
327
328         state->response.length += gr_mem_len;
329         state->response.extra_data = gr_mem;
330
331         return WINBINDD_OK;
332 }
333
334 /* Return a group structure from a gid number */
335
336 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
337 {
338         struct winbindd_domain *domain;
339         WINBINDD_GR *grp;
340         DOM_SID group_sid;
341         enum SID_NAME_USE name_type;
342         fstring dom_name;
343         fstring group_name;
344         int gr_mem_len;
345         char *gr_mem;
346
347         DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, 
348                   (unsigned long)state->request.data.gid));
349
350         /* Bug out if the gid isn't in the winbind range */
351
352         if ((state->request.data.gid < server_state.gid_low) ||
353             (state->request.data.gid > server_state.gid_high))
354                 return WINBINDD_ERROR;
355
356         /* alway try local tdb lookup first */
357         if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
358                 char *buffer = NULL;
359                 
360                 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
361                 
362                 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
363                 
364                 state->response.data.gr.gr_mem_ofs = 0;
365                 state->response.length += gr_mem_len;
366                 state->response.extra_data = buffer;    /* give away the memory */
367                 
368                 return WINBINDD_OK;
369         }
370
371         /* Get rid from gid */
372         if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
373                 DEBUG(1, ("could not convert gid %lu to rid\n", 
374                           (unsigned long)state->request.data.gid));
375                 return WINBINDD_ERROR;
376         }
377
378         /* Get name from sid */
379
380         if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
381                 DEBUG(1, ("could not lookup sid\n"));
382                 return WINBINDD_ERROR;
383         }
384
385         /* Fill in group structure */
386
387         domain = find_domain_from_sid(&group_sid);
388
389         if (!domain) {
390                 DEBUG(1,("Can't find domain from sid\n"));
391                 return WINBINDD_ERROR;
392         }
393
394         if ( !((name_type==SID_NAME_DOM_GRP) ||
395                ((name_type==SID_NAME_ALIAS) && domain->primary) ||
396                ((name_type==SID_NAME_ALIAS) && domain->internal)) )
397         {
398                 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
399                           group_name, name_type));
400                 return WINBINDD_ERROR;
401         }
402
403         if (!fill_grent(&state->response.data.gr, dom_name, group_name, 
404                         state->request.data.gid) ||
405             !fill_grent_mem(domain, &group_sid, name_type,
406                             &state->response.data.gr.num_gr_mem,
407                             &gr_mem, &gr_mem_len))
408                 return WINBINDD_ERROR;
409
410         /* Group membership lives at start of extra data */
411
412         state->response.data.gr.gr_mem_ofs = 0;
413
414         state->response.length += gr_mem_len;
415         state->response.extra_data = gr_mem;
416
417         return WINBINDD_OK;
418 }
419
420 /*
421  * set/get/endgrent functions
422  */
423
424 /* "Rewind" file pointer for group database enumeration */
425
426 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
427 {
428         struct winbindd_domain *domain;
429
430         DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
431
432         /* Check user has enabled this */
433
434         if (!lp_winbind_enum_groups())
435                 return WINBINDD_ERROR;
436
437         /* Free old static data if it exists */
438         
439         if (state->getgrent_state != NULL) {
440                 free_getent_state(state->getgrent_state);
441                 state->getgrent_state = NULL;
442         }
443         
444         /* Create sam pipes for each domain we know about */
445         
446         for (domain = domain_list(); domain != NULL; domain = domain->next) {
447                 struct getent_state *domain_state;
448                 
449                 
450                 /* don't add our domaina if we are a PDC or if we 
451                    are a member of a Samba domain */
452                 
453                 if ( (IS_DC || lp_winbind_trusted_domains_only())
454                         && domain->primary )
455                 {
456                         continue;
457                 }
458                                                 
459                 /* Create a state record for this domain */
460                 
461                 if ((domain_state = (struct getent_state *)
462                      malloc(sizeof(struct getent_state))) == NULL) {
463                         DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
464                         return WINBINDD_ERROR;
465                 }
466                 
467                 ZERO_STRUCTP(domain_state);
468                 
469                 fstrcpy(domain_state->domain_name, domain->name);
470
471                 /* Add to list of open domains */
472                 
473                 DLIST_ADD(state->getgrent_state, domain_state);
474         }
475         
476         return WINBINDD_OK;
477 }
478
479 /* Close file pointer to ntdom group database */
480
481 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
482 {
483         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
484
485         free_getent_state(state->getgrent_state);
486         state->getgrent_state = NULL;
487         
488         return WINBINDD_OK;
489 }
490
491 /* Get the list of domain groups and domain aliases for a domain.  We fill in
492    the sam_entries and num_sam_entries fields with domain group information.  
493    The dispinfo_ndx field is incremented to the index of the next group to 
494    fetch. Return True if some groups were returned, False otherwise. */
495
496 #define MAX_FETCH_SAM_ENTRIES 100
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 = malloc(sizeof(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 = Realloc( name_list, sizeof(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 = 
629              malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
630                 return WINBINDD_ERROR;
631
632         state->response.data.num_entries = 0;
633
634         group_list = (struct winbindd_gr *)state->response.extra_data;
635
636         if (!(ent = state->getgrent_state))
637                 return WINBINDD_ERROR;
638
639         /* Start sending back groups */
640
641         for (i = 0; i < num_groups; i++) {
642                 struct acct_info *name_list = NULL;
643                 fstring domain_group_name;
644                 uint32 result;
645                 gid_t group_gid;
646                 int gr_mem_len;
647                 char *gr_mem, *new_gr_mem_list;
648                 DOM_SID group_sid;
649                 struct winbindd_domain *domain;
650                                 
651                 /* Do we need to fetch another chunk of groups? */
652
653         tryagain:
654
655                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
656                            ent->sam_entry_index, ent->num_sam_entries));
657
658                 if (ent->num_sam_entries == ent->sam_entry_index) {
659
660                         while(ent && !get_sam_group_entries(ent)) {
661                                 struct getent_state *next_ent;
662
663                                 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 
664
665                                 /* Free state information for this domain */
666
667                                 SAFE_FREE(ent->sam_entries);
668
669                                 next_ent = ent->next;
670                                 DLIST_REMOVE(state->getgrent_state, ent);
671                                 
672                                 SAFE_FREE(ent);
673                                 ent = next_ent;
674                         }
675
676                         /* No more domains */
677
678                         if (!ent) 
679                                 break;
680                 }
681                 
682                 name_list = ent->sam_entries;
683                 
684                 if (!(domain = 
685                       find_domain_from_name(ent->domain_name))) {
686                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
687                         result = False;
688                         goto done;
689                 }
690
691                 /* Lookup group info */
692                 
693                 sid_copy(&group_sid, &domain->sid);
694                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
695
696                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
697                         
698                         DEBUG(1, ("could not look up gid for group %s\n", 
699                                   name_list[ent->sam_entry_index].acct_name));
700                         
701                         ent->sam_entry_index++;
702                         goto tryagain;
703                 }
704
705                 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
706                            (unsigned long)name_list[ent->sam_entry_index].rid));
707                 
708                 /* Fill in group entry */
709
710                 fill_domain_username(domain_group_name, ent->domain_name, 
711                          name_list[ent->sam_entry_index].acct_name);
712
713                 result = fill_grent(&group_list[group_list_ndx], 
714                                     ent->domain_name,
715                                     name_list[ent->sam_entry_index].acct_name,
716                                     group_gid);
717
718                 /* Fill in group membership entry */
719
720                 if (result) {
721                         DOM_SID member_sid;
722                         group_list[group_list_ndx].num_gr_mem = 0;
723                         gr_mem = NULL;
724                         gr_mem_len = 0;
725                         
726                         /* Get group membership */                      
727                         if (state->request.cmd == WINBINDD_GETGRLST) {
728                                 result = True;
729                         } else {
730                                 sid_copy(&member_sid, &domain->sid);
731                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
732                                 result = fill_grent_mem(
733                                         domain,
734                                         &member_sid,
735                                         SID_NAME_DOM_GRP,
736                                         &group_list[group_list_ndx].num_gr_mem, 
737                                         &gr_mem, &gr_mem_len);
738                         }
739                 }
740
741                 if (result) {
742                         /* Append to group membership list */
743                         new_gr_mem_list = Realloc(
744                                 gr_mem_list,
745                                 gr_mem_list_len + gr_mem_len);
746
747                         if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
748                                 DEBUG(0, ("out of memory\n"));
749                                 SAFE_FREE(gr_mem_list);
750                                 gr_mem_list_len = 0;
751                                 break;
752                         }
753
754                         DEBUG(10, ("list_len = %d, mem_len = %d\n",
755                                    gr_mem_list_len, gr_mem_len));
756
757                         gr_mem_list = new_gr_mem_list;
758
759                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
760                                gr_mem_len);
761
762                         SAFE_FREE(gr_mem);
763
764                         group_list[group_list_ndx].gr_mem_ofs = 
765                                 gr_mem_list_len;
766
767                         gr_mem_list_len += gr_mem_len;
768                 }
769
770                 ent->sam_entry_index++;
771                 
772                 /* Add group to return list */
773                 
774                 if (result) {
775
776                         DEBUG(10, ("adding group num_entries = %d\n",
777                                    state->response.data.num_entries));
778
779                         group_list_ndx++;
780                         state->response.data.num_entries++;
781                         
782                         state->response.length +=
783                                 sizeof(struct winbindd_gr);
784                         
785                 } else {
786                         DEBUG(0, ("could not lookup domain group %s\n", 
787                                   domain_group_name));
788                 }
789         }
790
791         /* Copy the list of group memberships to the end of the extra data */
792
793         if (group_list_ndx == 0)
794                 goto done;
795
796         new_extra_data = Realloc(
797                 state->response.extra_data,
798                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
799
800         if (!new_extra_data) {
801                 DEBUG(0, ("out of memory\n"));
802                 group_list_ndx = 0;
803                 SAFE_FREE(state->response.extra_data);
804                 SAFE_FREE(gr_mem_list);
805
806                 return WINBINDD_ERROR;
807         }
808
809         state->response.extra_data = new_extra_data;
810
811         memcpy(&((char *)state->response.extra_data)
812                [group_list_ndx * sizeof(struct winbindd_gr)], 
813                gr_mem_list, gr_mem_list_len);
814
815         SAFE_FREE(gr_mem_list);
816
817         state->response.length += gr_mem_list_len;
818
819         DEBUG(10, ("returning %d groups, length = %d\n",
820                    group_list_ndx, gr_mem_list_len));
821
822         /* Out of domains */
823
824  done:
825
826         return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
827 }
828
829 /* List domain groups without mapping to unix ids */
830
831 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
832 {
833         uint32 total_entries = 0;
834         struct winbindd_domain *domain;
835         const char *which_domain;
836         char *extra_data = NULL;
837         char *ted = NULL;
838         unsigned int extra_data_len = 0, i;
839
840         DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
841
842         /* Ensure null termination */
843         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
844         which_domain = state->request.domain_name;
845         
846         /* Enumerate over trusted domains */
847
848         for (domain = domain_list(); domain; domain = domain->next) {
849                 struct getent_state groups;
850
851                 /* if we have a domain name restricting the request and this
852                    one in the list doesn't match, then just bypass the remainder
853                    of the loop */
854                    
855                 if ( *which_domain && !strequal(which_domain, domain->name) )
856                         continue;
857                         
858                 ZERO_STRUCT(groups);
859
860                 /* Get list of sam groups */
861                 
862                 fstrcpy(groups.domain_name, domain->name);
863
864                 get_sam_group_entries(&groups);
865                         
866                 if (groups.num_sam_entries == 0) {
867                         /* this domain is empty or in an error state */
868                         continue;
869                 }
870
871                 /* keep track the of the total number of groups seen so 
872                    far over all domains */
873                 total_entries += groups.num_sam_entries;
874                 
875                 /* Allocate some memory for extra data.  Note that we limit
876                    account names to sizeof(fstring) = 128 characters.  */               
877                 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
878  
879                 if (!ted) {
880                         DEBUG(0,("failed to enlarge buffer!\n"));
881                         SAFE_FREE(extra_data);
882                         return WINBINDD_ERROR;
883                 } else
884                         extra_data = ted;
885
886                 /* Pack group list into extra data fields */
887                 for (i = 0; i < groups.num_sam_entries; i++) {
888                         char *group_name = ((struct acct_info *)
889                                             groups.sam_entries)[i].acct_name; 
890                         fstring name;
891
892                         fill_domain_username(name, domain->name, group_name);
893                         /* Append to extra data */                      
894                         memcpy(&extra_data[extra_data_len], name, 
895                                strlen(name));
896                         extra_data_len += strlen(name);
897                         extra_data[extra_data_len++] = ',';
898                 }
899
900                 SAFE_FREE(groups.sam_entries);
901         }
902
903         /* Assign extra_data fields in response structure */
904         if (extra_data) {
905                 extra_data[extra_data_len - 1] = '\0';
906                 state->response.extra_data = extra_data;
907                 state->response.length += extra_data_len;
908         }
909
910         /* No domains may have responded but that's still OK so don't
911            return an error. */
912
913         return WINBINDD_OK;
914 }
915
916 static void add_gid_to_array_unique(gid_t gid, gid_t **gids, int *num)
917 {
918         int i;
919
920         if ((*num) >= groups_max())
921                 return;
922
923         for (i=0; i<*num; i++) {
924                 if ((*gids)[i] == gid)
925                         return;
926         }
927         
928         *gids = Realloc(*gids, (*num+1) * sizeof(gid_t));
929
930         if (*gids == NULL)
931                 return;
932
933         (*gids)[*num] = gid;
934         *num += 1;
935 }
936
937 static void add_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
938 {
939         gid_t gid;
940         DOM_SID *aliases;
941         int j, num_aliases;
942
943         DEBUG(10, ("Adding gids from SID: %s\n", sid_string_static(sid)));
944
945         if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
946                 add_gid_to_array_unique(gid, gids, num);
947
948         /* Don't expand aliases if not explicitly activated -- for now */
949         /* we don't support windows local nested groups if we are a DC.
950            refer to to sid_to_gid() in the smbd server code to see why 
951            -- jerry */
952
953         if (!lp_winbind_nested_groups() || IS_DC)
954                 return;
955
956         /* Add nested group memberships */
957
958         if (!pdb_enum_alias_memberships(sid, &aliases, &num_aliases))
959                 return;
960
961         for (j=0; j<num_aliases; j++) {
962
963                 if (!NT_STATUS_IS_OK(sid_to_gid(&aliases[j], &gid)))
964                         continue;
965
966                 add_gid_to_array_unique(gid, gids, num);
967         }
968         SAFE_FREE(aliases);
969 }
970
971 /* Get user supplementary groups.  This is much quicker than trying to
972    invert the groups database.  We merge the groups from the gids and
973    other_sids info3 fields as trusted domain, universal group
974    memberships, and nested groups (win2k native mode only) are not
975    returned by the getgroups RPC call but are present in the info3. */
976
977 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
978 {
979         fstring name_domain, name_user;
980         DOM_SID user_sid, group_sid;
981         enum SID_NAME_USE name_type;
982         uint32 num_groups = 0;
983         uint32 num_gids = 0;
984         NTSTATUS status;
985         DOM_SID **user_grpsids;
986         struct winbindd_domain *domain;
987         enum winbindd_result result = WINBINDD_ERROR;
988         gid_t *gid_list = NULL;
989         unsigned int i;
990         TALLOC_CTX *mem_ctx;
991         NET_USER_INFO_3 *info3 = NULL;
992         
993         /* Ensure null termination */
994         state->request.data.username[sizeof(state->request.data.username)-1]='\0';
995
996         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
997                   state->request.data.username));
998
999         if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
1000                                           state->request.data.username)))
1001                 return WINBINDD_ERROR;
1002
1003         /* Parse domain and username */
1004
1005         parse_domain_user(state->request.data.username, 
1006                           name_domain, name_user);
1007         
1008         /* Get info for the domain */
1009         
1010         if ((domain = find_domain_from_name(name_domain)) == NULL) {
1011                 DEBUG(7, ("could not find domain entry for domain %s\n", 
1012                           name_domain));
1013                 goto done;
1014         }
1015
1016         if ( domain->primary && lp_winbind_trusted_domains_only()) {
1017                 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n", 
1018                         name_domain, name_user));
1019                 return WINBINDD_ERROR;
1020         }       
1021         
1022         /* Get rid and name type from name.  The following costs 1 packet */
1023
1024         if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid, 
1025                                          &name_type)) {
1026                 DEBUG(1, ("user '%s' does not exist\n", name_user));
1027                 goto done;
1028         }
1029
1030         if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1031                 DEBUG(1, ("name '%s' is not a user name: %d\n", 
1032                           name_user, name_type));
1033                 goto done;
1034         }
1035
1036         add_gids_from_sid(&user_sid, &gid_list, &num_gids);
1037
1038         /* Treat the info3 cache as authoritative as the
1039            lookup_usergroups() function may return cached data. */
1040
1041         if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1042
1043                 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1044                            info3->num_groups2, info3->num_other_sids));
1045
1046                 num_groups = info3->num_other_sids + info3->num_groups2;
1047
1048                 /* Go through each other sid and convert it to a gid */
1049
1050                 for (i = 0; i < info3->num_other_sids; i++) {
1051                         fstring name;
1052                         fstring dom_name;
1053                         enum SID_NAME_USE sid_type;
1054
1055                         /* Is this sid known to us?  It can either be
1056                            a trusted domain sid or a foreign sid. */
1057
1058                         if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid, 
1059                                 dom_name, name, &sid_type))
1060                         {
1061                                 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n", 
1062                                            sid_string_static(&info3->other_sids[i].sid)));
1063                                 continue;
1064                         }
1065
1066                         /* Check it is a domain group or an alias (domain local group) 
1067                            in a win2k native mode domain. */
1068                         
1069                         if ( !((sid_type==SID_NAME_DOM_GRP) ||
1070                                 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1071                         {
1072                                 DEBUG(10, ("winbindd_getgroups: sid type %d "
1073                                            "for %s is not a domain group\n",
1074                                            sid_type,
1075                                            sid_string_static(
1076                                                    &info3->other_sids[i].sid)));
1077                                 continue;
1078                         }
1079
1080                         add_gids_from_sid(&info3->other_sids[i].sid,
1081                                           &gid_list, &num_gids);
1082
1083                         if (gid_list == NULL)
1084                                 goto done;
1085                 }
1086
1087                 for (i = 0; i < info3->num_groups2; i++) {
1088                 
1089                         /* create the group SID */
1090                         
1091                         sid_copy( &group_sid, &domain->sid );
1092                         sid_append_rid( &group_sid, info3->gids[i].g_rid );
1093
1094                         add_gids_from_sid(&group_sid, &gid_list, &num_gids);
1095
1096                         if (gid_list == NULL)
1097                                 goto done;
1098                 }
1099
1100                 SAFE_FREE(info3);
1101
1102         } else {
1103                 status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1104                                                     &user_sid, &num_groups, 
1105                                                     &user_grpsids);
1106                 if (!NT_STATUS_IS_OK(status)) 
1107                         goto done;
1108
1109                 if (state->response.extra_data)
1110                         goto done;
1111
1112                 for (i = 0; i < num_groups; i++) {
1113                         add_gids_from_sid(user_grpsids[i],
1114                                           &gid_list, &num_gids);
1115
1116                         if (gid_list == NULL)
1117                                 goto done;
1118                 }
1119         }
1120
1121         /* Send data back to client */
1122
1123         state->response.data.num_entries = num_gids;
1124         state->response.extra_data = gid_list;
1125         state->response.length += num_gids * sizeof(gid_t);
1126
1127         result = WINBINDD_OK;
1128
1129  done:
1130
1131         talloc_destroy(mem_ctx);
1132
1133         return result;
1134 }
1135
1136
1137 /* Get user supplementary sids. This is equivalent to the
1138    winbindd_getgroups() function but it involves a SID->SIDs mapping
1139    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1140    idmap. This call is designed to be used with applications that need
1141    to do ACL evaluation themselves. Note that the cached info3 data is
1142    not used 
1143
1144    this function assumes that the SID that comes in is a user SID. If
1145    you pass in another type of SID then you may get unpredictable
1146    results.
1147 */
1148 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1149 {
1150         DOM_SID user_sid;
1151         NTSTATUS status;
1152         DOM_SID **user_grpsids;
1153         struct winbindd_domain *domain;
1154         enum winbindd_result result = WINBINDD_ERROR;
1155         unsigned int i;
1156         TALLOC_CTX *mem_ctx;
1157         char *ret = NULL;
1158         uint32 num_groups;
1159         unsigned ofs, ret_size = 0;
1160
1161         /* Ensure null termination */
1162         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1163
1164         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1165                 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1166                 return WINBINDD_ERROR;
1167         }
1168
1169         if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1170                                     state->request.data.username))) {
1171                 return WINBINDD_ERROR;
1172         }
1173
1174         /* Get info for the domain */   
1175         if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1176                 DEBUG(0,("could not find domain entry for sid %s\n", 
1177                           sid_string_static(&user_sid)));
1178                 goto done;
1179         }
1180         
1181         status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1182                                                     &user_sid, &num_groups, 
1183                                                     &user_grpsids);
1184         if (!NT_STATUS_IS_OK(status)) 
1185                 goto done;
1186
1187         if (num_groups == 0) {
1188                 goto no_groups;
1189         }
1190
1191         /* work out the response size */
1192         for (i = 0; i < num_groups; i++) {
1193                 const char *s = sid_string_static(user_grpsids[i]);
1194                 ret_size += strlen(s) + 1;
1195         }
1196
1197         /* build the reply */
1198         ret = malloc(ret_size);
1199         if (!ret) goto done;
1200         ofs = 0;
1201         for (i = 0; i < num_groups; i++) {
1202                 const char *s = sid_string_static(user_grpsids[i]);
1203                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1204                 ofs += strlen(ret+ofs) + 1;
1205         }
1206
1207 no_groups:
1208         /* Send data back to client */
1209         state->response.data.num_entries = num_groups;
1210         state->response.extra_data = ret;
1211         state->response.length += ret_size;
1212         result = WINBINDD_OK;
1213
1214  done:
1215         talloc_destroy(mem_ctx);
1216
1217         return result;
1218 }
1219