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