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