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