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