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