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