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