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