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