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