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