r6080: Port some of the non-critical changes from HEAD to 3_0. The main one is the
[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                 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 BOOL enum_alias_memberships(const DOM_SID *member_sid,
924                                    DOM_SID **aliases, int *num_aliases)
925 {
926         TALLOC_CTX *mem_ctx = talloc_init("enum_alias_memberships");
927         DOM_SID builtin_sid;
928
929         uint32 *rids = NULL;
930         int i, num_rids = 0;
931
932         BOOL result = False;
933
934         if (mem_ctx == NULL)
935                 return False;
936         
937         *aliases = NULL;
938         *num_aliases = 0;
939
940         if (!pdb_enum_alias_memberships(mem_ctx, get_global_sam_sid(),
941                                         member_sid, 1, &rids, &num_rids))
942                 goto done;
943
944         for (i=0; i<num_rids; i++) {
945                 DOM_SID alias_sid;
946                 sid_copy(&alias_sid, get_global_sam_sid());
947                 sid_append_rid(&alias_sid, rids[i]);
948                 add_sid_to_array(NULL, &alias_sid, aliases, num_aliases);
949         }
950
951         string_to_sid(&builtin_sid, "S-1-5-32");
952
953         if (!pdb_enum_alias_memberships(mem_ctx, &builtin_sid,
954                                         member_sid, 1, &rids, &num_rids))
955                 goto done;
956
957         for (i=0; i<num_rids; i++) {
958                 DOM_SID alias_sid;
959                 sid_copy(&alias_sid, &builtin_sid);
960                 sid_append_rid(&alias_sid, rids[i]);
961                 add_sid_to_array(NULL, &alias_sid, aliases, num_aliases);
962         }
963
964         result = True;
965  done:
966         if (mem_ctx != NULL)
967                 talloc_destroy(mem_ctx);
968
969         return result;
970 }
971
972 static void add_local_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
973 {
974         gid_t gid;
975         DOM_SID *aliases;
976         int j, num_aliases;
977
978         DEBUG(10, ("Adding local gids from SID: %s\n",
979                    sid_string_static(sid)));
980
981         /* Don't expand aliases if not explicitly activated -- for now
982            -- jerry */
983
984         if (!lp_winbind_nested_groups())
985                 return;
986
987         /* Add nested group memberships */
988
989         if (!enum_alias_memberships(sid, &aliases, &num_aliases))
990                 return;
991
992         for (j=0; j<num_aliases; j++) {
993                 enum SID_NAME_USE type;
994
995                 if (!local_sid_to_gid(&gid, &aliases[j], &type)) {
996                         DEBUG(1, ("Got an alias membership with no alias\n"));
997                         continue;
998                 }
999
1000                 if ((type != SID_NAME_ALIAS) && (type != SID_NAME_WKN_GRP)) {
1001                         DEBUG(1, ("Got an alias membership in a non-alias\n"));
1002                         continue;
1003                 }
1004
1005                 add_gid_to_array_unique(NULL, gid, gids, num);
1006         }
1007         SAFE_FREE(aliases);
1008 }
1009
1010 static void add_gids_from_user_sid(DOM_SID *sid, gid_t **gids, int *num)
1011 {
1012         DEBUG(10, ("Adding gids from user SID: %s\n",
1013                    sid_string_static(sid)));
1014
1015         add_local_gids_from_sid(sid, gids, num);
1016 }
1017
1018 static void add_gids_from_group_sid(DOM_SID *sid, gid_t **gids, int *num)
1019 {
1020         gid_t gid;
1021
1022         DEBUG(10, ("Adding gids from group SID: %s\n",
1023                    sid_string_static(sid)));
1024
1025         if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
1026                 add_gid_to_array_unique(NULL, gid, gids, num);
1027
1028         add_local_gids_from_sid(sid, gids, num);
1029 }
1030
1031 /* Get user supplementary groups.  This is much quicker than trying to
1032    invert the groups database.  We merge the groups from the gids and
1033    other_sids info3 fields as trusted domain, universal group
1034    memberships, and nested groups (win2k native mode only) are not
1035    returned by the getgroups RPC call but are present in the info3. */
1036
1037 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
1038 {
1039         fstring name_domain, name_user;
1040         DOM_SID user_sid, group_sid;
1041         enum SID_NAME_USE name_type;
1042         uint32 num_groups = 0;
1043         uint32 num_gids = 0;
1044         NTSTATUS status;
1045         DOM_SID **user_grpsids;
1046         struct winbindd_domain *domain;
1047         enum winbindd_result result = WINBINDD_ERROR;
1048         gid_t *gid_list = NULL;
1049         unsigned int i;
1050         TALLOC_CTX *mem_ctx;
1051         NET_USER_INFO_3 *info3 = NULL;
1052         
1053         /* Ensure null termination */
1054         state->request.data.username[sizeof(state->request.data.username)-1]='\0';
1055
1056         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1057                   state->request.data.username));
1058
1059         if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
1060                                           state->request.data.username)))
1061                 return WINBINDD_ERROR;
1062
1063         /* Parse domain and username */
1064
1065         parse_domain_user(state->request.data.username, 
1066                           name_domain, name_user);
1067         
1068         /* Get info for the domain */
1069         
1070         if ((domain = find_domain_from_name(name_domain)) == NULL) {
1071                 DEBUG(7, ("could not find domain entry for domain %s\n", 
1072                           name_domain));
1073                 goto done;
1074         }
1075
1076         if ( domain->primary && lp_winbind_trusted_domains_only()) {
1077                 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n", 
1078                         name_domain, name_user));
1079                 return WINBINDD_ERROR;
1080         }       
1081         
1082         /* Get rid and name type from name.  The following costs 1 packet */
1083
1084         if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid, 
1085                                          &name_type)) {
1086                 DEBUG(4, ("user '%s' does not exist\n", name_user));
1087                 goto done;
1088         }
1089
1090         if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1091                 DEBUG(1, ("name '%s' is not a user name: %d\n", 
1092                           name_user, name_type));
1093                 goto done;
1094         }
1095
1096         add_gids_from_user_sid(&user_sid, &gid_list, &num_gids);
1097
1098         /* Treat the info3 cache as authoritative as the
1099            lookup_usergroups() function may return cached data. */
1100
1101         if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1102
1103                 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1104                            info3->num_groups2, info3->num_other_sids));
1105
1106                 num_groups = info3->num_other_sids + info3->num_groups2;
1107
1108                 /* Go through each other sid and convert it to a gid */
1109
1110                 for (i = 0; i < info3->num_other_sids; i++) {
1111                         fstring name;
1112                         fstring dom_name;
1113                         enum SID_NAME_USE sid_type;
1114
1115                         /* Is this sid known to us?  It can either be
1116                            a trusted domain sid or a foreign sid. */
1117
1118                         if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid, 
1119                                 dom_name, name, &sid_type))
1120                         {
1121                                 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n", 
1122                                            sid_string_static(&info3->other_sids[i].sid)));
1123                                 continue;
1124                         }
1125
1126                         /* Check it is a domain group or an alias (domain local group) 
1127                            in a win2k native mode domain. */
1128                         
1129                         if ( !((sid_type==SID_NAME_DOM_GRP) ||
1130                                 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1131                         {
1132                                 DEBUG(10, ("winbindd_getgroups: sid type %d "
1133                                            "for %s is not a domain group\n",
1134                                            sid_type,
1135                                            sid_string_static(
1136                                                    &info3->other_sids[i].sid)));
1137                                 continue;
1138                         }
1139
1140                         add_gids_from_group_sid(&info3->other_sids[i].sid,
1141                                                 &gid_list, &num_gids);
1142                 }
1143
1144                 for (i = 0; i < info3->num_groups2; i++) {
1145                 
1146                         /* create the group SID */
1147                         
1148                         sid_copy( &group_sid, &domain->sid );
1149                         sid_append_rid( &group_sid, info3->gids[i].g_rid );
1150
1151                         add_gids_from_group_sid(&group_sid, &gid_list,
1152                                                 &num_gids);
1153                 }
1154
1155                 SAFE_FREE(info3);
1156
1157         } else {
1158                 status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1159                                                     &user_sid, &num_groups, 
1160                                                     &user_grpsids);
1161                 if (!NT_STATUS_IS_OK(status)) 
1162                         goto done;
1163
1164                 if (state->response.extra_data)
1165                         goto done;
1166
1167                 for (i = 0; i < num_groups; i++) {
1168                         add_gids_from_group_sid(user_grpsids[i],
1169                                                 &gid_list, &num_gids);
1170                 }
1171         }
1172
1173         /* We want at least one group... */
1174         if (gid_list == NULL)
1175                 goto done;
1176
1177         remove_duplicate_gids( &num_gids, gid_list );
1178
1179         /* Send data back to client */
1180
1181         state->response.data.num_entries = num_gids;
1182         state->response.extra_data = gid_list;
1183         state->response.length += num_gids * sizeof(gid_t);
1184
1185         result = WINBINDD_OK;
1186
1187  done:
1188
1189         talloc_destroy(mem_ctx);
1190
1191         return result;
1192 }
1193
1194 static void add_sid_to_parray_unique(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1195                                     DOM_SID ***sids, int *num_sids)
1196 {
1197         int i;
1198
1199         for (i=0; i<(*num_sids); i++) {
1200                 if (sid_compare(sid, (*sids)[i]) == 0)
1201                         return;
1202         }
1203
1204         *sids = TALLOC_REALLOC_ARRAY(mem_ctx, *sids, DOM_SID *, *num_sids+1);
1205
1206         if (*sids == NULL)
1207                 return;
1208
1209         (*sids)[*num_sids] = TALLOC_P(mem_ctx, DOM_SID);
1210         sid_copy((*sids)[*num_sids], sid);
1211         *num_sids += 1;
1212         return;
1213 }
1214
1215 static void add_local_sids_from_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1216                                     DOM_SID ***user_grpsids,
1217                                     int *num_groups)
1218 {
1219         DOM_SID *aliases = NULL;
1220         int i, num_aliases = 0;
1221
1222         if (!enum_alias_memberships(sid, &aliases, &num_aliases))
1223                 return;
1224
1225         if (num_aliases == 0)
1226                 return;
1227
1228         for (i=0; i<num_aliases; i++)
1229                 add_sid_to_parray_unique(mem_ctx, &aliases[i], user_grpsids,
1230                                          num_groups);
1231
1232         SAFE_FREE(aliases);
1233
1234         return;
1235 }
1236
1237 /* Get user supplementary sids. This is equivalent to the
1238    winbindd_getgroups() function but it involves a SID->SIDs mapping
1239    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1240    idmap. This call is designed to be used with applications that need
1241    to do ACL evaluation themselves. Note that the cached info3 data is
1242    not used 
1243
1244    this function assumes that the SID that comes in is a user SID. If
1245    you pass in another type of SID then you may get unpredictable
1246    results.
1247 */
1248 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1249 {
1250         DOM_SID user_sid;
1251         NTSTATUS status;
1252         DOM_SID **user_grpsids;
1253         struct winbindd_domain *domain;
1254         enum winbindd_result result = WINBINDD_ERROR;
1255         unsigned int i;
1256         TALLOC_CTX *mem_ctx;
1257         char *ret = NULL;
1258         uint32 num_groups;
1259         unsigned ofs, ret_size = 0;
1260
1261         /* Ensure null termination */
1262         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1263
1264         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1265                 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1266                 return WINBINDD_ERROR;
1267         }
1268
1269         if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1270                                     state->request.data.username))) {
1271                 return WINBINDD_ERROR;
1272         }
1273
1274         /* Get info for the domain */   
1275         if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1276                 DEBUG(0,("could not find domain entry for sid %s\n", 
1277                           sid_string_static(&user_sid)));
1278                 goto done;
1279         }
1280         
1281         status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1282                                                     &user_sid, &num_groups, 
1283                                                     &user_grpsids);
1284         if (!NT_STATUS_IS_OK(status)) 
1285                 goto done;
1286
1287         if (num_groups == 0) {
1288                 goto no_groups;
1289         }
1290
1291         domain = find_our_domain();
1292
1293         if (domain == NULL) {
1294                 DEBUG(0, ("Could not find our domain\n"));
1295                 goto done;
1296         }
1297
1298         /* Note that I do not check for AD or its mode. XP in a real NT4
1299          * domain also asks for this info. -- vl */
1300
1301         if (!IS_DC) {
1302                 uint32 *alias_rids = NULL;
1303                 int num_aliases;
1304
1305                 /* We need to include the user SID to expand */
1306                 user_grpsids = TALLOC_REALLOC_ARRAY(mem_ctx, user_grpsids,
1307                                                     DOM_SID *, num_groups+1);
1308                 user_grpsids[num_groups] = &user_sid;
1309
1310                 status = domain->methods->lookup_useraliases(domain, mem_ctx,
1311                                                              num_groups,
1312                                                              user_grpsids+1,
1313                                                              &num_aliases,
1314                                                              &alias_rids);
1315
1316                 if (!NT_STATUS_IS_OK(status)) {
1317                         DEBUG(3, ("Could not expand alias sids: %s\n",
1318                                   nt_errstr(status)));
1319                         goto done;
1320                 }
1321
1322                 for (i=0; i<num_aliases; i++) {
1323                         DOM_SID sid;
1324                         sid_copy(&sid, &domain->sid);
1325                         sid_append_rid(&sid, alias_rids[i]);
1326                         add_sid_to_parray_unique(mem_ctx, &sid, &user_grpsids,
1327                                                  &num_groups);
1328                 }
1329         }
1330
1331         if (lp_winbind_nested_groups()) {
1332                 int k;
1333                 /* num_groups is changed during the loop, that's why we have
1334                    to count down here.*/
1335
1336                 for (k=num_groups-1; k>=0; k--) {
1337                         add_local_sids_from_sid(mem_ctx, user_grpsids[k],
1338                                                 &user_grpsids, &num_groups);
1339                 }
1340
1341                 add_local_sids_from_sid(mem_ctx, &user_sid, &user_grpsids,
1342                                         &num_groups);
1343         }
1344
1345         /* work out the response size */
1346         for (i = 0; i < num_groups; i++) {
1347                 const char *s = sid_string_static(user_grpsids[i]);
1348                 ret_size += strlen(s) + 1;
1349         }
1350
1351         /* build the reply */
1352         ret = SMB_MALLOC(ret_size);
1353         if (!ret) goto done;
1354         ofs = 0;
1355         for (i = 0; i < num_groups; i++) {
1356                 const char *s = sid_string_static(user_grpsids[i]);
1357                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1358                 ofs += strlen(ret+ofs) + 1;
1359         }
1360
1361 no_groups:
1362         /* Send data back to client */
1363         state->response.data.num_entries = num_groups;
1364         state->response.extra_data = ret;
1365         state->response.length += ret_size;
1366         result = WINBINDD_OK;
1367
1368  done:
1369         talloc_destroy(mem_ctx);
1370
1371         return result;
1372 }
1373