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