c2371c48c58c0f9d45361162be7ce01c1ef147b9
[metze/samba/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                 if ( !domain->initialized )
862                         set_dc_type_and_flags( domain );
863
864                         
865                 ZERO_STRUCT(groups);
866
867                 /* Get list of sam groups */
868                 
869                 fstrcpy(groups.domain_name, domain->name);
870
871                 get_sam_group_entries(&groups);
872                         
873                 if (groups.num_sam_entries == 0) {
874                         /* this domain is empty or in an error state */
875                         continue;
876                 }
877
878                 /* keep track the of the total number of groups seen so 
879                    far over all domains */
880                 total_entries += groups.num_sam_entries;
881                 
882                 /* Allocate some memory for extra data.  Note that we limit
883                    account names to sizeof(fstring) = 128 characters.  */               
884                 ted = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
885  
886                 if (!ted) {
887                         DEBUG(0,("failed to enlarge buffer!\n"));
888                         SAFE_FREE(extra_data);
889                         return WINBINDD_ERROR;
890                 } else
891                         extra_data = ted;
892
893                 /* Pack group list into extra data fields */
894                 for (i = 0; i < groups.num_sam_entries; i++) {
895                         char *group_name = ((struct acct_info *)
896                                             groups.sam_entries)[i].acct_name; 
897                         fstring name;
898
899                         fill_domain_username(name, domain->name, group_name);
900                         /* Append to extra data */                      
901                         memcpy(&extra_data[extra_data_len], name, 
902                                strlen(name));
903                         extra_data_len += strlen(name);
904                         extra_data[extra_data_len++] = ',';
905                 }
906
907                 SAFE_FREE(groups.sam_entries);
908         }
909
910         /* Assign extra_data fields in response structure */
911         if (extra_data) {
912                 extra_data[extra_data_len - 1] = '\0';
913                 state->response.extra_data = extra_data;
914                 state->response.length += extra_data_len;
915         }
916
917         /* No domains may have responded but that's still OK so don't
918            return an error. */
919
920         return WINBINDD_OK;
921 }
922
923 static void add_local_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
924 {
925         gid_t gid;
926         DOM_SID *aliases;
927         int j, num_aliases;
928
929         DEBUG(10, ("Adding local gids from SID: %s\n",
930                    sid_string_static(sid)));
931
932         /* Don't expand aliases if not explicitly activated -- for now
933            -- jerry */
934
935         if (!lp_winbind_nested_groups())
936                 return;
937
938         /* Add nested group memberships */
939
940         if (!pdb_enum_alias_memberships(sid, 1, &aliases, &num_aliases))
941                 return;
942
943         for (j=0; j<num_aliases; j++) {
944                 enum SID_NAME_USE type;
945
946                 if (!local_sid_to_gid(&gid, &aliases[j], &type)) {
947                         DEBUG(1, ("Got an alias membership with no alias\n"));
948                         continue;
949                 }
950
951                 if ((type != SID_NAME_ALIAS) && (type != SID_NAME_WKN_GRP)) {
952                         DEBUG(1, ("Got an alias membership in a non-alias\n"));
953                         continue;
954                 }
955
956                 add_gid_to_array_unique(gid, gids, num);
957         }
958         SAFE_FREE(aliases);
959 }
960
961 static void add_gids_from_user_sid(DOM_SID *sid, gid_t **gids, int *num)
962 {
963         DEBUG(10, ("Adding gids from user SID: %s\n",
964                    sid_string_static(sid)));
965
966         add_local_gids_from_sid(sid, gids, num);
967 }
968
969 static void add_gids_from_group_sid(DOM_SID *sid, gid_t **gids, int *num)
970 {
971         gid_t gid;
972
973         DEBUG(10, ("Adding gids from group SID: %s\n",
974                    sid_string_static(sid)));
975
976         if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
977                 add_gid_to_array_unique(gid, gids, num);
978
979         add_local_gids_from_sid(sid, gids, num);
980 }
981
982 /* Get user supplementary groups.  This is much quicker than trying to
983    invert the groups database.  We merge the groups from the gids and
984    other_sids info3 fields as trusted domain, universal group
985    memberships, and nested groups (win2k native mode only) are not
986    returned by the getgroups RPC call but are present in the info3. */
987
988 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
989 {
990         fstring name_domain, name_user;
991         DOM_SID user_sid, group_sid;
992         enum SID_NAME_USE name_type;
993         uint32 num_groups = 0;
994         uint32 num_gids = 0;
995         NTSTATUS status;
996         DOM_SID **user_grpsids;
997         struct winbindd_domain *domain;
998         enum winbindd_result result = WINBINDD_ERROR;
999         gid_t *gid_list = NULL;
1000         unsigned int i;
1001         TALLOC_CTX *mem_ctx;
1002         NET_USER_INFO_3 *info3 = NULL;
1003         
1004         /* Ensure null termination */
1005         state->request.data.username[sizeof(state->request.data.username)-1]='\0';
1006
1007         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1008                   state->request.data.username));
1009
1010         if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
1011                                           state->request.data.username)))
1012                 return WINBINDD_ERROR;
1013
1014         /* Parse domain and username */
1015
1016         parse_domain_user(state->request.data.username, 
1017                           name_domain, name_user);
1018         
1019         /* Get info for the domain */
1020         
1021         if ((domain = find_domain_from_name(name_domain)) == NULL) {
1022                 DEBUG(7, ("could not find domain entry for domain %s\n", 
1023                           name_domain));
1024                 goto done;
1025         }
1026
1027         if ( domain->primary && lp_winbind_trusted_domains_only()) {
1028                 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n", 
1029                         name_domain, name_user));
1030                 return WINBINDD_ERROR;
1031         }       
1032         
1033         /* Get rid and name type from name.  The following costs 1 packet */
1034
1035         if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid, 
1036                                          &name_type)) {
1037                 DEBUG(4, ("user '%s' does not exist\n", name_user));
1038                 goto done;
1039         }
1040
1041         if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1042                 DEBUG(1, ("name '%s' is not a user name: %d\n", 
1043                           name_user, name_type));
1044                 goto done;
1045         }
1046
1047         add_gids_from_user_sid(&user_sid, &gid_list, &num_gids);
1048
1049         /* Treat the info3 cache as authoritative as the
1050            lookup_usergroups() function may return cached data. */
1051
1052         if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1053
1054                 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1055                            info3->num_groups2, info3->num_other_sids));
1056
1057                 num_groups = info3->num_other_sids + info3->num_groups2;
1058
1059                 /* Go through each other sid and convert it to a gid */
1060
1061                 for (i = 0; i < info3->num_other_sids; i++) {
1062                         fstring name;
1063                         fstring dom_name;
1064                         enum SID_NAME_USE sid_type;
1065
1066                         /* Is this sid known to us?  It can either be
1067                            a trusted domain sid or a foreign sid. */
1068
1069                         if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid, 
1070                                 dom_name, name, &sid_type))
1071                         {
1072                                 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n", 
1073                                            sid_string_static(&info3->other_sids[i].sid)));
1074                                 continue;
1075                         }
1076
1077                         /* Check it is a domain group or an alias (domain local group) 
1078                            in a win2k native mode domain. */
1079                         
1080                         if ( !((sid_type==SID_NAME_DOM_GRP) ||
1081                                 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1082                         {
1083                                 DEBUG(10, ("winbindd_getgroups: sid type %d "
1084                                            "for %s is not a domain group\n",
1085                                            sid_type,
1086                                            sid_string_static(
1087                                                    &info3->other_sids[i].sid)));
1088                                 continue;
1089                         }
1090
1091                         add_gids_from_group_sid(&info3->other_sids[i].sid,
1092                                                 &gid_list, &num_gids);
1093                 }
1094
1095                 for (i = 0; i < info3->num_groups2; i++) {
1096                 
1097                         /* create the group SID */
1098                         
1099                         sid_copy( &group_sid, &domain->sid );
1100                         sid_append_rid( &group_sid, info3->gids[i].g_rid );
1101
1102                         add_gids_from_group_sid(&group_sid, &gid_list,
1103                                                 &num_gids);
1104                 }
1105
1106                 SAFE_FREE(info3);
1107
1108         } else {
1109                 status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1110                                                     &user_sid, &num_groups, 
1111                                                     &user_grpsids);
1112                 if (!NT_STATUS_IS_OK(status)) 
1113                         goto done;
1114
1115                 if (state->response.extra_data)
1116                         goto done;
1117
1118                 for (i = 0; i < num_groups; i++) {
1119                         add_gids_from_group_sid(user_grpsids[i],
1120                                                 &gid_list, &num_gids);
1121                 }
1122         }
1123
1124         /* We want at least one group... */
1125         if (gid_list == NULL)
1126                 goto done;
1127
1128         remove_duplicate_gids( &num_gids, gid_list );
1129
1130         /* Send data back to client */
1131
1132         state->response.data.num_entries = num_gids;
1133         state->response.extra_data = gid_list;
1134         state->response.length += num_gids * sizeof(gid_t);
1135
1136         result = WINBINDD_OK;
1137
1138  done:
1139
1140         talloc_destroy(mem_ctx);
1141
1142         return result;
1143 }
1144
1145 static void add_sid_to_parray_unique(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1146                                     DOM_SID ***sids, int *num_sids)
1147 {
1148         int i;
1149
1150         for (i=0; i<(*num_sids); i++) {
1151                 if (sid_compare(sid, (*sids)[i]) == 0)
1152                         return;
1153         }
1154
1155         *sids = TALLOC_REALLOC_ARRAY(mem_ctx, *sids, DOM_SID *, *num_sids+1);
1156
1157         if (*sids == NULL)
1158                 return;
1159
1160         (*sids)[*num_sids] = TALLOC_P(mem_ctx, DOM_SID);
1161         sid_copy((*sids)[*num_sids], sid);
1162         *num_sids += 1;
1163         return;
1164 }
1165
1166 static void add_local_sids_from_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1167                                     DOM_SID ***user_grpsids,
1168                                     int *num_groups)
1169 {
1170         DOM_SID *aliases = NULL;
1171         int i, num_aliases = 0;
1172
1173         if (!pdb_enum_alias_memberships(sid, 1, &aliases, &num_aliases))
1174                 return;
1175
1176         if (num_aliases == 0)
1177                 return;
1178
1179         for (i=0; i<num_aliases; i++)
1180                 add_sid_to_parray_unique(mem_ctx, &aliases[i], user_grpsids,
1181                                          num_groups);
1182
1183         SAFE_FREE(aliases);
1184
1185         return;
1186 }
1187
1188 /* Get user supplementary sids. This is equivalent to the
1189    winbindd_getgroups() function but it involves a SID->SIDs mapping
1190    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1191    idmap. This call is designed to be used with applications that need
1192    to do ACL evaluation themselves. Note that the cached info3 data is
1193    not used 
1194
1195    this function assumes that the SID that comes in is a user SID. If
1196    you pass in another type of SID then you may get unpredictable
1197    results.
1198 */
1199 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1200 {
1201         DOM_SID user_sid;
1202         NTSTATUS status;
1203         DOM_SID **user_grpsids;
1204         struct winbindd_domain *domain;
1205         enum winbindd_result result = WINBINDD_ERROR;
1206         unsigned int i;
1207         TALLOC_CTX *mem_ctx;
1208         char *ret = NULL;
1209         uint32 num_groups;
1210         unsigned ofs, ret_size = 0;
1211
1212         /* Ensure null termination */
1213         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1214
1215         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1216                 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1217                 return WINBINDD_ERROR;
1218         }
1219
1220         if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1221                                     state->request.data.username))) {
1222                 return WINBINDD_ERROR;
1223         }
1224
1225         /* Get info for the domain */   
1226         if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1227                 DEBUG(0,("could not find domain entry for sid %s\n", 
1228                           sid_string_static(&user_sid)));
1229                 goto done;
1230         }
1231         
1232         status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1233                                                     &user_sid, &num_groups, 
1234                                                     &user_grpsids);
1235         if (!NT_STATUS_IS_OK(status)) 
1236                 goto done;
1237
1238         if (num_groups == 0) {
1239                 goto no_groups;
1240         }
1241
1242         domain = find_our_domain();
1243
1244         if (domain == NULL) {
1245                 DEBUG(0, ("Could not find our domain\n"));
1246                 goto done;
1247         }
1248
1249         /* Note that I do not check for AD or its mode. XP in a real NT4
1250          * domain also asks for this info. -- vl */
1251
1252         if (!IS_DC) {
1253                 uint32 *alias_rids = NULL;
1254                 int num_aliases;
1255
1256                 /* We need to include the user SID to expand */
1257                 user_grpsids = TALLOC_REALLOC_ARRAY(mem_ctx, user_grpsids,
1258                                                     DOM_SID *, num_groups+1);
1259                 user_grpsids[num_groups] = &user_sid;
1260
1261                 status = domain->methods->lookup_useraliases(domain, mem_ctx,
1262                                                              num_groups,
1263                                                              user_grpsids+1,
1264                                                              &num_aliases,
1265                                                              &alias_rids);
1266
1267                 if (!NT_STATUS_IS_OK(status)) {
1268                         DEBUG(3, ("Could not expand alias sids: %s\n",
1269                                   nt_errstr(status)));
1270                         goto done;
1271                 }
1272
1273                 for (i=0; i<num_aliases; i++) {
1274                         DOM_SID sid;
1275                         sid_copy(&sid, &domain->sid);
1276                         sid_append_rid(&sid, alias_rids[i]);
1277                         add_sid_to_parray_unique(mem_ctx, &sid, &user_grpsids,
1278                                                  &num_groups);
1279                 }
1280         }
1281
1282         if (lp_winbind_nested_groups()) {
1283                 int k;
1284                 /* num_groups is changed during the loop, that's why we have
1285                    to count down here.*/
1286
1287                 for (k=num_groups-1; k>=0; k--) {
1288                         add_local_sids_from_sid(mem_ctx, user_grpsids[k],
1289                                                 &user_grpsids, &num_groups);
1290                 }
1291
1292                 add_local_sids_from_sid(mem_ctx, &user_sid, &user_grpsids,
1293                                         &num_groups);
1294         }
1295
1296         /* work out the response size */
1297         for (i = 0; i < num_groups; i++) {
1298                 const char *s = sid_string_static(user_grpsids[i]);
1299                 ret_size += strlen(s) + 1;
1300         }
1301
1302         /* build the reply */
1303         ret = SMB_MALLOC(ret_size);
1304         if (!ret) goto done;
1305         ofs = 0;
1306         for (i = 0; i < num_groups; i++) {
1307                 const char *s = sid_string_static(user_grpsids[i]);
1308                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1309                 ofs += strlen(ret+ofs) + 1;
1310         }
1311
1312 no_groups:
1313         /* Send data back to client */
1314         state->response.data.num_entries = num_groups;
1315         state->response.extra_data = ret;
1316         state->response.length += ret_size;
1317         result = WINBINDD_OK;
1318
1319  done:
1320         talloc_destroy(mem_ctx);
1321
1322         return result;
1323 }
1324