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