r18271: Big change:
[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    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
45         fill_domain_username( full_group_name, dom_name, gr_name, True );
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 lsa_SidType group_name_type, 
62                            size_t *num_gr_mem, char **gr_mem, size_t *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 (%s)\n", the_name, sid_type_lookup(name_types[i])));
144                         continue;
145                 }
146
147                 /* Append domain name */
148
149                 fill_domain_username(name, domain->name, the_name, True);
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 = (char *)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 = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem, 
188                    (unsigned int)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 void winbindd_getgrnam(struct winbindd_cli_state *state)
203 {
204         DOM_SID group_sid, tmp_sid;
205         uint32 grp_rid;
206         struct winbindd_domain *domain;
207         enum lsa_SidType name_type;
208         fstring name_domain, name_group;
209         char *tmp, *gr_mem;
210         size_t gr_mem_len;
211         size_t num_gr_mem;
212         gid_t gid;
213         union unid_t id;
214         NTSTATUS status;
215         
216         /* Ensure null termination */
217         state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
218
219         DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
220                   state->request.data.groupname));
221
222         /* Parse domain and groupname */
223         
224         memset(name_group, 0, sizeof(fstring));
225
226         tmp = state->request.data.groupname;
227         
228         parse_domain_user(tmp, name_domain, name_group);
229
230         /* if no domain or our local domain and no local tdb group, default to
231          * our local domain for aliases */
232
233         if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
234                 fstrcpy(name_domain, get_global_sam_name());
235         }
236
237         /* Get info for the domain */
238
239         if ((domain = find_domain_from_name(name_domain)) == NULL) {
240                 DEBUG(3, ("could not get domain sid for domain %s\n",
241                           name_domain));
242                 request_error(state);
243                 return;
244         }
245         /* should we deal with users for our domain? */
246         
247         if ( lp_winbind_trusted_domains_only() && domain->primary) {
248                 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
249                          "getgrnam() for %s\\%s.\n", name_domain, name_group));
250                 request_error(state);
251                 return;
252         }
253
254         /* Get rid and name type from name */
255         
256         if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, domain->name,
257                                          name_group, &group_sid, &name_type)) {
258                 DEBUG(1, ("group %s in domain %s does not exist\n", 
259                           name_group, name_domain));
260                 request_error(state);
261                 return;
262         }
263
264         if ( !((name_type==SID_NAME_DOM_GRP) ||
265                ((name_type==SID_NAME_ALIAS) && domain->primary) ||
266                ((name_type==SID_NAME_ALIAS) && domain->internal) ||
267                ((name_type==SID_NAME_WKN_GRP) && domain->internal)) )
268         {
269                 DEBUG(1, ("name '%s' is not a local, domain or builtin "
270                           "group: %d\n", name_group, name_type));
271                 request_error(state);
272                 return;
273         }
274
275         /* Make sure that the group SID is within the domain of the
276            original domain */
277
278         sid_copy( &tmp_sid, &group_sid );
279         sid_split_rid( &tmp_sid, &grp_rid );
280         if ( !sid_equal( &tmp_sid, &domain->sid ) ) {
281                 DEBUG(3,("winbindd_getgrnam: group %s resolves to a SID in the wrong domain [%s]\n", 
282                         state->request.data.groupname, sid_string_static(&group_sid)));
283                 request_error(state);
284                 return;
285         }
286
287         
288
289         /* Try to get the GID */
290
291         status = idmap_sid_to_gid(&group_sid, &gid, 0);
292
293         if (NT_STATUS_IS_OK(status)) {
294                 goto got_gid;
295         }
296
297         /* Maybe it's one of our aliases in passdb */
298
299         if (pdb_sid_to_id(&group_sid, &id, &name_type) &&
300             ((name_type == SID_NAME_ALIAS) ||
301              (name_type == SID_NAME_WKN_GRP))) {
302                 gid = id.gid;
303                 goto got_gid;
304         }
305
306         DEBUG(1, ("error converting unix gid to sid\n"));
307         request_error(state);
308         return;
309
310  got_gid:
311
312         if (!fill_grent(&state->response.data.gr, name_domain,
313                         name_group, gid) ||
314             !fill_grent_mem(domain, &group_sid, name_type,
315                             &num_gr_mem,
316                             &gr_mem, &gr_mem_len)) {
317                 request_error(state);
318                 return;
319         }
320
321         state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
322
323         /* Group membership lives at start of extra data */
324
325         state->response.data.gr.gr_mem_ofs = 0;
326
327         state->response.length += gr_mem_len;
328         state->response.extra_data.data = gr_mem;
329         request_ok(state);
330 }
331
332 static void getgrgid_got_sid(struct winbindd_cli_state *state, DOM_SID group_sid)
333 {
334         struct winbindd_domain *domain;
335         enum lsa_SidType name_type;
336         fstring dom_name;
337         fstring group_name;
338         size_t gr_mem_len;
339         size_t num_gr_mem;
340         char *gr_mem;
341
342         /* Get name from sid */
343
344         if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, dom_name,
345                                          group_name, &name_type)) {
346                 DEBUG(1, ("could not lookup sid\n"));
347                 request_error(state);
348                 return;
349         }
350
351         /* Fill in group structure */
352
353         domain = find_domain_from_sid_noinit(&group_sid);
354
355         if (!domain) {
356                 DEBUG(1,("Can't find domain from sid\n"));
357                 request_error(state);
358                 return;
359         }
360
361         if ( !((name_type==SID_NAME_DOM_GRP) ||
362                ((name_type==SID_NAME_ALIAS) && domain->primary) ||
363                ((name_type==SID_NAME_ALIAS) && domain->internal)) )
364         {
365                 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
366                           group_name, name_type));
367                 request_error(state);
368                 return;
369         }
370
371         if (!fill_grent(&state->response.data.gr, dom_name, group_name, 
372                         state->request.data.gid) ||
373             !fill_grent_mem(domain, &group_sid, name_type,
374                             &num_gr_mem,
375                             &gr_mem, &gr_mem_len)) {
376                 request_error(state);
377                 return;
378         }
379
380         state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
381
382         /* Group membership lives at start of extra data */
383
384         state->response.data.gr.gr_mem_ofs = 0;
385
386         state->response.length += gr_mem_len;
387         state->response.extra_data.data = gr_mem;
388
389         request_ok(state);
390 }
391
392 static void getgrgid_recv(void *private_data, BOOL success, const char *sid)
393 {
394         struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
395         enum lsa_SidType name_type;
396         DOM_SID group_sid;
397
398         if (success) {
399                 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
400                           (unsigned long)(state->request.data.gid), sid));
401
402                 string_to_sid(&group_sid, sid);
403                 getgrgid_got_sid(state, group_sid);
404                 return;
405         }
406
407         /* Ok, this might be "ours", i.e. an alias */
408         if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
409             lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
410             (name_type == SID_NAME_ALIAS)) {
411                 /* Hey, got an alias */
412                 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
413                           (unsigned long)(state->request.data.gid), sid));
414                 getgrgid_got_sid(state, group_sid);
415                 return;
416         }
417
418         DEBUG(1, ("could not convert gid %lu to sid\n", 
419                   (unsigned long)state->request.data.gid));
420         request_error(state);
421 }
422
423 /* Return a group structure from a gid number */
424 void winbindd_getgrgid(struct winbindd_cli_state *state)
425 {
426         DOM_SID group_sid;
427         NTSTATUS status;
428
429         DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, 
430                   (unsigned long)state->request.data.gid));
431
432         /* Bug out if the gid isn't in the winbind range */
433
434         if ((state->request.data.gid < server_state.gid_low) ||
435             (state->request.data.gid > server_state.gid_high)) {
436                 request_error(state);
437                 return;
438         }
439
440         /* Get sid from gid */
441
442         status = idmap_gid_to_sid(&group_sid, state->request.data.gid, IDMAP_FLAG_NONE);
443         if (NT_STATUS_IS_OK(status)) {
444                 /* This is a remote one */
445                 getgrgid_got_sid(state, group_sid);
446                 return;
447         }
448
449         DEBUG(10,("winbindd_getgrgid: gid %lu not found in cache, try with the async interface\n",
450                   (unsigned long)state->request.data.gid));
451
452         winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state);
453 }
454
455 /*
456  * set/get/endgrent functions
457  */
458
459 /* "Rewind" file pointer for group database enumeration */
460
461 static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state)
462 {
463         struct winbindd_domain *domain;
464
465         DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
466
467         /* Check user has enabled this */
468
469         if (!lp_winbind_enum_groups()) {
470                 return False;
471         }               
472
473         /* Free old static data if it exists */
474         
475         if (state->getgrent_state != NULL) {
476                 free_getent_state(state->getgrent_state);
477                 state->getgrent_state = NULL;
478         }
479         
480         /* Create sam pipes for each domain we know about */
481         
482         for (domain = domain_list(); domain != NULL; domain = domain->next) {
483                 struct getent_state *domain_state;
484                 
485                 /* Create a state record for this domain */
486
487                 /* don't add our domaina if we are a PDC or if we 
488                    are a member of a Samba domain */
489                 
490                 if ( lp_winbind_trusted_domains_only() && domain->primary )
491                 {
492                         continue;
493                 }
494                                                 
495                 
496                 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
497                         DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
498                         return False;
499                 }
500                 
501                 ZERO_STRUCTP(domain_state);
502                 
503                 fstrcpy(domain_state->domain_name, domain->name);
504
505                 /* Add to list of open domains */
506                 
507                 DLIST_ADD(state->getgrent_state, domain_state);
508         }
509         
510         state->getgrent_initialized = True;
511         return True;
512 }
513
514 void winbindd_setgrent(struct winbindd_cli_state *state)
515 {
516         if (winbindd_setgrent_internal(state)) {
517                 request_ok(state);
518         } else {
519                 request_error(state);
520         }
521 }
522
523 /* Close file pointer to ntdom group database */
524
525 void winbindd_endgrent(struct winbindd_cli_state *state)
526 {
527         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
528
529         free_getent_state(state->getgrent_state);
530         state->getgrent_initialized = False;
531         state->getgrent_state = NULL;
532         request_ok(state);
533 }
534
535 /* Get the list of domain groups and domain aliases for a domain.  We fill in
536    the sam_entries and num_sam_entries fields with domain group information.  
537    The dispinfo_ndx field is incremented to the index of the next group to 
538    fetch. Return True if some groups were returned, False otherwise. */
539
540 static BOOL get_sam_group_entries(struct getent_state *ent)
541 {
542         NTSTATUS status;
543         uint32 num_entries;
544         struct acct_info *name_list = NULL;
545         TALLOC_CTX *mem_ctx;
546         BOOL result = False;
547         struct acct_info *sam_grp_entries = NULL;
548         struct winbindd_domain *domain;
549         
550         if (ent->got_sam_entries)
551                 return False;
552
553         if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
554                                           ent->domain_name))) {
555                 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); 
556                 return False;
557         }
558                 
559         /* Free any existing group info */
560
561         SAFE_FREE(ent->sam_entries);
562         ent->num_sam_entries = 0;
563         ent->got_sam_entries = True;
564
565         /* Enumerate domain groups */
566
567         num_entries = 0;
568
569         if (!(domain = find_domain_from_name(ent->domain_name))) {
570                 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
571                 goto done;
572         }
573
574         /* always get the domain global groups */
575
576         status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
577         
578         if (!NT_STATUS_IS_OK(status)) {
579                 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
580                 result = False;
581                 goto done;
582         }
583
584         /* Copy entries into return buffer */
585
586         if (num_entries) {
587                 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
588                         DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", 
589                                 num_entries));
590                         result = False;
591                         goto done;
592                 }
593                 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
594         }
595         
596         ent->num_sam_entries = num_entries;
597         
598         /* get the domain local groups if we are a member of a native win2k domain
599            and are not using LDAP to get the groups */
600            
601         if ( ( lp_security() != SEC_ADS && domain->native_mode 
602                 && domain->primary) || domain->internal )
603         {
604                 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n", 
605                         domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
606                 
607                 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
608                 
609                 if ( !NT_STATUS_IS_OK(status) ) { 
610                         DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
611                         num_entries = 0;
612                 }
613                 else
614                         DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
615                 
616                 /* Copy entries into return buffer */
617
618                 if ( num_entries ) {
619                         if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
620                         {
621                                 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", 
622                                         num_entries));
623                                 result = False;
624                                 goto done;
625                         }
626                         
627                         memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, 
628                                 num_entries * sizeof(struct acct_info) );
629                 }
630         
631                 ent->num_sam_entries += num_entries;
632         }
633         
634                 
635         /* Fill in remaining fields */
636
637         ent->sam_entries = name_list;
638         ent->sam_entry_index = 0;
639
640         result = (ent->num_sam_entries > 0);
641
642  done:
643         talloc_destroy(mem_ctx);
644
645         return result;
646 }
647
648 /* Fetch next group entry from ntdom database */
649
650 #define MAX_GETGRENT_GROUPS 500
651
652 void winbindd_getgrent(struct winbindd_cli_state *state)
653 {
654         struct getent_state *ent;
655         struct winbindd_gr *group_list = NULL;
656         int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
657         char *gr_mem_list = NULL;
658
659         DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
660
661         /* Check user has enabled this */
662
663         if (!lp_winbind_enum_groups()) {
664                 request_error(state);
665                 return;
666         }
667
668         num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
669
670         if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
671                 request_error(state);
672                 return;
673         }
674
675         memset(state->response.extra_data.data, '\0',
676                 num_groups * sizeof(struct winbindd_gr) );
677
678         state->response.data.num_entries = 0;
679
680         group_list = (struct winbindd_gr *)state->response.extra_data.data;
681
682         if (!state->getgrent_initialized)
683                 winbindd_setgrent_internal(state);
684
685         if (!(ent = state->getgrent_state)) {
686                 request_error(state);
687                 return;
688         }
689
690         /* Start sending back groups */
691
692         for (i = 0; i < num_groups; i++) {
693                 struct acct_info *name_list = NULL;
694                 fstring domain_group_name;
695                 uint32 result;
696                 gid_t group_gid;
697                 size_t gr_mem_len;
698                 char *gr_mem;
699                 DOM_SID group_sid;
700                 struct winbindd_domain *domain;
701                                 
702                 /* Do we need to fetch another chunk of groups? */
703
704         tryagain:
705
706                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
707                            ent->sam_entry_index, ent->num_sam_entries));
708
709                 if (ent->num_sam_entries == ent->sam_entry_index) {
710
711                         while(ent && !get_sam_group_entries(ent)) {
712                                 struct getent_state *next_ent;
713
714                                 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 
715
716                                 /* Free state information for this domain */
717
718                                 SAFE_FREE(ent->sam_entries);
719
720                                 next_ent = ent->next;
721                                 DLIST_REMOVE(state->getgrent_state, ent);
722                                 
723                                 SAFE_FREE(ent);
724                                 ent = next_ent;
725                         }
726
727                         /* No more domains */
728
729                         if (!ent) 
730                                 break;
731                 }
732                 
733                 name_list = (struct acct_info *)ent->sam_entries;
734                 
735                 if (!(domain = 
736                       find_domain_from_name(ent->domain_name))) {
737                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
738                         result = False;
739                         goto done;
740                 }
741
742                 /* Lookup group info */
743                 
744                 sid_copy(&group_sid, &domain->sid);
745                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
746
747                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid,
748                                                     &group_gid, 0))) {
749                         union unid_t id;
750                         enum lsa_SidType type;
751
752                         DEBUG(10, ("SID %s not in idmap\n",
753                                    sid_string_static(&group_sid)));
754
755                         if (!pdb_sid_to_id(&group_sid, &id, &type)) {
756                                 DEBUG(1, ("could not look up gid for group "
757                                           "%s\n", 
758                                           name_list[ent->sam_entry_index].acct_name));
759                                 ent->sam_entry_index++;
760                                 goto tryagain;
761                         }
762
763                         if ((type != SID_NAME_DOM_GRP) &&
764                             (type != SID_NAME_ALIAS) &&
765                             (type != SID_NAME_WKN_GRP)) {
766                                 DEBUG(1, ("Group %s is a %s, not a group\n",
767                                           sid_type_lookup(type),
768                                           name_list[ent->sam_entry_index].acct_name));
769                                 ent->sam_entry_index++;
770                                 goto tryagain;
771                         }
772                         group_gid = id.gid;
773                 }
774
775                 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
776                            (unsigned long)name_list[ent->sam_entry_index].rid));
777                 
778                 /* Fill in group entry */
779
780                 fill_domain_username(domain_group_name, ent->domain_name, 
781                          name_list[ent->sam_entry_index].acct_name, True);
782
783                 result = fill_grent(&group_list[group_list_ndx], 
784                                     ent->domain_name,
785                                     name_list[ent->sam_entry_index].acct_name,
786                                     group_gid);
787
788                 /* Fill in group membership entry */
789
790                 if (result) {
791                         size_t num_gr_mem = 0;
792                         DOM_SID member_sid;
793                         group_list[group_list_ndx].num_gr_mem = 0;
794                         gr_mem = NULL;
795                         gr_mem_len = 0;
796                         
797                         /* Get group membership */                      
798                         if (state->request.cmd == WINBINDD_GETGRLST) {
799                                 result = True;
800                         } else {
801                                 sid_copy(&member_sid, &domain->sid);
802                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
803                                 result = fill_grent_mem(
804                                         domain,
805                                         &member_sid,
806                                         SID_NAME_DOM_GRP,
807                                         &num_gr_mem,
808                                         &gr_mem, &gr_mem_len);
809
810                                 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
811                         }
812                 }
813
814                 if (result) {
815                         /* Append to group membership list */
816                         gr_mem_list = (char *)SMB_REALLOC(
817                                 gr_mem_list, gr_mem_list_len + gr_mem_len);
818
819                         if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
820                                 DEBUG(0, ("out of memory\n"));
821                                 gr_mem_list_len = 0;
822                                 break;
823                         }
824
825                         DEBUG(10, ("list_len = %d, mem_len = %u\n",
826                                    gr_mem_list_len, (unsigned int)gr_mem_len));
827
828                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
829                                gr_mem_len);
830
831                         SAFE_FREE(gr_mem);
832
833                         group_list[group_list_ndx].gr_mem_ofs = 
834                                 gr_mem_list_len;
835
836                         gr_mem_list_len += gr_mem_len;
837                 }
838
839                 ent->sam_entry_index++;
840                 
841                 /* Add group to return list */
842                 
843                 if (result) {
844
845                         DEBUG(10, ("adding group num_entries = %d\n",
846                                    state->response.data.num_entries));
847
848                         group_list_ndx++;
849                         state->response.data.num_entries++;
850                         
851                         state->response.length +=
852                                 sizeof(struct winbindd_gr);
853                         
854                 } else {
855                         DEBUG(0, ("could not lookup domain group %s\n", 
856                                   domain_group_name));
857                 }
858         }
859
860         /* Copy the list of group memberships to the end of the extra data */
861
862         if (group_list_ndx == 0)
863                 goto done;
864
865         state->response.extra_data.data = SMB_REALLOC(
866                 state->response.extra_data.data,
867                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
868
869         if (!state->response.extra_data.data) {
870                 DEBUG(0, ("out of memory\n"));
871                 group_list_ndx = 0;
872                 SAFE_FREE(gr_mem_list);
873                 request_error(state);
874                 return;
875         }
876
877         memcpy(&((char *)state->response.extra_data.data)
878                [group_list_ndx * sizeof(struct winbindd_gr)], 
879                gr_mem_list, gr_mem_list_len);
880
881         state->response.length += gr_mem_list_len;
882
883         DEBUG(10, ("returning %d groups, length = %d\n",
884                    group_list_ndx, gr_mem_list_len));
885
886         /* Out of domains */
887
888  done:
889
890         SAFE_FREE(gr_mem_list);
891
892         if (group_list_ndx > 0)
893                 request_ok(state);
894         else
895                 request_error(state);
896 }
897
898 /* List domain groups without mapping to unix ids */
899
900 void winbindd_list_groups(struct winbindd_cli_state *state)
901 {
902         uint32 total_entries = 0;
903         struct winbindd_domain *domain;
904         const char *which_domain;
905         char *extra_data = NULL;
906         unsigned int extra_data_len = 0, i;
907
908         DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
909
910         /* Ensure null termination */
911         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
912         which_domain = state->request.domain_name;
913         
914         /* Enumerate over trusted domains */
915
916         for (domain = domain_list(); domain; domain = domain->next) {
917                 struct getent_state groups;
918
919                 /* if we have a domain name restricting the request and this
920                    one in the list doesn't match, then just bypass the remainder
921                    of the loop */
922                    
923                 if ( *which_domain && !strequal(which_domain, domain->name) )
924                         continue;
925                         
926                 ZERO_STRUCT(groups);
927
928                 /* Get list of sam groups */
929                 
930                 fstrcpy(groups.domain_name, domain->name);
931
932                 get_sam_group_entries(&groups);
933                         
934                 if (groups.num_sam_entries == 0) {
935                         /* this domain is empty or in an error state */
936                         continue;
937                 }
938
939                 /* keep track the of the total number of groups seen so 
940                    far over all domains */
941                 total_entries += groups.num_sam_entries;
942                 
943                 /* Allocate some memory for extra data.  Note that we limit
944                    account names to sizeof(fstring) = 128 characters.  */               
945                 extra_data = (char *)SMB_REALLOC(
946                         extra_data, sizeof(fstring) * total_entries);
947  
948                 if (!extra_data) {
949                         DEBUG(0,("failed to enlarge buffer!\n"));
950                         request_error(state);
951                         return;
952                 }
953
954                 /* Pack group list into extra data fields */
955                 for (i = 0; i < groups.num_sam_entries; i++) {
956                         char *group_name = ((struct acct_info *)
957                                             groups.sam_entries)[i].acct_name; 
958                         fstring name;
959
960                         fill_domain_username(name, domain->name, group_name, True);
961                         /* Append to extra data */                      
962                         memcpy(&extra_data[extra_data_len], name, 
963                                strlen(name));
964                         extra_data_len += strlen(name);
965                         extra_data[extra_data_len++] = ',';
966                 }
967
968                 SAFE_FREE(groups.sam_entries);
969         }
970
971         /* Assign extra_data fields in response structure */
972         if (extra_data) {
973                 extra_data[extra_data_len - 1] = '\0';
974                 state->response.extra_data.data = extra_data;
975                 state->response.length += extra_data_len;
976         }
977
978         /* No domains may have responded but that's still OK so don't
979            return an error. */
980
981         request_ok(state);
982 }
983
984 /* Get user supplementary groups.  This is much quicker than trying to
985    invert the groups database.  We merge the groups from the gids and
986    other_sids info3 fields as trusted domain, universal group
987    memberships, and nested groups (win2k native mode only) are not
988    returned by the getgroups RPC call but are present in the info3. */
989
990 struct getgroups_state {
991         struct winbindd_cli_state *state;
992         struct winbindd_domain *domain;
993         char *domname;
994         char *username;
995         DOM_SID user_sid;
996
997         const DOM_SID *token_sids;
998         size_t i, num_token_sids;
999
1000         gid_t *token_gids;
1001         size_t num_token_gids;
1002 };
1003
1004 static void getgroups_usersid_recv(void *private_data, BOOL success,
1005                                    const DOM_SID *sid, enum lsa_SidType type);
1006 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1007                                      DOM_SID *token_sids, size_t num_token_sids);
1008 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
1009
1010 void winbindd_getgroups(struct winbindd_cli_state *state)
1011 {
1012         struct getgroups_state *s;
1013
1014         /* Ensure null termination */
1015         state->request.data.username
1016                 [sizeof(state->request.data.username)-1]='\0';
1017
1018         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1019                   state->request.data.username));
1020
1021         /* Parse domain and username */
1022
1023         s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1024         if (s == NULL) {
1025                 DEBUG(0, ("talloc failed\n"));
1026                 request_error(state);
1027                 return;
1028         }
1029
1030         s->state = state;
1031
1032         if (!parse_domain_user_talloc(state->mem_ctx,
1033                                       state->request.data.username,
1034                                       &s->domname, &s->username)) {
1035                 DEBUG(5, ("Could not parse domain user: %s\n",
1036                           state->request.data.username));
1037
1038                 /* error out if we do not have nested group support */
1039
1040                 if ( !lp_winbind_nested_groups() ) {
1041                         request_error(state);
1042                         return;
1043                 }
1044
1045                 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1046                 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1047         }
1048         
1049         /* Get info for the domain */
1050
1051         s->domain = find_domain_from_name_noinit(s->domname);
1052
1053         if (s->domain == NULL) {
1054                 DEBUG(7, ("could not find domain entry for domain %s\n", 
1055                           s->domname));
1056                 request_error(state);
1057                 return;
1058         }
1059
1060         if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1061                 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting "
1062                          "getgroups() for %s\\%s.\n", s->domname,
1063                          s->username));
1064                 request_error(state);
1065                 return;
1066         }       
1067
1068         /* Get rid and name type from name.  The following costs 1 packet */
1069
1070         winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1071                                   getgroups_usersid_recv, s);
1072 }
1073
1074 static void getgroups_usersid_recv(void *private_data, BOOL success,
1075                                    const DOM_SID *sid, enum lsa_SidType type)
1076 {
1077         struct getgroups_state *s =
1078                 (struct getgroups_state *)private_data;
1079
1080         if ((!success) ||
1081             ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1082                 request_error(s->state);
1083                 return;
1084         }
1085
1086         sid_copy(&s->user_sid, sid);
1087
1088         winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1089                                 getgroups_tokensids_recv, s);
1090 }
1091
1092 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1093                                      DOM_SID *token_sids, size_t num_token_sids)
1094 {
1095         struct getgroups_state *s =
1096                 (struct getgroups_state *)private_data;
1097
1098         /* We need at least the user sid and the primary group in the token,
1099          * otherwise it's an error */
1100
1101         if ((!success) || (num_token_sids < 2)) {
1102                 request_error(s->state);
1103                 return;
1104         }
1105
1106         s->token_sids = token_sids;
1107         s->num_token_sids = num_token_sids;
1108         s->i = 0;
1109
1110         s->token_gids = NULL;
1111         s->num_token_gids = 0;
1112
1113         getgroups_sid2gid_recv(s, False, 0);
1114 }
1115
1116 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
1117 {
1118         struct getgroups_state *s =
1119                 (struct getgroups_state *)private_data;
1120
1121         if (success)
1122                 add_gid_to_array_unique(NULL, gid,
1123                                         &s->token_gids,
1124                                         &s->num_token_gids);
1125
1126         if (s->i < s->num_token_sids) {
1127                 const DOM_SID *sid = &s->token_sids[s->i];
1128                 s->i += 1;
1129
1130                 if (sid_equal(sid, &s->user_sid)) {
1131                         getgroups_sid2gid_recv(s, False, 0);
1132                         return;
1133                 }
1134
1135                 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1136                                        getgroups_sid2gid_recv, s);
1137                 return;
1138         }
1139
1140         s->state->response.data.num_entries = s->num_token_gids;
1141         s->state->response.extra_data.data = s->token_gids;
1142         s->state->response.length += s->num_token_gids * sizeof(gid_t);
1143         request_ok(s->state);
1144 }
1145
1146 /* Get user supplementary sids. This is equivalent to the
1147    winbindd_getgroups() function but it involves a SID->SIDs mapping
1148    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1149    idmap. This call is designed to be used with applications that need
1150    to do ACL evaluation themselves. Note that the cached info3 data is
1151    not used 
1152
1153    this function assumes that the SID that comes in is a user SID. If
1154    you pass in another type of SID then you may get unpredictable
1155    results.
1156 */
1157
1158 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1159                              size_t num_sids);
1160
1161 void winbindd_getusersids(struct winbindd_cli_state *state)
1162 {
1163         DOM_SID *user_sid;
1164
1165         /* Ensure null termination */
1166         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1167
1168         user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1169         if (user_sid == NULL) {
1170                 DEBUG(1, ("talloc failed\n"));
1171                 request_error(state);
1172                 return;
1173         }
1174
1175         if (!string_to_sid(user_sid, state->request.data.sid)) {
1176                 DEBUG(1, ("Could not get convert sid %s from string\n",
1177                           state->request.data.sid));
1178                 request_error(state);
1179                 return;
1180         }
1181
1182         winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1183                                 state);
1184 }
1185
1186 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1187                              size_t num_sids)
1188 {
1189         struct winbindd_cli_state *state =
1190                 (struct winbindd_cli_state *)private_data;
1191         char *ret = NULL;
1192         unsigned ofs, ret_size = 0;
1193         size_t i;
1194
1195         if (!success) {
1196                 request_error(state);
1197                 return;
1198         }
1199
1200         /* work out the response size */
1201         for (i = 0; i < num_sids; i++) {
1202                 const char *s = sid_string_static(&sids[i]);
1203                 ret_size += strlen(s) + 1;
1204         }
1205
1206         /* build the reply */
1207         ret = (char *)SMB_MALLOC(ret_size);
1208         if (!ret) {
1209                 DEBUG(0, ("malloc failed\n"));
1210                 request_error(state);
1211                 return;
1212         }
1213         ofs = 0;
1214         for (i = 0; i < num_sids; i++) {
1215                 const char *s = sid_string_static(&sids[i]);
1216                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1217                 ofs += strlen(ret+ofs) + 1;
1218         }
1219
1220         /* Send data back to client */
1221         state->response.data.num_entries = num_sids;
1222         state->response.extra_data.data = ret;
1223         state->response.length += ret_size;
1224         request_ok(state);
1225 }
1226
1227 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1228 {
1229         DOM_SID user_sid;
1230         struct winbindd_domain *domain;
1231
1232         /* Ensure null termination */
1233         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1234
1235         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1236                 DEBUG(1, ("Could not get convert sid %s from string\n",
1237                           state->request.data.sid));
1238                 request_error(state);
1239                 return;
1240         }
1241
1242         /* Get info for the domain */   
1243         if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1244                 DEBUG(0,("could not find domain entry for sid %s\n", 
1245                          sid_string_static(&user_sid)));
1246                 request_error(state);
1247                 return;
1248         }
1249
1250         sendto_domain(state, domain);
1251 }
1252
1253 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1254                                                     struct winbindd_cli_state *state)
1255 {
1256         DOM_SID user_sid;
1257         NTSTATUS status;
1258
1259         char *sidstring;
1260         ssize_t len;
1261         DOM_SID *groups;
1262         uint32 num_groups;
1263
1264         /* Ensure null termination */
1265         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1266
1267         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1268                 DEBUG(1, ("Could not get convert sid %s from string\n",
1269                           state->request.data.sid));
1270                 return WINBINDD_ERROR;
1271         }
1272
1273         status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1274                                                     &user_sid, &num_groups,
1275                                                     &groups);
1276         if (!NT_STATUS_IS_OK(status))
1277                 return WINBINDD_ERROR;
1278
1279         if (num_groups == 0) {
1280                 state->response.data.num_entries = 0;
1281                 state->response.extra_data.data = NULL;
1282                 return WINBINDD_OK;
1283         }
1284
1285         if (!print_sidlist(NULL, groups, num_groups, &sidstring, &len)) {
1286                 DEBUG(0, ("malloc failed\n"));
1287                 return WINBINDD_ERROR;
1288         }
1289
1290         state->response.extra_data.data = sidstring;
1291         state->response.length += len+1;
1292         state->response.data.num_entries = num_groups;
1293
1294         return WINBINDD_OK;
1295 }