Put back the changes that Simo reverted and fix a speling mistak.
[tprouty/samba.git] / source / 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 "winbindd.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 /*********************************************************************
31 *********************************************************************/
32
33 static int gr_mem_buffer( char **buffer, char **members, int num_members )
34 {
35         int i;
36         int len = 0;
37         int idx = 0;
38
39         if ( num_members == 0 ) {
40                 *buffer = NULL;
41                 return 0;
42         }
43         
44         for ( i=0; i<num_members; i++ )
45                 len += strlen(members[i])+1;
46
47         *buffer = (char*)smb_xmalloc(len);
48         for ( i=0; i<num_members; i++ ) {
49                 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
50                 idx += strlen(members[i])+1;
51         }
52         /* terminate with NULL */
53         (*buffer)[len-1] = '\0';
54         
55         return len;     
56 }
57
58 /***************************************************************
59  Empty static struct for negative caching.
60 ****************************************************************/
61
62 /* Fill a grent structure from various other information */
63
64 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name, 
65                        const char *gr_name, gid_t unix_gid)
66 {
67         fstring full_group_name;
68         /* Fill in uid/gid */
69         fill_domain_username(full_group_name, dom_name, gr_name);
70
71         gr->gr_gid = unix_gid;
72     
73         /* Group name and password */
74     
75         safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
76         safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
77
78         return True;
79 }
80
81 /* Fill in the group membership field of a NT group given by group_sid */
82
83 static BOOL fill_grent_mem(struct winbindd_domain *domain,
84                            DOM_SID *group_sid, 
85                            enum SID_NAME_USE group_name_type, 
86                            int *num_gr_mem, char **gr_mem, int *gr_mem_len)
87 {
88         DOM_SID **sid_mem = NULL;
89         uint32 num_names = 0;
90         uint32 *name_types = NULL;
91         unsigned int buf_len, buf_ndx, i;
92         char **names = NULL, *buf;
93         BOOL result = False;
94         TALLOC_CTX *mem_ctx;
95         NTSTATUS status;
96         fstring sid_string;
97
98         if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
99                 return False;
100
101         /* Initialise group membership information */
102         
103         DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
104
105         *num_gr_mem = 0;
106         
107         if ( !((group_name_type==SID_NAME_DOM_GRP) ||
108                 ((group_name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
109         {
110                 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", 
111                           sid_to_string(sid_string, group_sid), domain->name, 
112                           group_name_type));
113                 goto done;
114         }
115
116         /* Lookup group members */
117         status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names, 
118                                                   &sid_mem, &names, &name_types);
119         if (!NT_STATUS_IS_OK(status)) {
120                 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n", 
121                           sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
122
123                 goto done;
124         }
125
126         DEBUG(10, ("looked up %d names\n", num_names));
127
128         if (DEBUGLEVEL >= 10) {
129                 for (i = 0; i < num_names; i++)
130                         DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
131                                    name_types[i]));
132         }
133
134         /* Add members to list */
135
136         buf = NULL;
137         buf_len = buf_ndx = 0;
138
139  again:
140
141         for (i = 0; i < num_names; i++) {
142                 char *the_name;
143                 fstring name;
144                 int len;
145                         
146                 the_name = names[i];
147
148                 DEBUG(10, ("processing name %s\n", the_name));
149
150                 /* FIXME: need to cope with groups within groups.  These
151                    occur in Universal groups on a Windows 2000 native mode
152                    server. */
153
154                 if (name_types[i] != SID_NAME_USER) {
155                         DEBUG(3, ("name %s isn't a domain user\n", the_name));
156                         continue;
157                 }
158
159                 /* Don't bother with machine accounts */
160                 
161                 if (the_name[strlen(the_name) - 1] == '$') {
162                         DEBUG(10, ("%s is machine account\n", the_name));
163                         continue;
164                 }
165
166                 /* Append domain name */
167
168                 fill_domain_username(name, domain->name, the_name);
169
170                 len = strlen(name);
171                 
172                 /* Add to list or calculate buffer length */
173
174                 if (!buf) {
175                         buf_len += len + 1; /* List is comma separated */
176                         (*num_gr_mem)++;
177                         DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
178                 } else {
179                         DEBUG(10, ("appending %s at ndx %d\n", name, len));
180                         safe_strcpy(&buf[buf_ndx], name, len);
181                         buf_ndx += len;
182                         buf[buf_ndx] = ',';
183                         buf_ndx++;
184                 }
185         }
186
187         /* Allocate buffer */
188
189         if (!buf && buf_len != 0) {
190                 if (!(buf = malloc(buf_len))) {
191                         DEBUG(1, ("out of memory\n"));
192                         result = False;
193                         goto done;
194                 }
195                 memset(buf, 0, buf_len);
196                 goto again;
197         }
198
199         if (buf && buf_ndx > 0) {
200                 buf[buf_ndx - 1] = '\0';
201         }
202
203         *gr_mem = buf;
204         *gr_mem_len = buf_len;
205
206         DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem, 
207                    buf_len, *num_gr_mem ? buf : "NULL")); 
208         result = True;
209
210 done:
211
212         talloc_destroy(mem_ctx);
213         
214         DEBUG(10, ("fill_grent_mem returning %d\n", result));
215
216         return result;
217 }
218
219 /* Return a group structure from a group name */
220
221 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
222 {
223         DOM_SID group_sid;
224         WINBINDD_GR *grp;
225         struct winbindd_domain *domain;
226         enum SID_NAME_USE name_type;
227         fstring name_domain, name_group;
228         char *tmp, *gr_mem;
229         int gr_mem_len;
230         gid_t gid;
231         
232         /* Ensure null termination */
233         state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
234
235         DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
236                   state->request.data.groupname));
237
238         /* Parse domain and groupname */
239         
240         memset(name_group, 0, sizeof(fstring));
241
242         tmp = state->request.data.groupname;
243         
244         parse_domain_user(tmp, name_domain, name_group);
245
246         /* if no domain or our local domain, then do a local tdb search */
247         
248         if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
249                 char *buffer = NULL;
250                 
251                 if ( !(grp=wb_getgrnam(name_group)) ) {
252                         DEBUG(5,("winbindd_getgrnam: lookup for %s\\%s failed\n",
253                                 name_domain, name_group));
254                         return WINBINDD_ERROR;
255                 }
256                 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
257
258                 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
259                 
260                 state->response.data.gr.gr_mem_ofs = 0;
261                 state->response.length += gr_mem_len;
262                 state->response.extra_data = buffer;    /* give the memory away */
263                 
264                 return WINBINDD_OK;
265         }
266
267         /* should we deal with users for our domain? */
268         
269         if ( lp_winbind_trusted_domains_only() && strequal(name_domain, lp_workgroup())) {
270                 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n", 
271                         name_domain, name_group));
272                 return WINBINDD_ERROR;
273         }       
274
275         
276         /* Get info for the domain */
277
278         if ((domain = find_domain_from_name(name_domain)) == NULL) {
279                 DEBUG(0, ("could not get domain sid for domain %s\n",
280                           name_domain));
281                 return WINBINDD_ERROR;
282         }
283
284         /* Get rid and name type from name */
285         
286         if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid, 
287                                          &name_type)) {
288                 DEBUG(1, ("group %s in domain %s does not exist\n", 
289                           name_group, name_domain));
290                 return WINBINDD_ERROR;
291         }
292
293         if ( !((name_type==SID_NAME_DOM_GRP) ||
294                 ((name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
295         {
296                 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
297                           name_group, name_type));
298                 return WINBINDD_ERROR;
299         }
300
301         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
302                 DEBUG(1, ("error converting unix gid to sid\n"));
303                 return WINBINDD_ERROR;
304         }
305
306         if (!fill_grent(&state->response.data.gr, name_domain,
307                         name_group, gid) ||
308             !fill_grent_mem(domain, &group_sid, name_type,
309                             &state->response.data.gr.num_gr_mem,
310                             &gr_mem, &gr_mem_len)) {
311                 return WINBINDD_ERROR;
312         }
313
314         /* Group membership lives at start of extra data */
315
316         state->response.data.gr.gr_mem_ofs = 0;
317
318         state->response.length += gr_mem_len;
319         state->response.extra_data = gr_mem;
320
321         return WINBINDD_OK;
322 }
323
324 /* Return a group structure from a gid number */
325
326 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
327 {
328         struct winbindd_domain *domain;
329         WINBINDD_GR *grp;
330         DOM_SID group_sid;
331         enum SID_NAME_USE name_type;
332         fstring dom_name;
333         fstring group_name;
334         int gr_mem_len;
335         char *gr_mem;
336
337         DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, 
338                   (unsigned long)state->request.data.gid));
339
340         /* Bug out if the gid isn't in the winbind range */
341
342         if ((state->request.data.gid < server_state.gid_low) ||
343             (state->request.data.gid > server_state.gid_high))
344                 return WINBINDD_ERROR;
345
346         /* alway try local tdb lookup first */
347         if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
348                 char *buffer = NULL;
349                 
350                 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
351                 
352                 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
353                 
354                 state->response.data.gr.gr_mem_ofs = 0;
355                 state->response.length += gr_mem_len;
356                 state->response.extra_data = buffer;    /* give away the memory */
357                 
358                 return WINBINDD_OK;
359         }
360
361         /* Get rid from gid */
362         if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
363                 DEBUG(1, ("could not convert gid %lu to rid\n", 
364                           (unsigned long)state->request.data.gid));
365                 return WINBINDD_ERROR;
366         }
367
368         /* Get name from sid */
369
370         if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
371                 DEBUG(1, ("could not lookup sid\n"));
372                 return WINBINDD_ERROR;
373         }
374
375         /* Fill in group structure */
376
377         domain = find_domain_from_sid(&group_sid);
378
379         if (!domain) {
380                 DEBUG(1,("Can't find domain from sid\n"));
381                 return WINBINDD_ERROR;
382         }
383
384         if ( !((name_type==SID_NAME_DOM_GRP) ||
385                 ((name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
386         {
387                 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
388                           group_name, name_type));
389                 return WINBINDD_ERROR;
390         }
391
392         if (!fill_grent(&state->response.data.gr, dom_name, group_name, 
393                         state->request.data.gid) ||
394             !fill_grent_mem(domain, &group_sid, name_type,
395                             &state->response.data.gr.num_gr_mem,
396                             &gr_mem, &gr_mem_len))
397                 return WINBINDD_ERROR;
398
399         /* Group membership lives at start of extra data */
400
401         state->response.data.gr.gr_mem_ofs = 0;
402
403         state->response.length += gr_mem_len;
404         state->response.extra_data = gr_mem;
405
406         return WINBINDD_OK;
407 }
408
409 /*
410  * set/get/endgrent functions
411  */
412
413 /* "Rewind" file pointer for group database enumeration */
414
415 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
416 {
417         struct winbindd_domain *domain;
418
419         DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
420
421         /* Check user has enabled this */
422
423         if (!lp_winbind_enum_groups())
424                 return WINBINDD_ERROR;
425
426         /* Free old static data if it exists */
427         
428         if (state->getgrent_state != NULL) {
429                 free_getent_state(state->getgrent_state);
430                 state->getgrent_state = NULL;
431         }
432         
433         /* Create sam pipes for each domain we know about */
434         
435         for (domain = domain_list(); domain != NULL; domain = domain->next) {
436                 struct getent_state *domain_state;
437                 
438                 
439                 /* don't add our domaina if we are a PDC or if we 
440                    are a member of a Samba domain */
441                 
442                 if ( (IS_DC || lp_winbind_trusted_domains_only())
443                         && strequal(domain->name, lp_workgroup()) )
444                 {
445                         continue;
446                 }
447                                                 
448                 /* Create a state record for this domain */
449                 
450                 if ((domain_state = (struct getent_state *)
451                      malloc(sizeof(struct getent_state))) == NULL) {
452                         DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
453                         return WINBINDD_ERROR;
454                 }
455                 
456                 ZERO_STRUCTP(domain_state);
457                 
458                 fstrcpy(domain_state->domain_name, domain->name);
459
460                 /* Add to list of open domains */
461                 
462                 DLIST_ADD(state->getgrent_state, domain_state);
463         }
464         
465         return WINBINDD_OK;
466 }
467
468 /* Close file pointer to ntdom group database */
469
470 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
471 {
472         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
473
474         free_getent_state(state->getgrent_state);
475         state->getgrent_state = NULL;
476         
477         return WINBINDD_OK;
478 }
479
480 /* Get the list of domain groups and domain aliases for a domain.  We fill in
481    the sam_entries and num_sam_entries fields with domain group information.  
482    The dispinfo_ndx field is incremented to the index of the next group to 
483    fetch. Return True if some groups were returned, False otherwise. */
484
485 #define MAX_FETCH_SAM_ENTRIES 100
486
487 static BOOL get_sam_group_entries(struct getent_state *ent)
488 {
489         NTSTATUS status;
490         uint32 num_entries;
491         struct acct_info *name_list = NULL, *tmp_name_list = NULL;
492         TALLOC_CTX *mem_ctx;
493         BOOL result = False;
494         struct acct_info *sam_grp_entries = NULL;
495         struct winbindd_domain *domain;
496         
497         if (ent->got_sam_entries)
498                 return False;
499
500         if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
501                                           ent->domain_name))) {
502                 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); 
503                 return False;
504         }
505                 
506         /* Free any existing group info */
507
508         SAFE_FREE(ent->sam_entries);
509         ent->num_sam_entries = 0;
510         ent->got_sam_entries = True;
511
512         /* Enumerate domain groups */
513
514         num_entries = 0;
515
516         if (!(domain = find_domain_from_name(ent->domain_name))) {
517                 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
518                 goto done;
519         }
520
521         /* always get the domain global groups */
522
523         status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
524         
525         if (!NT_STATUS_IS_OK(status)) {
526                 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
527                 result = False;
528                 goto done;
529         }
530
531         /* Copy entries into return buffer */
532
533         if (num_entries) {
534                 if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
535                         DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", 
536                                 num_entries));
537                         result = False;
538                         goto done;
539                 }
540                 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
541         }
542         
543         ent->num_sam_entries = num_entries;
544         
545         /* get the domain local groups if we are a member of a native win2k domain
546            and are not using LDAP to get the groups */
547            
548         if ( lp_security() != SEC_ADS && domain->native_mode 
549                 && strequal(lp_workgroup(), domain->name) )
550         {
551                 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
552                 
553                 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
554                 
555                 if ( !NT_STATUS_IS_OK(status) ) { 
556                         DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
557                         num_entries = 0;
558                 }
559                 else
560                         DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
561                 
562                 /* Copy entries into return buffer */
563
564                 if ( num_entries ) {
565                         if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
566                         {
567                                 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", 
568                                         num_entries));
569                                 result = False;
570                                 SAFE_FREE( name_list );
571                                 goto done;
572                         }
573                         
574                         name_list = tmp_name_list;
575                                 
576                         memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, 
577                                 num_entries * sizeof(struct acct_info) );
578                 }
579         
580                 ent->num_sam_entries += num_entries;
581         }
582         
583                 
584         /* Fill in remaining fields */
585
586         ent->sam_entries = name_list;
587         ent->sam_entry_index = 0;
588
589         result = (ent->num_sam_entries > 0);
590
591  done:
592         talloc_destroy(mem_ctx);
593
594         return result;
595 }
596
597 /* Fetch next group entry from ntdom database */
598
599 #define MAX_GETGRENT_GROUPS 500
600
601 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
602 {
603         struct getent_state *ent;
604         struct winbindd_gr *group_list = NULL;
605         int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
606         char *new_extra_data, *gr_mem_list = NULL;
607
608         DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
609
610         /* Check user has enabled this */
611
612         if (!lp_winbind_enum_groups())
613                 return WINBINDD_ERROR;
614
615         num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
616
617         if ((state->response.extra_data = 
618              malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
619                 return WINBINDD_ERROR;
620
621         state->response.data.num_entries = 0;
622
623         group_list = (struct winbindd_gr *)state->response.extra_data;
624
625         if (!(ent = state->getgrent_state))
626                 return WINBINDD_ERROR;
627
628         /* Start sending back groups */
629
630         for (i = 0; i < num_groups; i++) {
631                 struct acct_info *name_list = NULL;
632                 fstring domain_group_name;
633                 uint32 result;
634                 gid_t group_gid;
635                 int gr_mem_len;
636                 char *gr_mem, *new_gr_mem_list;
637                 DOM_SID group_sid;
638                 struct winbindd_domain *domain;
639                                 
640                 /* Do we need to fetch another chunk of groups? */
641
642         tryagain:
643
644                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
645                            ent->sam_entry_index, ent->num_sam_entries));
646
647                 if (ent->num_sam_entries == ent->sam_entry_index) {
648
649                         while(ent && !get_sam_group_entries(ent)) {
650                                 struct getent_state *next_ent;
651
652                                 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 
653
654                                 /* Free state information for this domain */
655
656                                 SAFE_FREE(ent->sam_entries);
657
658                                 next_ent = ent->next;
659                                 DLIST_REMOVE(state->getgrent_state, ent);
660                                 
661                                 SAFE_FREE(ent);
662                                 ent = next_ent;
663                         }
664
665                         /* No more domains */
666
667                         if (!ent) 
668                                 break;
669                 }
670                 
671                 name_list = ent->sam_entries;
672                 
673                 if (!(domain = 
674                       find_domain_from_name(ent->domain_name))) {
675                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
676                         result = False;
677                         goto done;
678                 }
679
680                 /* Lookup group info */
681                 
682                 sid_copy(&group_sid, &domain->sid);
683                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
684
685                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
686                         
687                         DEBUG(1, ("could not look up gid for group %s\n", 
688                                   name_list[ent->sam_entry_index].acct_name));
689                         
690                         ent->sam_entry_index++;
691                         goto tryagain;
692                 }
693
694                 DEBUG(10, ("got gid %lu for group %x\n", (unsigned long)group_gid,
695                            name_list[ent->sam_entry_index].rid));
696                 
697                 /* Fill in group entry */
698
699                 fill_domain_username(domain_group_name, ent->domain_name, 
700                          name_list[ent->sam_entry_index].acct_name);
701
702                 result = fill_grent(&group_list[group_list_ndx], 
703                                     ent->domain_name,
704                                     name_list[ent->sam_entry_index].acct_name,
705                                     group_gid);
706
707                 /* Fill in group membership entry */
708
709                 if (result) {
710                         DOM_SID member_sid;
711                         group_list[group_list_ndx].num_gr_mem = 0;
712                         gr_mem = NULL;
713                         gr_mem_len = 0;
714                         
715                         /* Get group membership */                      
716                         if (state->request.cmd == WINBINDD_GETGRLST) {
717                                 result = True;
718                         } else {
719                                 sid_copy(&member_sid, &domain->sid);
720                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
721                                 result = fill_grent_mem(
722                                         domain,
723                                         &member_sid,
724                                         SID_NAME_DOM_GRP,
725                                         &group_list[group_list_ndx].num_gr_mem, 
726                                         &gr_mem, &gr_mem_len);
727                         }
728                 }
729
730                 if (result) {
731                         /* Append to group membership list */
732                         new_gr_mem_list = Realloc(
733                                 gr_mem_list,
734                                 gr_mem_list_len + gr_mem_len);
735
736                         if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
737                                 DEBUG(0, ("out of memory\n"));
738                                 SAFE_FREE(gr_mem_list);
739                                 gr_mem_list_len = 0;
740                                 break;
741                         }
742
743                         DEBUG(10, ("list_len = %d, mem_len = %d\n",
744                                    gr_mem_list_len, gr_mem_len));
745
746                         gr_mem_list = new_gr_mem_list;
747
748                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
749                                gr_mem_len);
750
751                         SAFE_FREE(gr_mem);
752
753                         group_list[group_list_ndx].gr_mem_ofs = 
754                                 gr_mem_list_len;
755
756                         gr_mem_list_len += gr_mem_len;
757                 }
758
759                 ent->sam_entry_index++;
760                 
761                 /* Add group to return list */
762                 
763                 if (result) {
764
765                         DEBUG(10, ("adding group num_entries = %d\n",
766                                    state->response.data.num_entries));
767
768                         group_list_ndx++;
769                         state->response.data.num_entries++;
770                         
771                         state->response.length +=
772                                 sizeof(struct winbindd_gr);
773                         
774                 } else {
775                         DEBUG(0, ("could not lookup domain group %s\n", 
776                                   domain_group_name));
777                 }
778         }
779
780         /* Copy the list of group memberships to the end of the extra data */
781
782         if (group_list_ndx == 0)
783                 goto done;
784
785         new_extra_data = Realloc(
786                 state->response.extra_data,
787                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
788
789         if (!new_extra_data) {
790                 DEBUG(0, ("out of memory\n"));
791                 group_list_ndx = 0;
792                 SAFE_FREE(state->response.extra_data);
793                 SAFE_FREE(gr_mem_list);
794
795                 return WINBINDD_ERROR;
796         }
797
798         state->response.extra_data = new_extra_data;
799
800         memcpy(&((char *)state->response.extra_data)
801                [group_list_ndx * sizeof(struct winbindd_gr)], 
802                gr_mem_list, gr_mem_list_len);
803
804         SAFE_FREE(gr_mem_list);
805
806         state->response.length += gr_mem_list_len;
807
808         DEBUG(10, ("returning %d groups, length = %d\n",
809                    group_list_ndx, gr_mem_list_len));
810
811         /* Out of domains */
812
813  done:
814
815         return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
816 }
817
818 /* List domain groups without mapping to unix ids */
819
820 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
821 {
822         uint32 total_entries = 0;
823         struct winbindd_domain *domain;
824         const char *which_domain;
825         char *extra_data = NULL;
826         char *ted = NULL;
827         unsigned int extra_data_len = 0, i;
828
829         DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
830
831         /* Ensure null termination */
832         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
833         which_domain = state->request.domain_name;
834         
835         /* Enumerate over trusted domains */
836
837         for (domain = domain_list(); domain; domain = domain->next) {
838                 struct getent_state groups;
839
840                 /* if we have a domain name restricting the request and this
841                    one in the list doesn't match, then just bypass the remainder
842                    of the loop */
843                    
844                 if ( *which_domain && !strequal(which_domain, domain->name) )
845                         continue;
846                         
847                 ZERO_STRUCT(groups);
848
849                 /* Get list of sam groups */
850                 
851                 fstrcpy(groups.domain_name, domain->name);
852
853                 get_sam_group_entries(&groups);
854                         
855                 if (groups.num_sam_entries == 0) {
856                         /* this domain is empty or in an error state */
857                         continue;
858                 }
859
860                 /* keep track the of the total number of groups seen so 
861                    far over all domains */
862                 total_entries += groups.num_sam_entries;
863                 
864                 /* Allocate some memory for extra data.  Note that we limit
865                    account names to sizeof(fstring) = 128 characters.  */               
866                 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
867  
868                 if (!ted) {
869                         DEBUG(0,("failed to enlarge buffer!\n"));
870                         SAFE_FREE(extra_data);
871                         return WINBINDD_ERROR;
872                 } else
873                         extra_data = ted;
874
875                 /* Pack group list into extra data fields */
876                 for (i = 0; i < groups.num_sam_entries; i++) {
877                         char *group_name = ((struct acct_info *)
878                                             groups.sam_entries)[i].acct_name; 
879                         fstring name;
880
881                         fill_domain_username(name, domain->name, group_name);
882                         /* Append to extra data */                      
883                         memcpy(&extra_data[extra_data_len], name, 
884                                strlen(name));
885                         extra_data_len += strlen(name);
886                         extra_data[extra_data_len++] = ',';
887                 }
888
889                 free(groups.sam_entries);
890         }
891
892         /* Assign extra_data fields in response structure */
893         if (extra_data) {
894                 extra_data[extra_data_len - 1] = '\0';
895                 state->response.extra_data = extra_data;
896                 state->response.length += extra_data_len;
897         }
898
899         /* No domains may have responded but that's still OK so don't
900            return an error. */
901
902         return WINBINDD_OK;
903 }
904
905 /* Get user supplementary groups.  This is much quicker than trying to
906    invert the groups database.  We merge the groups from the gids and
907    other_sids info3 fields as trusted domain, universal group
908    memberships, and nested groups (win2k native mode only) are not
909    returned by the getgroups RPC call but are present in the info3. */
910
911 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
912 {
913         fstring name_domain, name_user;
914         DOM_SID user_sid, group_sid;
915         enum SID_NAME_USE name_type;
916         uint32 num_groups = 0;
917         uint32 num_gids = 0;
918         NTSTATUS status;
919         DOM_SID **user_grpsids;
920         struct winbindd_domain *domain;
921         enum winbindd_result result = WINBINDD_ERROR;
922         gid_t *gid_list;
923         unsigned int i;
924         TALLOC_CTX *mem_ctx;
925         NET_USER_INFO_3 *info3 = NULL;
926         
927         /* Ensure null termination */
928         state->request.data.username[sizeof(state->request.data.username)-1]='\0';
929
930         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
931                   state->request.data.username));
932
933         if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
934                                           state->request.data.username)))
935                 return WINBINDD_ERROR;
936
937         /* Parse domain and username */
938
939         parse_domain_user(state->request.data.username, 
940                 name_domain, name_user);
941         
942         /* bail if there is no domain */ 
943         
944         if ( !*name_domain )
945                 goto done;
946
947         /* Get info for the domain */
948         
949         if ((domain = find_domain_from_name(name_domain)) == NULL) {
950                 DEBUG(0, ("could not find domain entry for domain %s\n", 
951                           name_domain));
952                 goto done;
953         }
954
955         /* Get rid and name type from name.  The following costs 1 packet */
956
957         if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid, 
958                                          &name_type)) {
959                 DEBUG(1, ("user '%s' does not exist\n", name_user));
960                 goto done;
961         }
962
963         if (name_type != SID_NAME_USER) {
964                 DEBUG(1, ("name '%s' is not a user name: %d\n", 
965                           name_user, name_type));
966                 goto done;
967         }
968
969         /* Treat the info3 cache as authoritative as the
970            lookup_usergroups() function may return cached data. */
971
972         if ((info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
973
974                 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
975                            info3->num_groups2, info3->num_other_sids));
976
977                 num_groups = info3->num_other_sids + info3->num_groups2;
978                 gid_list = calloc(sizeof(gid_t), num_groups);
979
980                 /* Go through each other sid and convert it to a gid */
981
982                 for (i = 0; i < info3->num_other_sids; i++) {
983                         fstring name;
984                         fstring dom_name;
985                         enum SID_NAME_USE sid_type;
986
987                         /* Is this sid known to us?  It can either be
988                            a trusted domain sid or a foreign sid. */
989
990                         if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid, 
991                                 dom_name, name, &sid_type))
992                         {
993                                 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n", 
994                                            sid_string_static(&info3->other_sids[i].sid)));
995                                 continue;
996                         }
997
998                         /* Check it is a domain group or an alias (domain local group) 
999                            in a win2k native mode domain. */
1000                         
1001                         if ( !((sid_type==SID_NAME_DOM_GRP) ||
1002                                 ((sid_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
1003                         {
1004                                 DEBUG(10, ("winbindd_getgroups: sid type %d "
1005                                            "for %s is not a domain group\n",
1006                                            sid_type,
1007                                            sid_string_static(
1008                                                    &info3->other_sids[i].sid)));
1009                                 continue;
1010                         }
1011
1012                         /* Map to a gid */
1013
1014                         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&info3->other_sids[i].sid, &gid_list[num_gids], 0)) )
1015                         {
1016                                 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1017                                            sid_string_static(&info3->other_sids[i].sid)));
1018                                 continue;
1019                         }
1020
1021                         /* We've jumped through a lot of hoops to get here */
1022
1023                         DEBUG(10, ("winbindd_getgroups: mapped other sid %s to "
1024                                    "gid %lu\n", sid_string_static(
1025                                            &info3->other_sids[i].sid),
1026                                    (unsigned long)gid_list[num_gids]));
1027
1028                         num_gids++;
1029                 }
1030
1031                 for (i = 0; i < info3->num_groups2; i++) {
1032                 
1033                         /* create the group SID */
1034                         
1035                         sid_copy( &group_sid, &domain->sid );
1036                         sid_append_rid( &group_sid, info3->gids[i].g_rid );
1037
1038                         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid_list[num_gids], 0)) ) {
1039                                 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1040                                            sid_string_static(&group_sid)));
1041                         }
1042
1043                         num_gids++;
1044                 }
1045
1046                 SAFE_FREE(info3);
1047
1048         } else {
1049                 status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1050                                                     &user_sid, &num_groups, 
1051                                                     &user_grpsids);
1052                 if (!NT_STATUS_IS_OK(status)) 
1053                         goto done;
1054
1055                 gid_list = malloc(sizeof(gid_t) * num_groups);
1056
1057                 if (state->response.extra_data)
1058                         goto done;
1059
1060                 for (i = 0; i < num_groups; i++) {
1061                         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_grpsids[i], &gid_list[num_gids], 0))) {
1062                                 DEBUG(1, ("unable to convert group sid %s to gid\n", 
1063                                           sid_string_static(user_grpsids[i])));
1064                                 continue;
1065                         }
1066                         num_gids++;
1067                 }
1068         }
1069
1070         /* Send data back to client */
1071
1072         state->response.data.num_entries = num_gids;
1073         state->response.extra_data = gid_list;
1074         state->response.length += num_gids * sizeof(gid_t);
1075
1076         result = WINBINDD_OK;
1077
1078  done:
1079
1080         talloc_destroy(mem_ctx);
1081
1082         return result;
1083 }