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