4b49c1873e2425974512436faf3b1438e4010360
[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 = SMB_XMALLOC_ARRAY(char, 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 = SMB_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 = SMB_MALLOC_P(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         state->getgrent_initialized = True;
475
476         return WINBINDD_OK;
477 }
478
479 /* Close file pointer to ntdom group database */
480
481 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
482 {
483         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
484
485         free_getent_state(state->getgrent_state);
486         state->getgrent_initialized = False;
487         state->getgrent_state = NULL;
488         
489         return WINBINDD_OK;
490 }
491
492 /* Get the list of domain groups and domain aliases for a domain.  We fill in
493    the sam_entries and num_sam_entries fields with domain group information.  
494    The dispinfo_ndx field is incremented to the index of the next group to 
495    fetch. Return True if some groups were returned, False otherwise. */
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 = SMB_MALLOC_ARRAY(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 = SMB_REALLOC_ARRAY( name_list, 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 = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL)
628                 return WINBINDD_ERROR;
629
630         memset(state->response.extra_data, '\0',
631                 num_groups * sizeof(struct winbindd_gr) );
632
633         state->response.data.num_entries = 0;
634
635         group_list = (struct winbindd_gr *)state->response.extra_data;
636
637         if (!state->getgrent_initialized)
638                 winbindd_setgrent(state);
639
640         if (!(ent = state->getgrent_state))
641                 return WINBINDD_ERROR;
642
643         /* Start sending back groups */
644
645         for (i = 0; i < num_groups; i++) {
646                 struct acct_info *name_list = NULL;
647                 fstring domain_group_name;
648                 uint32 result;
649                 gid_t group_gid;
650                 int gr_mem_len;
651                 char *gr_mem, *new_gr_mem_list;
652                 DOM_SID group_sid;
653                 struct winbindd_domain *domain;
654                                 
655                 /* Do we need to fetch another chunk of groups? */
656
657         tryagain:
658
659                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
660                            ent->sam_entry_index, ent->num_sam_entries));
661
662                 if (ent->num_sam_entries == ent->sam_entry_index) {
663
664                         while(ent && !get_sam_group_entries(ent)) {
665                                 struct getent_state *next_ent;
666
667                                 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 
668
669                                 /* Free state information for this domain */
670
671                                 SAFE_FREE(ent->sam_entries);
672
673                                 next_ent = ent->next;
674                                 DLIST_REMOVE(state->getgrent_state, ent);
675                                 
676                                 SAFE_FREE(ent);
677                                 ent = next_ent;
678                         }
679
680                         /* No more domains */
681
682                         if (!ent) 
683                                 break;
684                 }
685                 
686                 name_list = ent->sam_entries;
687                 
688                 if (!(domain = 
689                       find_domain_from_name(ent->domain_name))) {
690                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
691                         result = False;
692                         goto done;
693                 }
694
695                 /* Lookup group info */
696                 
697                 sid_copy(&group_sid, &domain->sid);
698                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
699
700                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
701                         
702                         DEBUG(1, ("could not look up gid for group %s\n", 
703                                   name_list[ent->sam_entry_index].acct_name));
704                         
705                         ent->sam_entry_index++;
706                         goto tryagain;
707                 }
708
709                 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
710                            (unsigned long)name_list[ent->sam_entry_index].rid));
711                 
712                 /* Fill in group entry */
713
714                 fill_domain_username(domain_group_name, ent->domain_name, 
715                          name_list[ent->sam_entry_index].acct_name);
716
717                 result = fill_grent(&group_list[group_list_ndx], 
718                                     ent->domain_name,
719                                     name_list[ent->sam_entry_index].acct_name,
720                                     group_gid);
721
722                 /* Fill in group membership entry */
723
724                 if (result) {
725                         DOM_SID member_sid;
726                         group_list[group_list_ndx].num_gr_mem = 0;
727                         gr_mem = NULL;
728                         gr_mem_len = 0;
729                         
730                         /* Get group membership */                      
731                         if (state->request.cmd == WINBINDD_GETGRLST) {
732                                 result = True;
733                         } else {
734                                 sid_copy(&member_sid, &domain->sid);
735                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
736                                 result = fill_grent_mem(
737                                         domain,
738                                         &member_sid,
739                                         SID_NAME_DOM_GRP,
740                                         &group_list[group_list_ndx].num_gr_mem, 
741                                         &gr_mem, &gr_mem_len);
742                         }
743                 }
744
745                 if (result) {
746                         /* Append to group membership list */
747                         new_gr_mem_list = SMB_REALLOC( gr_mem_list, gr_mem_list_len + gr_mem_len);
748
749                         if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
750                                 DEBUG(0, ("out of memory\n"));
751                                 SAFE_FREE(gr_mem_list);
752                                 gr_mem_list_len = 0;
753                                 break;
754                         }
755
756                         DEBUG(10, ("list_len = %d, mem_len = %d\n",
757                                    gr_mem_list_len, gr_mem_len));
758
759                         gr_mem_list = new_gr_mem_list;
760
761                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
762                                gr_mem_len);
763
764                         SAFE_FREE(gr_mem);
765
766                         group_list[group_list_ndx].gr_mem_ofs = 
767                                 gr_mem_list_len;
768
769                         gr_mem_list_len += gr_mem_len;
770                 }
771
772                 ent->sam_entry_index++;
773                 
774                 /* Add group to return list */
775                 
776                 if (result) {
777
778                         DEBUG(10, ("adding group num_entries = %d\n",
779                                    state->response.data.num_entries));
780
781                         group_list_ndx++;
782                         state->response.data.num_entries++;
783                         
784                         state->response.length +=
785                                 sizeof(struct winbindd_gr);
786                         
787                 } else {
788                         DEBUG(0, ("could not lookup domain group %s\n", 
789                                   domain_group_name));
790                 }
791         }
792
793         /* Copy the list of group memberships to the end of the extra data */
794
795         if (group_list_ndx == 0)
796                 goto done;
797
798         new_extra_data = SMB_REALLOC(
799                 state->response.extra_data,
800                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
801
802         if (!new_extra_data) {
803                 DEBUG(0, ("out of memory\n"));
804                 group_list_ndx = 0;
805                 SAFE_FREE(state->response.extra_data);
806                 SAFE_FREE(gr_mem_list);
807
808                 return WINBINDD_ERROR;
809         }
810
811         state->response.extra_data = new_extra_data;
812
813         memcpy(&((char *)state->response.extra_data)
814                [group_list_ndx * sizeof(struct winbindd_gr)], 
815                gr_mem_list, gr_mem_list_len);
816
817         SAFE_FREE(gr_mem_list);
818
819         state->response.length += gr_mem_list_len;
820
821         DEBUG(10, ("returning %d groups, length = %d\n",
822                    group_list_ndx, gr_mem_list_len));
823
824         /* Out of domains */
825
826  done:
827
828         return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
829 }
830
831 /* List domain groups without mapping to unix ids */
832
833 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
834 {
835         uint32 total_entries = 0;
836         struct winbindd_domain *domain;
837         const char *which_domain;
838         char *extra_data = NULL;
839         char *ted = NULL;
840         unsigned int extra_data_len = 0, i;
841
842         DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
843
844         /* Ensure null termination */
845         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
846         which_domain = state->request.domain_name;
847         
848         /* Enumerate over trusted domains */
849
850         for (domain = domain_list(); domain; domain = domain->next) {
851                 struct getent_state groups;
852
853                 /* if we have a domain name restricting the request and this
854                    one in the list doesn't match, then just bypass the remainder
855                    of the loop */
856                    
857                 if ( *which_domain && !strequal(which_domain, domain->name) )
858                         continue;
859                         
860                 ZERO_STRUCT(groups);
861
862                 /* Get list of sam groups */
863                 
864                 fstrcpy(groups.domain_name, domain->name);
865
866                 get_sam_group_entries(&groups);
867                         
868                 if (groups.num_sam_entries == 0) {
869                         /* this domain is empty or in an error state */
870                         continue;
871                 }
872
873                 /* keep track the of the total number of groups seen so 
874                    far over all domains */
875                 total_entries += groups.num_sam_entries;
876                 
877                 /* Allocate some memory for extra data.  Note that we limit
878                    account names to sizeof(fstring) = 128 characters.  */               
879                 ted = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
880  
881                 if (!ted) {
882                         DEBUG(0,("failed to enlarge buffer!\n"));
883                         SAFE_FREE(extra_data);
884                         return WINBINDD_ERROR;
885                 } else
886                         extra_data = ted;
887
888                 /* Pack group list into extra data fields */
889                 for (i = 0; i < groups.num_sam_entries; i++) {
890                         char *group_name = ((struct acct_info *)
891                                             groups.sam_entries)[i].acct_name; 
892                         fstring name;
893
894                         fill_domain_username(name, domain->name, group_name);
895                         /* Append to extra data */                      
896                         memcpy(&extra_data[extra_data_len], name, 
897                                strlen(name));
898                         extra_data_len += strlen(name);
899                         extra_data[extra_data_len++] = ',';
900                 }
901
902                 SAFE_FREE(groups.sam_entries);
903         }
904
905         /* Assign extra_data fields in response structure */
906         if (extra_data) {
907                 extra_data[extra_data_len - 1] = '\0';
908                 state->response.extra_data = extra_data;
909                 state->response.length += extra_data_len;
910         }
911
912         /* No domains may have responded but that's still OK so don't
913            return an error. */
914
915         return WINBINDD_OK;
916 }
917
918 static void add_local_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
919 {
920         gid_t gid;
921         DOM_SID *aliases;
922         int j, num_aliases;
923
924         DEBUG(10, ("Adding local gids from SID: %s\n",
925                    sid_string_static(sid)));
926
927         /* Don't expand aliases if not explicitly activated -- for now
928            -- jerry */
929
930         if (!lp_winbind_nested_groups())
931                 return;
932
933         /* Add nested group memberships */
934
935         if (!pdb_enum_alias_memberships(sid, 1, &aliases, &num_aliases))
936                 return;
937
938         for (j=0; j<num_aliases; j++) {
939                 enum SID_NAME_USE type;
940
941                 if (!local_sid_to_gid(&gid, &aliases[j], &type)) {
942                         DEBUG(1, ("Got an alias membership with no alias\n"));
943                         continue;
944                 }
945
946                 if ((type != SID_NAME_ALIAS) && (type != SID_NAME_WKN_GRP)) {
947                         DEBUG(1, ("Got an alias membership in a non-alias\n"));
948                         continue;
949                 }
950
951                 add_gid_to_array_unique(gid, gids, num);
952         }
953         SAFE_FREE(aliases);
954 }
955
956 static void add_gids_from_user_sid(DOM_SID *sid, gid_t **gids, int *num)
957 {
958         DEBUG(10, ("Adding gids from user SID: %s\n",
959                    sid_string_static(sid)));
960
961         add_local_gids_from_sid(sid, gids, num);
962 }
963
964 static void add_gids_from_group_sid(DOM_SID *sid, gid_t **gids, int *num)
965 {
966         gid_t gid;
967
968         DEBUG(10, ("Adding gids from group SID: %s\n",
969                    sid_string_static(sid)));
970
971         if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
972                 add_gid_to_array_unique(gid, gids, num);
973
974         add_local_gids_from_sid(sid, gids, num);
975 }
976
977 /* Get user supplementary groups.  This is much quicker than trying to
978    invert the groups database.  We merge the groups from the gids and
979    other_sids info3 fields as trusted domain, universal group
980    memberships, and nested groups (win2k native mode only) are not
981    returned by the getgroups RPC call but are present in the info3. */
982
983 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
984 {
985         fstring name_domain, name_user;
986         DOM_SID user_sid, group_sid;
987         enum SID_NAME_USE name_type;
988         uint32 num_groups = 0;
989         uint32 num_gids = 0;
990         NTSTATUS status;
991         DOM_SID **user_grpsids;
992         struct winbindd_domain *domain;
993         enum winbindd_result result = WINBINDD_ERROR;
994         gid_t *gid_list = NULL;
995         unsigned int i;
996         TALLOC_CTX *mem_ctx;
997         NET_USER_INFO_3 *info3 = NULL;
998         
999         /* Ensure null termination */
1000         state->request.data.username[sizeof(state->request.data.username)-1]='\0';
1001
1002         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1003                   state->request.data.username));
1004
1005         if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
1006                                           state->request.data.username)))
1007                 return WINBINDD_ERROR;
1008
1009         /* Parse domain and username */
1010
1011         parse_domain_user(state->request.data.username, 
1012                           name_domain, name_user);
1013         
1014         /* Get info for the domain */
1015         
1016         if ((domain = find_domain_from_name(name_domain)) == NULL) {
1017                 DEBUG(7, ("could not find domain entry for domain %s\n", 
1018                           name_domain));
1019                 goto done;
1020         }
1021
1022         if ( domain->primary && lp_winbind_trusted_domains_only()) {
1023                 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n", 
1024                         name_domain, name_user));
1025                 return WINBINDD_ERROR;
1026         }       
1027         
1028         /* Get rid and name type from name.  The following costs 1 packet */
1029
1030         if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid, 
1031                                          &name_type)) {
1032                 DEBUG(1, ("user '%s' does not exist\n", name_user));
1033                 goto done;
1034         }
1035
1036         if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1037                 DEBUG(1, ("name '%s' is not a user name: %d\n", 
1038                           name_user, name_type));
1039                 goto done;
1040         }
1041
1042         add_gids_from_user_sid(&user_sid, &gid_list, &num_gids);
1043
1044         /* Treat the info3 cache as authoritative as the
1045            lookup_usergroups() function may return cached data. */
1046
1047         if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1048
1049                 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1050                            info3->num_groups2, info3->num_other_sids));
1051
1052                 num_groups = info3->num_other_sids + info3->num_groups2;
1053
1054                 /* Go through each other sid and convert it to a gid */
1055
1056                 for (i = 0; i < info3->num_other_sids; i++) {
1057                         fstring name;
1058                         fstring dom_name;
1059                         enum SID_NAME_USE sid_type;
1060
1061                         /* Is this sid known to us?  It can either be
1062                            a trusted domain sid or a foreign sid. */
1063
1064                         if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid, 
1065                                 dom_name, name, &sid_type))
1066                         {
1067                                 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n", 
1068                                            sid_string_static(&info3->other_sids[i].sid)));
1069                                 continue;
1070                         }
1071
1072                         /* Check it is a domain group or an alias (domain local group) 
1073                            in a win2k native mode domain. */
1074                         
1075                         if ( !((sid_type==SID_NAME_DOM_GRP) ||
1076                                 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1077                         {
1078                                 DEBUG(10, ("winbindd_getgroups: sid type %d "
1079                                            "for %s is not a domain group\n",
1080                                            sid_type,
1081                                            sid_string_static(
1082                                                    &info3->other_sids[i].sid)));
1083                                 continue;
1084                         }
1085
1086                         add_gids_from_group_sid(&info3->other_sids[i].sid,
1087                                                 &gid_list, &num_gids);
1088                 }
1089
1090                 for (i = 0; i < info3->num_groups2; i++) {
1091                 
1092                         /* create the group SID */
1093                         
1094                         sid_copy( &group_sid, &domain->sid );
1095                         sid_append_rid( &group_sid, info3->gids[i].g_rid );
1096
1097                         add_gids_from_group_sid(&group_sid, &gid_list,
1098                                                 &num_gids);
1099                 }
1100
1101                 SAFE_FREE(info3);
1102
1103         } else {
1104                 status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1105                                                     &user_sid, &num_groups, 
1106                                                     &user_grpsids);
1107                 if (!NT_STATUS_IS_OK(status)) 
1108                         goto done;
1109
1110                 if (state->response.extra_data)
1111                         goto done;
1112
1113                 for (i = 0; i < num_groups; i++) {
1114                         add_gids_from_group_sid(user_grpsids[i],
1115                                                 &gid_list, &num_gids);
1116                 }
1117         }
1118
1119         /* We want at least one group... */
1120         if (gid_list == NULL)
1121                 goto done;
1122
1123         remove_duplicate_gids( &num_gids, gid_list );
1124
1125         /* Send data back to client */
1126
1127         state->response.data.num_entries = num_gids;
1128         state->response.extra_data = gid_list;
1129         state->response.length += num_gids * sizeof(gid_t);
1130
1131         result = WINBINDD_OK;
1132
1133  done:
1134
1135         talloc_destroy(mem_ctx);
1136
1137         return result;
1138 }
1139
1140 static void add_sid_to_parray_unique(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1141                                     DOM_SID ***sids, int *num_sids)
1142 {
1143         int i;
1144
1145         for (i=0; i<(*num_sids); i++) {
1146                 if (sid_compare(sid, (*sids)[i]) == 0)
1147                         return;
1148         }
1149
1150         *sids = TALLOC_REALLOC_ARRAY(mem_ctx, *sids, DOM_SID *, *num_sids+1);
1151
1152         if (*sids == NULL)
1153                 return;
1154
1155         (*sids)[*num_sids] = TALLOC_P(mem_ctx, DOM_SID);
1156         sid_copy((*sids)[*num_sids], sid);
1157         *num_sids += 1;
1158         return;
1159 }
1160
1161 static void add_local_sids_from_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1162                                     DOM_SID ***user_grpsids,
1163                                     int *num_groups)
1164 {
1165         DOM_SID *aliases = NULL;
1166         int i, num_aliases = 0;
1167
1168         if (!pdb_enum_alias_memberships(sid, 1, &aliases, &num_aliases))
1169                 return;
1170
1171         if (num_aliases == 0)
1172                 return;
1173
1174         for (i=0; i<num_aliases; i++)
1175                 add_sid_to_parray_unique(mem_ctx, &aliases[i], user_grpsids,
1176                                          num_groups);
1177
1178         SAFE_FREE(aliases);
1179
1180         return;
1181 }
1182
1183 /* Get user supplementary sids. This is equivalent to the
1184    winbindd_getgroups() function but it involves a SID->SIDs mapping
1185    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1186    idmap. This call is designed to be used with applications that need
1187    to do ACL evaluation themselves. Note that the cached info3 data is
1188    not used 
1189
1190    this function assumes that the SID that comes in is a user SID. If
1191    you pass in another type of SID then you may get unpredictable
1192    results.
1193 */
1194 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1195 {
1196         DOM_SID user_sid;
1197         NTSTATUS status;
1198         DOM_SID **user_grpsids;
1199         struct winbindd_domain *domain;
1200         enum winbindd_result result = WINBINDD_ERROR;
1201         unsigned int i;
1202         TALLOC_CTX *mem_ctx;
1203         char *ret = NULL;
1204         uint32 num_groups;
1205         unsigned ofs, ret_size = 0;
1206
1207         /* Ensure null termination */
1208         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1209
1210         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1211                 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1212                 return WINBINDD_ERROR;
1213         }
1214
1215         if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1216                                     state->request.data.username))) {
1217                 return WINBINDD_ERROR;
1218         }
1219
1220         /* Get info for the domain */   
1221         if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1222                 DEBUG(0,("could not find domain entry for sid %s\n", 
1223                           sid_string_static(&user_sid)));
1224                 goto done;
1225         }
1226         
1227         status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1228                                                     &user_sid, &num_groups, 
1229                                                     &user_grpsids);
1230         if (!NT_STATUS_IS_OK(status)) 
1231                 goto done;
1232
1233         if (num_groups == 0) {
1234                 goto no_groups;
1235         }
1236
1237         if (lp_winbind_nested_groups()) {
1238                 int k;
1239                 /* num_groups is changed during the loop, that's why we have
1240                    to count down here.*/
1241
1242                 for (k=num_groups-1; k>=0; k--) {
1243                         add_local_sids_from_sid(mem_ctx, user_grpsids[k],
1244                                                 &user_grpsids, &num_groups);
1245                 }
1246
1247                 add_local_sids_from_sid(mem_ctx, &user_sid, &user_grpsids,
1248                                         &num_groups);
1249         }
1250
1251         /* work out the response size */
1252         for (i = 0; i < num_groups; i++) {
1253                 const char *s = sid_string_static(user_grpsids[i]);
1254                 ret_size += strlen(s) + 1;
1255         }
1256
1257         /* build the reply */
1258         ret = SMB_MALLOC(ret_size);
1259         if (!ret) goto done;
1260         ofs = 0;
1261         for (i = 0; i < num_groups; i++) {
1262                 const char *s = sid_string_static(user_grpsids[i]);
1263                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1264                 ofs += strlen(ret+ofs) + 1;
1265         }
1266
1267 no_groups:
1268         /* Send data back to client */
1269         state->response.data.num_entries = num_groups;
1270         state->response.extra_data = ret;
1271         state->response.length += ret_size;
1272         result = WINBINDD_OK;
1273
1274  done:
1275         talloc_destroy(mem_ctx);
1276
1277         return result;
1278 }
1279