d5d3accec10d452055c5354ae8b5c2e5fa5f65f9
[jra/samba/.git] / source3 / winbindd / 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 3 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, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27
28 extern bool opt_nocache;
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
32
33 static void add_member(const char *domain, const char *user,
34            char **pp_members, size_t *p_num_members)
35 {
36         fstring name;
37
38         fill_domain_username(name, domain, user, True);
39         safe_strcat(name, ",", sizeof(name)-1);
40         string_append(pp_members, name);
41         *p_num_members += 1;
42 }
43
44 /**********************************************************************
45  Add member users resulting from sid. Expand if it is a domain group.
46 **********************************************************************/
47
48 static void add_expanded_sid(const DOM_SID *sid, char **pp_members, size_t *p_num_members)
49 {
50         DOM_SID dom_sid;
51         uint32 rid;
52         struct winbindd_domain *domain;
53         size_t i;
54
55         char *domain_name = NULL;
56         char *name = NULL;
57         enum lsa_SidType type;
58
59         uint32 num_names;
60         DOM_SID *sid_mem;
61         char **names;
62         uint32 *types;
63
64         NTSTATUS result;
65
66         TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
67
68         if (mem_ctx == NULL) {
69                 DEBUG(1, ("talloc_init failed\n"));
70                 return;
71         }
72
73         sid_copy(&dom_sid, sid);
74         sid_split_rid(&dom_sid, &rid);
75
76         domain = find_lookup_domain_from_sid(sid);
77
78         if (domain == NULL) {
79                 DEBUG(3, ("Could not find domain for sid %s\n",
80                           sid_string_dbg(sid)));
81                 goto done;
82         }
83
84         result = domain->methods->sid_to_name(domain, mem_ctx, sid,
85                                               &domain_name, &name, &type);
86
87         if (!NT_STATUS_IS_OK(result)) {
88                 DEBUG(3, ("sid_to_name failed for sid %s\n",
89                           sid_string_dbg(sid)));
90                 goto done;
91         }
92
93         DEBUG(10, ("Found name %s, type %d\n", name, type));
94
95         if (type == SID_NAME_USER) {
96                 add_member(domain_name, name, pp_members, p_num_members);
97                 goto done;
98         }
99
100         if (type != SID_NAME_DOM_GRP) {
101                 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
102                            name));
103                 goto done;
104         }
105
106         /* Expand the domain group, this must be done via the target domain */
107
108         domain = find_domain_from_sid(sid);
109
110         if (domain == NULL) {
111                 DEBUG(3, ("Could not find domain from SID %s\n",
112                           sid_string_dbg(sid)));
113                 goto done;
114         }
115
116         result = domain->methods->lookup_groupmem(domain, mem_ctx,
117                                                   sid, &num_names,
118                                                   &sid_mem, &names,
119                                                   &types);
120
121         if (!NT_STATUS_IS_OK(result)) {
122                 DEBUG(10, ("Could not lookup group members for %s: %s\n",
123                            name, nt_errstr(result)));
124                 goto done;
125         }
126
127         for (i=0; i<num_names; i++) {
128                 DEBUG(10, ("Adding group member SID %s\n",
129                            sid_string_dbg(&sid_mem[i])));
130
131                 if (types[i] != SID_NAME_USER) {
132                         DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
133                                   "Ignoring.\n", names[i], name));
134                         continue;
135                 }
136
137                 add_member(domain->name, names[i], pp_members, p_num_members);
138         }
139
140  done:
141         talloc_destroy(mem_ctx);
142         return;
143 }
144
145 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
146                              DOM_SID *group_sid, 
147                              size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
148 {
149         DOM_SID *members;
150         size_t i, num_members;
151
152         *num_gr_mem = 0;
153         *gr_mem = NULL;
154         *gr_mem_len = 0;
155
156         if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
157                                                &num_members)))
158                 return True;
159
160         for (i=0; i<num_members; i++) {
161                 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
162         }
163
164         TALLOC_FREE(members);
165
166         if (*gr_mem != NULL) {
167                 size_t len;
168
169                 /* We have at least one member, strip off the last "," */
170                 len = strlen(*gr_mem);
171                 (*gr_mem)[len-1] = '\0';
172                 *gr_mem_len = len;
173         }
174
175         return True;
176 }
177
178 /* Fill a grent structure from various other information */
179
180 static bool fill_grent(struct winbindd_gr *gr, const char *dom_name, 
181                        const char *gr_name, gid_t unix_gid)
182 {
183         fstring full_group_name;
184
185         fill_domain_username( full_group_name, dom_name, gr_name, True );
186
187         gr->gr_gid = unix_gid;
188     
189         /* Group name and password */
190     
191         safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
192         safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
193
194         return True;
195 }
196
197 /***********************************************************************
198  If "enum users" is set to false, and the group being looked
199  up is the Domain Users SID: S-1-5-domain-513, then for the
200  list of members check if the querying user is in that group,
201  and if so only return that user as the gr_mem array.
202  We can change this to a different parameter than "enum users"
203  if neccessaey, or parameterize the group list we do this for.
204 ***********************************************************************/
205
206 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
207                                      struct winbindd_domain *domain,
208                                      struct winbindd_cli_state *state,
209                                      DOM_SID *group_sid,
210                                      enum lsa_SidType group_name_type,
211                                      size_t *num_gr_mem, char **gr_mem, 
212                                      size_t *gr_mem_len)
213 {
214         DOM_SID querying_user_sid;
215         DOM_SID *pquerying_user_sid = NULL;
216         uint32 num_groups = 0;
217         DOM_SID *user_sids = NULL;
218         bool u_in_group = False;
219         NTSTATUS status;
220         int i;
221         unsigned int buf_len = 0;
222         char *buf = NULL;
223
224         DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
225                   domain->name ));
226
227         if (state) {
228                 uid_t ret_uid = (uid_t)-1;
229                 if (sys_getpeereid(state->sock, &ret_uid)==0) {
230                         /* We know who's asking - look up their SID if
231                            it's one we've mapped before. */
232                         status = idmap_uid_to_sid(&querying_user_sid, ret_uid);
233                         if (NT_STATUS_IS_OK(status)) {
234                                 pquerying_user_sid = &querying_user_sid;
235                                 DEBUG(10,("fill_grent_mem_domain_users: querying uid %u -> %s\n",
236                                           (unsigned int)ret_uid,
237                                           sid_string_dbg(pquerying_user_sid)));
238                         }
239                 }
240         }
241
242         /* Only look up if it was a winbindd user in this domain. */
243         if (pquerying_user_sid &&
244             (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
245                 
246                 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
247                           sid_string_dbg(pquerying_user_sid) ));
248                 
249                 status = domain->methods->lookup_usergroups(domain,
250                                                             mem_ctx,
251                                                             pquerying_user_sid,
252                                                             &num_groups,
253                                                             &user_sids);
254                 if (!NT_STATUS_IS_OK(status)) {
255                         DEBUG(1, ("fill_grent_mem_domain_users: lookup_usergroups failed "
256                                   "for sid %s in domain %s (error: %s)\n", 
257                                   sid_string_dbg(pquerying_user_sid),
258                                   domain->name,
259                                   nt_errstr(status)));
260                         return False;                   
261                 }
262
263                 for (i = 0; i < num_groups; i++) {
264                         if (sid_equal(group_sid, &user_sids[i])) {
265                                 /* User is in Domain Users, add their name
266                                    as the only group member. */
267                                 u_in_group = True;
268                                 break;
269                         }
270                 }
271         }
272         
273         if (u_in_group) {
274                 size_t len = 0;
275                 char *domainname = NULL;
276                 char *username = NULL;
277                 fstring name;
278                 enum lsa_SidType type;
279                 
280                 DEBUG(10,("fill_grent_mem_domain_users: sid %s in 'Domain Users' in domain %s\n",
281                           sid_string_dbg(pquerying_user_sid),
282                           domain->name ));
283                 
284                 status = domain->methods->sid_to_name(domain, mem_ctx,
285                                                       pquerying_user_sid,
286                                                       &domainname,
287                                                       &username,
288                                                       &type);
289                 if (!NT_STATUS_IS_OK(status)) {
290                         DEBUG(1, ("could not lookup username for user "
291                                   "sid %s in domain %s (error: %s)\n", 
292                                   sid_string_dbg(pquerying_user_sid),
293                                   domain->name,
294                                   nt_errstr(status)));
295                         return False;                   
296                 }
297                 fill_domain_username(name, domain->name, username, True);
298                 len = strlen(name);
299                 buf_len = len + 1;
300                 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
301                         DEBUG(1, ("out of memory\n"));
302                         return False;                   
303                 }
304                 memcpy(buf, name, buf_len);
305                 
306                 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
307                           "'Domain Users' in domain %s\n",
308                           name, domain->name ));
309                 
310                 /* user is the only member */
311                 *num_gr_mem = 1;
312         }
313                 
314         *gr_mem = buf;
315         *gr_mem_len = buf_len;
316         
317         DEBUG(10, ("fill_grent_mem_domain_users: num_mem = %u, len = %u, mem = %s\n", 
318                    (unsigned int)*num_gr_mem, 
319                    (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); 
320
321         return True;
322 }                                        
323
324 /***********************************************************************
325  Add names to a list.  Assumes  a canonical version of the string
326  in DOMAIN\user
327 ***********************************************************************/
328
329 static int namecmp( const void *a, const void *b )
330 {
331         return StrCaseCmp( * (char * const *) a, * (char * const *) b); 
332 }
333
334 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx, 
335                                    char ***list, uint32 *n_list, 
336                                    char **names, uint32 n_names )
337 {
338         char **new_list = NULL; 
339         uint32 n_new_list = 0;  
340         int i, j;       
341
342         if ( !names || (n_names == 0) )
343                 return NT_STATUS_OK;
344         
345         /* Alloc the maximum size we'll need */
346
347         if ( *list == NULL ) {
348                 if ( (new_list = TALLOC_ARRAY( ctx, char *, n_names )) == NULL ) 
349                         return NT_STATUS_NO_MEMORY;
350                 n_new_list = n_names;           
351         } else {
352                 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *, 
353                                                  (*n_list) + n_names );
354                 if ( !new_list )
355                         return NT_STATUS_NO_MEMORY;
356                 n_new_list = (*n_list) + n_names;
357         }
358
359         /* Add all names */
360
361         for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
362                 new_list[i] = talloc_strdup( new_list, names[j] );
363         }
364
365         /* search for duplicates for sorting and looking for matching
366            neighbors */
367         
368         qsort( new_list, n_new_list, sizeof(char*), QSORT_CAST namecmp );
369         
370         for ( i=1; i<n_new_list; i++ ) {
371                 if ( strcmp( new_list[i-1], new_list[i] ) == 0 ) {                      
372                         memmove( &new_list[i-1], &new_list[i], 
373                                  sizeof(char*)*(n_new_list-i) );
374                         n_new_list--;
375                 }
376         }
377
378         *list = new_list;
379         *n_list = n_new_list;   
380
381         return NT_STATUS_OK;    
382 }
383
384 /***********************************************************************
385 ***********************************************************************/
386
387 static NTSTATUS expand_groups( TALLOC_CTX *ctx, 
388                                struct winbindd_domain *d,
389                                DOM_SID *glist, uint32 n_glist,
390                                DOM_SID **new_glist, uint32 *n_new_glist,
391                                char ***members, uint32 *n_members )
392 {
393         int i, j;       
394         NTSTATUS status = NT_STATUS_OK;
395         uint32 num_names = 0;
396         uint32 *name_types = NULL;
397         char **names = NULL;
398         DOM_SID *sid_mem = NULL;
399         TALLOC_CTX *tmp_ctx = NULL;
400         DOM_SID *new_groups = NULL;
401         size_t new_groups_size = 0;     
402         
403         *members = NULL;
404         *n_members = 0; 
405         *new_glist = NULL;
406         *n_new_glist = 0;       
407         
408         for ( i=0; i<n_glist; i++ ) {
409                 tmp_ctx = talloc_new( ctx );
410
411                 /* Lookup the group membership */
412
413                 status = d->methods->lookup_groupmem(d, tmp_ctx, 
414                                                      &glist[i], &num_names,
415                                                      &sid_mem, &names, 
416                                                      &name_types);
417                 if ( !NT_STATUS_IS_OK(status) ) 
418                         goto out;
419                                 
420                 /* Separate users and groups into two lists */
421
422                 for ( j=0; j<num_names; j++ ) {
423
424                         /* Users */
425                         if ( name_types[j] == SID_NAME_USER ||
426                              name_types[j] == SID_NAME_COMPUTER )
427                         {
428                                 status = add_names_to_list( ctx, members, 
429                                                             n_members,
430                                                             names+j, 1 );
431                                 if ( !NT_STATUS_IS_OK(status) )
432                                         goto out;
433
434                                 continue;                               
435                         } 
436
437                         /* Groups */
438                         if ( name_types[j] == SID_NAME_DOM_GRP ||
439                              name_types[j] == SID_NAME_ALIAS )
440                         {
441                                 status = add_sid_to_array_unique(ctx,
442                                                                  &sid_mem[j],
443                                                                  &new_groups,
444                                                                  &new_groups_size);
445                                 if (NT_STATUS_IS_OK(status)) {
446                                         goto out;
447                                 }
448
449                                 continue;
450                         }
451                 }
452
453                 TALLOC_FREE( tmp_ctx );
454         }
455
456         *new_glist = new_groups;
457         *n_new_glist = (uint32)new_groups_size; 
458         
459  out:
460         TALLOC_FREE( tmp_ctx );
461         
462         return status;  
463 }
464
465 /***********************************************************************
466  Fill in the group membership field of a NT group given by group_sid
467 ***********************************************************************/
468
469 static bool fill_grent_mem(struct winbindd_domain *domain,
470                            struct winbindd_cli_state *state,
471                            DOM_SID *group_sid, 
472                            enum lsa_SidType group_name_type, 
473                            size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
474 {
475         uint32 num_names = 0;
476         unsigned int buf_len = 0, buf_ndx = 0, i;
477         char **names = NULL, *buf = NULL;
478         bool result = False;
479         TALLOC_CTX *mem_ctx;
480         uint32 group_rid;
481         DOM_SID *glist = NULL;
482         DOM_SID *new_glist = NULL;
483         uint32 n_glist, n_new_glist;    
484         int max_depth = lp_winbind_expand_groups();     
485
486         if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
487                 return False;
488
489         DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
490
491         /* Initialize with no members */
492
493         *num_gr_mem = 0;
494
495         /* HACK ALERT!! This whole routine does not cope with group members
496          * from more than one domain, ie aliases. Thus we have to work it out
497          * ourselves in a special routine. */
498
499         if (domain->internal) {
500                 result = fill_passdb_alias_grmem(domain, group_sid,
501                                                num_gr_mem,
502                                                gr_mem, gr_mem_len);
503                 goto done;
504         }
505
506         /* Verify name type */
507
508         if ( !((group_name_type==SID_NAME_DOM_GRP) ||
509                ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
510         {
511                 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", 
512                           sid_string_dbg(group_sid),
513                           domain->name, group_name_type));
514                 goto done;
515         }
516
517         /* OPTIMIZATION / HACK. See comment in
518            fill_grent_mem_domusers() */
519
520         sid_peek_rid( group_sid, &group_rid );
521         if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
522                 result = fill_grent_mem_domusers( mem_ctx, domain, state, 
523                                                   group_sid, group_name_type,
524                                                   num_gr_mem, gr_mem,
525                                                   gr_mem_len );
526                 goto done;              
527         }
528
529         /* Real work goes here.  Create a list of group names to
530            expand startign with the initial one.  Pass that to 
531            expand_groups() which returns a list of more group names
532            to expand.  Do this up to the max search depth. */
533
534         if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
535                 result = False;
536                 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
537                 goto done;
538         }
539         sid_copy( &glist[0], group_sid );       
540         n_glist = 1;    
541
542         for ( i=0; i<max_depth && glist; i++ ) {
543                 uint32 n_members = 0;
544                 char **members = NULL;
545                 NTSTATUS nt_status;             
546
547                 nt_status = expand_groups( mem_ctx, domain,
548                                            glist, n_glist, 
549                                            &new_glist, &n_new_glist,
550                                            &members, &n_members);
551                 if ( !NT_STATUS_IS_OK(nt_status) ) {
552                         result = False;                 
553                         goto done;
554                 }               
555                 
556                 /* Add new group members to list */
557
558                 nt_status = add_names_to_list( mem_ctx, &names, &num_names, 
559                                                members, n_members );
560                 if ( !NT_STATUS_IS_OK(nt_status) ) {
561                         result = False;                 
562                         goto done;
563                 }
564                 
565                 TALLOC_FREE( members );         
566
567                 /* If we have no more groups to expand, break out
568                    early */
569
570                 if (new_glist == NULL)
571                         break;
572
573                 /* One more round */
574                 TALLOC_FREE(glist);
575                 glist = new_glist;
576                 n_glist = n_new_glist;
577         }
578         TALLOC_FREE( glist );   
579          
580         DEBUG(10, ("looked up %d names\n", num_names));
581
582  again:
583         /* Add members to list */
584
585         for (i = 0; i < num_names; i++) {
586                 int len;
587                         
588                 DEBUG(10, ("processing name %s\n", names[i]));
589
590                 len = strlen(names[i]);
591                 
592                 /* Add to list or calculate buffer length */
593
594                 if (!buf) {
595                         buf_len += len + 1; /* List is comma separated */
596                         (*num_gr_mem)++;
597                         DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
598                 } else {
599                         DEBUG(10, ("appending %s at ndx %d\n", names[i], buf_ndx));
600                         safe_strcpy(&buf[buf_ndx], names[i], len);
601                         buf_ndx += len;
602                         buf[buf_ndx] = ',';
603                         buf_ndx++;
604                 }
605         }
606
607         /* Allocate buffer */
608
609         if (!buf && buf_len != 0) {
610                 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
611                         DEBUG(1, ("out of memory\n"));
612                         result = False;
613                         goto done;
614                 }
615                 memset(buf, 0, buf_len);
616                 goto again;
617         }
618
619         /* Now we're done */
620
621         if (buf && buf_ndx > 0) {
622                 buf[buf_ndx - 1] = '\0';
623         }
624
625         *gr_mem = buf;
626         *gr_mem_len = buf_len;
627
628         DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem, 
629                    (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); 
630         result = True;
631
632 done:
633
634         talloc_destroy(mem_ctx);
635         
636         DEBUG(10, ("fill_grent_mem returning %d\n", result));
637
638         return result;
639 }
640
641 static void winbindd_getgrsid( struct winbindd_cli_state *state, DOM_SID group_sid );
642
643 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid, 
644                            enum lsa_SidType type )
645 {
646         struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
647         
648         if (!success) {
649                 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
650                 request_error(state);
651                 return;
652         }
653
654         if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
655                 DEBUG(5,("getgrnam_recv: not a group!\n"));
656                 request_error(state);
657                 return;
658         }       
659
660         winbindd_getgrsid( state, *sid );
661 }
662
663         
664 /* Return a group structure from a group name */
665
666 void winbindd_getgrnam(struct winbindd_cli_state *state)
667 {
668         struct winbindd_domain *domain;
669         fstring name_domain, name_group;
670         char *tmp;
671         
672         /* Ensure null termination */
673         state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
674
675         DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
676                   state->request.data.groupname));
677
678         /* Parse domain and groupname */
679         
680         memset(name_group, 0, sizeof(fstring));
681
682         tmp = state->request.data.groupname;
683
684         name_domain[0] = '\0';
685         name_group[0] = '\0';
686         
687         parse_domain_user(tmp, name_domain, name_group);
688
689         /* if no domain or our local domain and no local tdb group, default to
690          * our local domain for aliases */
691
692         if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
693                 fstrcpy(name_domain, get_global_sam_name());
694         }
695
696         /* Get info for the domain */
697
698         if ((domain = find_domain_from_name(name_domain)) == NULL) {
699                 DEBUG(3, ("could not get domain sid for domain %s\n",
700                           name_domain));
701                 request_error(state);
702                 return;
703         }
704         /* should we deal with users for our domain? */
705         
706         if ( lp_winbind_trusted_domains_only() && domain->primary) {
707                 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
708                          "getgrnam() for %s\\%s.\n", name_domain, name_group));
709                 request_error(state);
710                 return;
711         }
712
713         /* Get rid and name type from name */
714
715         ws_name_replace( name_group, WB_REPLACE_CHAR );
716         
717         winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
718                                    getgrnam_recv, WINBINDD_GETGRNAM, state );
719 }
720
721 struct getgrsid_state {
722         struct winbindd_cli_state *state;
723         struct winbindd_domain *domain;
724         char *group_name;
725         enum lsa_SidType group_type;    
726         uid_t gid;
727         DOM_SID group_sid;
728 };
729
730 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
731         {
732         struct getgrsid_state *s =
733                 (struct getgrsid_state *)private_data;
734         struct winbindd_domain *domain;
735         size_t gr_mem_len;
736         size_t num_gr_mem;
737         char *gr_mem;
738         fstring dom_name, group_name;   
739
740         if (!success) {
741                 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
742                 request_error(s->state);
743                 return;
744         }
745
746         s->gid = gid;
747
748         if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
749                 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
750                 request_error(s->state);
751                 return;
752         }
753
754         
755         /* Fill in group structure */
756
757         if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
758                 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
759                 request_error(s->state);
760                 return;
761         }
762
763         if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) ||
764             !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
765                             &num_gr_mem, &gr_mem, &gr_mem_len)) 
766         {
767                 request_error(s->state);
768                 return;
769         }
770
771         s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
772
773         /* Group membership lives at start of extra data */
774
775         s->state->response.data.gr.gr_mem_ofs = 0;
776
777         s->state->response.length += gr_mem_len;
778         s->state->response.extra_data.data = gr_mem;
779
780         request_ok(s->state);   
781         }
782
783 static void getgrsid_lookupsid_recv( void *private_data, bool success,
784                                      const char *dom_name, const char *name,
785                                      enum lsa_SidType name_type )
786 {
787         struct getgrsid_state *s = (struct getgrsid_state *)private_data;
788
789         if (!success) {
790                 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
791                 request_error(s->state);
792                 return;
793         }
794
795         /* either it's a domain group, a domain local group, or a
796            local group in an internal domain */
797
798         if ( !( (name_type==SID_NAME_DOM_GRP) ||
799                 ((name_type==SID_NAME_ALIAS) && 
800                  (s->domain->primary || s->domain->internal)) ) )
801         {
802                 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n", 
803                           dom_name, name, name_type));
804                 request_error(s->state);
805                 return;
806 }
807
808         if ( (s->group_name = talloc_asprintf( s->state->mem_ctx, 
809                                                "%s\\%s", 
810                                                dom_name, name )) == NULL )
811 {
812                 DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
813                 request_error(s->state);
814                 return;
815         }
816
817         s->group_type = name_type;
818
819         winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
820                                getgrsid_sid2gid_recv, s);
821         }
822
823 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
824         {
825         struct getgrsid_state *s;
826
827         if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
828                 DEBUG(0, ("talloc failed\n"));
829                 request_error(state);
830                 return;
831         }
832
833         s->state = state;
834
835         if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
836                 DEBUG(3, ("Could not find domain for sid %s\n",
837                           sid_string_dbg(&group_sid)));
838                 request_error(state);
839                 return;
840         }
841
842         sid_copy(&s->group_sid, &group_sid);
843
844         winbindd_lookupsid_async( s->state->mem_ctx,  &group_sid, 
845                                   getgrsid_lookupsid_recv, s );
846 }
847
848
849 static void getgrgid_recv(void *private_data, bool success, const char *sid)
850 {
851         struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
852         enum lsa_SidType name_type;
853         DOM_SID group_sid;
854
855         if (success) {
856                 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
857                           (unsigned long)(state->request.data.gid), sid));
858
859                 string_to_sid(&group_sid, sid);
860                 winbindd_getgrsid(state, group_sid);
861                 return;
862         }
863
864         /* Ok, this might be "ours", i.e. an alias */
865         if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
866             lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
867             (name_type == SID_NAME_ALIAS)) {
868                 /* Hey, got an alias */
869                 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
870                           (unsigned long)(state->request.data.gid), sid));
871                 winbindd_getgrsid(state, group_sid);
872                 return;
873         }
874
875         DEBUG(1, ("could not convert gid %lu to sid\n", 
876                   (unsigned long)state->request.data.gid));
877         request_error(state);
878 }
879
880 /* Return a group structure from a gid number */
881 void winbindd_getgrgid(struct winbindd_cli_state *state)
882 {
883         DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, 
884                   (unsigned long)state->request.data.gid));
885
886         /* always use the async interface */
887         winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state);
888 }
889
890 /*
891  * set/get/endgrent functions
892  */
893
894 /* "Rewind" file pointer for group database enumeration */
895
896 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
897 {
898         struct winbindd_domain *domain;
899
900         DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
901
902         /* Check user has enabled this */
903
904         if (!lp_winbind_enum_groups()) {
905                 return False;
906         }               
907
908         /* Free old static data if it exists */
909         
910         if (state->getgrent_state != NULL) {
911                 free_getent_state(state->getgrent_state);
912                 state->getgrent_state = NULL;
913         }
914         
915         /* Create sam pipes for each domain we know about */
916         
917         for (domain = domain_list(); domain != NULL; domain = domain->next) {
918                 struct getent_state *domain_state;
919                 
920                 /* Create a state record for this domain */
921
922                 /* don't add our domaina if we are a PDC or if we 
923                    are a member of a Samba domain */
924                 
925                 if ( lp_winbind_trusted_domains_only() && domain->primary )
926                 {
927                         continue;
928                 }
929                                                 
930                 
931                 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
932                         DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
933                         return False;
934                 }
935                 
936                 ZERO_STRUCTP(domain_state);
937                 
938                 fstrcpy(domain_state->domain_name, domain->name);
939
940                 /* Add to list of open domains */
941                 
942                 DLIST_ADD(state->getgrent_state, domain_state);
943         }
944         
945         state->getgrent_initialized = True;
946         return True;
947 }
948
949 void winbindd_setgrent(struct winbindd_cli_state *state)
950 {
951         if (winbindd_setgrent_internal(state)) {
952                 request_ok(state);
953         } else {
954                 request_error(state);
955         }
956 }
957
958 /* Close file pointer to ntdom group database */
959
960 void winbindd_endgrent(struct winbindd_cli_state *state)
961 {
962         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
963
964         free_getent_state(state->getgrent_state);
965         state->getgrent_initialized = False;
966         state->getgrent_state = NULL;
967         request_ok(state);
968 }
969
970 /* Get the list of domain groups and domain aliases for a domain.  We fill in
971    the sam_entries and num_sam_entries fields with domain group information.  
972    The dispinfo_ndx field is incremented to the index of the next group to 
973    fetch. Return True if some groups were returned, False otherwise. */
974
975 static bool get_sam_group_entries(struct getent_state *ent)
976 {
977         NTSTATUS status;
978         uint32 num_entries;
979         struct acct_info *name_list = NULL;
980         TALLOC_CTX *mem_ctx;
981         bool result = False;
982         struct acct_info *sam_grp_entries = NULL;
983         struct winbindd_domain *domain;
984         
985         if (ent->got_sam_entries)
986                 return False;
987
988         if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
989                                           ent->domain_name))) {
990                 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); 
991                 return False;
992         }
993                 
994         /* Free any existing group info */
995
996         SAFE_FREE(ent->sam_entries);
997         ent->num_sam_entries = 0;
998         ent->got_sam_entries = True;
999
1000         /* Enumerate domain groups */
1001
1002         num_entries = 0;
1003
1004         if (!(domain = find_domain_from_name(ent->domain_name))) {
1005                 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
1006                 goto done;
1007         }
1008
1009         /* always get the domain global groups */
1010
1011         status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1012         
1013         if (!NT_STATUS_IS_OK(status)) {
1014                 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
1015                 result = False;
1016                 goto done;
1017         }
1018
1019         /* Copy entries into return buffer */
1020
1021         if (num_entries) {
1022                 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
1023                         DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", 
1024                                 num_entries));
1025                         result = False;
1026                         goto done;
1027                 }
1028                 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
1029         }
1030         
1031         ent->num_sam_entries = num_entries;
1032         
1033         /* get the domain local groups if we are a member of a native win2k domain
1034            and are not using LDAP to get the groups */
1035            
1036         if ( ( lp_security() != SEC_ADS && domain->native_mode 
1037                 && domain->primary) || domain->internal )
1038         {
1039                 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n", 
1040                         domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
1041                 
1042                 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1043                 
1044                 if ( !NT_STATUS_IS_OK(status) ) { 
1045                         DEBUG(3,("get_sam_group_entries: "
1046                                 "Failed to enumerate "
1047                                 "domain local groups with error %s!\n",
1048                                 nt_errstr(status)));
1049                         num_entries = 0;
1050                 }
1051                 else
1052                         DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
1053                 
1054                 /* Copy entries into return buffer */
1055
1056                 if ( num_entries ) {
1057                         if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
1058                         {
1059                                 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", 
1060                                         num_entries));
1061                                 result = False;
1062                                 goto done;
1063                         }
1064                         
1065                         memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, 
1066                                 num_entries * sizeof(struct acct_info) );
1067                 }
1068         
1069                 ent->num_sam_entries += num_entries;
1070         }
1071         
1072                 
1073         /* Fill in remaining fields */
1074
1075         ent->sam_entries = name_list;
1076         ent->sam_entry_index = 0;
1077
1078         result = (ent->num_sam_entries > 0);
1079
1080  done:
1081         talloc_destroy(mem_ctx);
1082
1083         return result;
1084 }
1085
1086 /* Fetch next group entry from ntdom database */
1087
1088 #define MAX_GETGRENT_GROUPS 500
1089
1090 void winbindd_getgrent(struct winbindd_cli_state *state)
1091 {
1092         struct getent_state *ent;
1093         struct winbindd_gr *group_list = NULL;
1094         int num_groups, group_list_ndx, gr_mem_list_len = 0;
1095         char *gr_mem_list = NULL;
1096
1097         DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1098
1099         /* Check user has enabled this */
1100
1101         if (!lp_winbind_enum_groups()) {
1102                 request_error(state);
1103                 return;
1104         }
1105
1106         num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1107
1108         if (num_groups == 0) {
1109                 request_error(state);
1110                 return;
1111         }
1112
1113         if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
1114                 request_error(state);
1115                 return;
1116         }
1117
1118         memset(state->response.extra_data.data, '\0',
1119                 num_groups * sizeof(struct winbindd_gr) );
1120
1121         state->response.data.num_entries = 0;
1122
1123         group_list = (struct winbindd_gr *)state->response.extra_data.data;
1124
1125         if (!state->getgrent_initialized)
1126                 winbindd_setgrent_internal(state);
1127
1128         if (!(ent = state->getgrent_state)) {
1129                 request_error(state);
1130                 return;
1131         }
1132
1133         /* Start sending back groups */
1134
1135         for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1136                 struct acct_info *name_list = NULL;
1137                 fstring domain_group_name;
1138                 uint32 result;
1139                 gid_t group_gid;
1140                 size_t gr_mem_len;
1141                 char *gr_mem;
1142                 DOM_SID group_sid;
1143                 struct winbindd_domain *domain;
1144                                 
1145                 /* Do we need to fetch another chunk of groups? */
1146
1147         tryagain:
1148
1149                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1150                            ent->sam_entry_index, ent->num_sam_entries));
1151
1152                 if (ent->num_sam_entries == ent->sam_entry_index) {
1153
1154                         while(ent && !get_sam_group_entries(ent)) {
1155                                 struct getent_state *next_ent;
1156
1157                                 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 
1158
1159                                 /* Free state information for this domain */
1160
1161                                 SAFE_FREE(ent->sam_entries);
1162
1163                                 next_ent = ent->next;
1164                                 DLIST_REMOVE(state->getgrent_state, ent);
1165                                 
1166                                 SAFE_FREE(ent);
1167                                 ent = next_ent;
1168                         }
1169
1170                         /* No more domains */
1171
1172                         if (!ent) 
1173                                 break;
1174                 }
1175                 
1176                 name_list = (struct acct_info *)ent->sam_entries;
1177                 
1178                 if (!(domain = 
1179                       find_domain_from_name(ent->domain_name))) {
1180                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
1181                         result = False;
1182                         goto done;
1183                 }
1184
1185                 /* Lookup group info */
1186                 
1187                 sid_copy(&group_sid, &domain->sid);
1188                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1189
1190                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) {
1191                         union unid_t id;
1192                         enum lsa_SidType type;
1193
1194                         DEBUG(10, ("SID %s not in idmap\n",
1195                                    sid_string_dbg(&group_sid)));
1196
1197                         if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1198                                 DEBUG(1, ("could not look up gid for group "
1199                                           "%s\n", 
1200                                           name_list[ent->sam_entry_index].acct_name));
1201                                 ent->sam_entry_index++;
1202                                 goto tryagain;
1203                         }
1204
1205                         if ((type != SID_NAME_DOM_GRP) &&
1206                             (type != SID_NAME_ALIAS) &&
1207                             (type != SID_NAME_WKN_GRP)) {
1208                                 DEBUG(1, ("Group %s is a %s, not a group\n",
1209                                           sid_type_lookup(type),
1210                                           name_list[ent->sam_entry_index].acct_name));
1211                                 ent->sam_entry_index++;
1212                                 goto tryagain;
1213                         }
1214                         group_gid = id.gid;
1215                 }
1216
1217                 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
1218                            (unsigned long)name_list[ent->sam_entry_index].rid));
1219                 
1220                 /* Fill in group entry */
1221
1222                 fill_domain_username(domain_group_name, ent->domain_name, 
1223                          name_list[ent->sam_entry_index].acct_name, True);
1224
1225                 result = fill_grent(&group_list[group_list_ndx], 
1226                                     ent->domain_name,
1227                                     name_list[ent->sam_entry_index].acct_name,
1228                                     group_gid);
1229
1230                 /* Fill in group membership entry */
1231
1232                 if (result) {
1233                         size_t num_gr_mem = 0;
1234                         DOM_SID member_sid;
1235                         group_list[group_list_ndx].num_gr_mem = 0;
1236                         gr_mem = NULL;
1237                         gr_mem_len = 0;
1238                         
1239                         /* Get group membership */                      
1240                         if (state->request.cmd == WINBINDD_GETGRLST) {
1241                                 result = True;
1242                         } else {
1243                                 sid_copy(&member_sid, &domain->sid);
1244                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1245                                 result = fill_grent_mem(
1246                                         domain,
1247                                         NULL,
1248                                         &member_sid,
1249                                         SID_NAME_DOM_GRP,
1250                                         &num_gr_mem,
1251                                         &gr_mem, &gr_mem_len);
1252
1253                                 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1254                         }
1255                 }
1256
1257                 if (result) {
1258                         /* Append to group membership list */
1259                         gr_mem_list = (char *)SMB_REALLOC(
1260                                 gr_mem_list, gr_mem_list_len + gr_mem_len);
1261
1262                         if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
1263                                 DEBUG(0, ("out of memory\n"));
1264                                 gr_mem_list_len = 0;
1265                                 break;
1266                         }
1267
1268                         DEBUG(10, ("list_len = %d, mem_len = %u\n",
1269                                    gr_mem_list_len, (unsigned int)gr_mem_len));
1270
1271                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1272                                gr_mem_len);
1273
1274                         SAFE_FREE(gr_mem);
1275
1276                         group_list[group_list_ndx].gr_mem_ofs = 
1277                                 gr_mem_list_len;
1278
1279                         gr_mem_list_len += gr_mem_len;
1280                 }
1281
1282                 ent->sam_entry_index++;
1283                 
1284                 /* Add group to return list */
1285                 
1286                 if (result) {
1287
1288                         DEBUG(10, ("adding group num_entries = %d\n",
1289                                    state->response.data.num_entries));
1290
1291                         group_list_ndx++;
1292                         state->response.data.num_entries++;
1293                         
1294                         state->response.length +=
1295                                 sizeof(struct winbindd_gr);
1296                         
1297                 } else {
1298                         DEBUG(0, ("could not lookup domain group %s\n", 
1299                                   domain_group_name));
1300                 }
1301         }
1302
1303         /* Copy the list of group memberships to the end of the extra data */
1304
1305         if (group_list_ndx == 0)
1306                 goto done;
1307
1308         state->response.extra_data.data = SMB_REALLOC(
1309                 state->response.extra_data.data,
1310                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1311
1312         if (!state->response.extra_data.data) {
1313                 DEBUG(0, ("out of memory\n"));
1314                 group_list_ndx = 0;
1315                 SAFE_FREE(gr_mem_list);
1316                 request_error(state);
1317                 return;
1318         }
1319
1320         memcpy(&((char *)state->response.extra_data.data)
1321                [group_list_ndx * sizeof(struct winbindd_gr)], 
1322                gr_mem_list, gr_mem_list_len);
1323
1324         state->response.length += gr_mem_list_len;
1325
1326         DEBUG(10, ("returning %d groups, length = %d\n",
1327                    group_list_ndx, gr_mem_list_len));
1328
1329         /* Out of domains */
1330
1331  done:
1332
1333         SAFE_FREE(gr_mem_list);
1334
1335         if (group_list_ndx > 0)
1336                 request_ok(state);
1337         else
1338                 request_error(state);
1339 }
1340
1341 /* List domain groups without mapping to unix ids */
1342
1343 void winbindd_list_groups(struct winbindd_cli_state *state)
1344 {
1345         uint32 total_entries = 0;
1346         struct winbindd_domain *domain;
1347         const char *which_domain;
1348         char *extra_data = NULL;
1349         unsigned int extra_data_len = 0, i;
1350
1351         DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
1352
1353         /* Ensure null termination */
1354         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
1355         which_domain = state->request.domain_name;
1356         
1357         /* Enumerate over trusted domains */
1358
1359         for (domain = domain_list(); domain; domain = domain->next) {
1360                 struct getent_state groups;
1361
1362                 /* if we have a domain name restricting the request and this
1363                    one in the list doesn't match, then just bypass the remainder
1364                    of the loop */
1365                    
1366                 if ( *which_domain && !strequal(which_domain, domain->name) )
1367                         continue;
1368                         
1369                 ZERO_STRUCT(groups);
1370
1371                 /* Get list of sam groups */
1372                 
1373                 fstrcpy(groups.domain_name, domain->name);
1374
1375                 get_sam_group_entries(&groups);
1376                         
1377                 if (groups.num_sam_entries == 0) {
1378                         /* this domain is empty or in an error state */
1379                         continue;
1380                 }
1381
1382                 /* keep track the of the total number of groups seen so 
1383                    far over all domains */
1384                 total_entries += groups.num_sam_entries;
1385                 
1386                 /* Allocate some memory for extra data.  Note that we limit
1387                    account names to sizeof(fstring) = 128 characters.  */               
1388                 extra_data = (char *)SMB_REALLOC(
1389                         extra_data, sizeof(fstring) * total_entries);
1390  
1391                 if (!extra_data) {
1392                         DEBUG(0,("failed to enlarge buffer!\n"));
1393                         request_error(state);
1394                         return;
1395                 }
1396
1397                 /* Pack group list into extra data fields */
1398                 for (i = 0; i < groups.num_sam_entries; i++) {
1399                         char *group_name = ((struct acct_info *)
1400                                             groups.sam_entries)[i].acct_name; 
1401                         fstring name;
1402
1403                         fill_domain_username(name, domain->name, group_name, True);
1404                         /* Append to extra data */                      
1405                         memcpy(&extra_data[extra_data_len], name, 
1406                                strlen(name));
1407                         extra_data_len += strlen(name);
1408                         extra_data[extra_data_len++] = ',';
1409                 }
1410
1411                 SAFE_FREE(groups.sam_entries);
1412         }
1413
1414         /* Assign extra_data fields in response structure */
1415         if (extra_data) {
1416                 extra_data[extra_data_len - 1] = '\0';
1417                 state->response.extra_data.data = extra_data;
1418                 state->response.length += extra_data_len;
1419         }
1420
1421         /* No domains may have responded but that's still OK so don't
1422            return an error. */
1423
1424         request_ok(state);
1425 }
1426
1427 /* Get user supplementary groups.  This is much quicker than trying to
1428    invert the groups database.  We merge the groups from the gids and
1429    other_sids info3 fields as trusted domain, universal group
1430    memberships, and nested groups (win2k native mode only) are not
1431    returned by the getgroups RPC call but are present in the info3. */
1432
1433 struct getgroups_state {
1434         struct winbindd_cli_state *state;
1435         struct winbindd_domain *domain;
1436         char *domname;
1437         char *username;
1438         DOM_SID user_sid;
1439
1440         const DOM_SID *token_sids;
1441         size_t i, num_token_sids;
1442
1443         gid_t *token_gids;
1444         size_t num_token_gids;
1445 };
1446
1447 static void getgroups_usersid_recv(void *private_data, bool success,
1448                                    const DOM_SID *sid, enum lsa_SidType type);
1449 static void getgroups_tokensids_recv(void *private_data, bool success,
1450                                      DOM_SID *token_sids, size_t num_token_sids);
1451 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1452
1453 void winbindd_getgroups(struct winbindd_cli_state *state)
1454 {
1455         struct getgroups_state *s;
1456
1457         /* Ensure null termination */
1458         state->request.data.username
1459                 [sizeof(state->request.data.username)-1]='\0';
1460
1461         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1462                   state->request.data.username));
1463
1464         /* Parse domain and username */
1465
1466         s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1467         if (s == NULL) {
1468                 DEBUG(0, ("talloc failed\n"));
1469                 request_error(state);
1470                 return;
1471         }
1472
1473         s->state = state;
1474
1475         ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
1476
1477         if (!parse_domain_user_talloc(state->mem_ctx,
1478                                       state->request.data.username,
1479                                       &s->domname, &s->username)) {
1480                 DEBUG(5, ("Could not parse domain user: %s\n",
1481                           state->request.data.username));
1482
1483                 /* error out if we do not have nested group support */
1484
1485                 if ( !lp_winbind_nested_groups() ) {
1486                         request_error(state);
1487                         return;
1488                 }
1489
1490                 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1491                 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1492         }
1493         
1494         /* Get info for the domain (either by short domain name or 
1495            DNS name in the case of a UPN) */
1496
1497         s->domain = find_domain_from_name_noinit(s->domname);
1498         if (!s->domain) {
1499                 char *p = strchr(s->username, '@');
1500                 
1501                 if (p) {
1502                         s->domain = find_domain_from_name_noinit(p+1);                  
1503                 }
1504                 
1505         }
1506
1507         if (s->domain == NULL) {
1508                 DEBUG(7, ("could not find domain entry for domain %s\n", 
1509                           s->domname));
1510                 request_error(state);
1511                 return;
1512         }
1513
1514         if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1515                 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1516                          "getgroups() for %s\\%s.\n", s->domname,
1517                          s->username));
1518                 request_error(state);
1519                 return;
1520         }       
1521
1522         /* Get rid and name type from name.  The following costs 1 packet */
1523
1524         winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1525                                   getgroups_usersid_recv, WINBINDD_GETGROUPS, s);
1526 }
1527
1528 static void getgroups_usersid_recv(void *private_data, bool success,
1529                                    const DOM_SID *sid, enum lsa_SidType type)
1530 {
1531         struct getgroups_state *s =
1532                 (struct getgroups_state *)private_data;
1533
1534         if ((!success) ||
1535             ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1536                 request_error(s->state);
1537                 return;
1538         }
1539
1540         sid_copy(&s->user_sid, sid);
1541
1542         winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1543                                 getgroups_tokensids_recv, s);
1544 }
1545
1546 static void getgroups_tokensids_recv(void *private_data, bool success,
1547                                      DOM_SID *token_sids, size_t num_token_sids)
1548 {
1549         struct getgroups_state *s =
1550                 (struct getgroups_state *)private_data;
1551
1552         /* We need at least the user sid and the primary group in the token,
1553          * otherwise it's an error */
1554
1555         if ((!success) || (num_token_sids < 2)) {
1556                 request_error(s->state);
1557                 return;
1558         }
1559
1560         s->token_sids = token_sids;
1561         s->num_token_sids = num_token_sids;
1562         s->i = 0;
1563
1564         s->token_gids = NULL;
1565         s->num_token_gids = 0;
1566
1567         getgroups_sid2gid_recv(s, False, 0);
1568 }
1569
1570 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1571 {
1572         struct getgroups_state *s =
1573                 (struct getgroups_state *)private_data;
1574
1575         if (success) {
1576                 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1577                                         &s->token_gids,
1578                                         &s->num_token_gids)) {
1579                         return;
1580                 }
1581         }
1582
1583         if (s->i < s->num_token_sids) {
1584                 const DOM_SID *sid = &s->token_sids[s->i];
1585                 s->i += 1;
1586
1587                 if (sid_equal(sid, &s->user_sid)) {
1588                         getgroups_sid2gid_recv(s, False, 0);
1589                         return;
1590                 }
1591
1592                 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1593                                        getgroups_sid2gid_recv, s);
1594                 return;
1595         }
1596
1597         s->state->response.data.num_entries = s->num_token_gids;
1598         if (s->num_token_gids) {
1599                 /* s->token_gids are talloced */
1600                 s->state->response.extra_data.data = smb_xmemdup(s->token_gids, s->num_token_gids * sizeof(gid_t));
1601                 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1602         }
1603         request_ok(s->state);
1604 }
1605
1606 /* Get user supplementary sids. This is equivalent to the
1607    winbindd_getgroups() function but it involves a SID->SIDs mapping
1608    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1609    idmap. This call is designed to be used with applications that need
1610    to do ACL evaluation themselves. Note that the cached info3 data is
1611    not used 
1612
1613    this function assumes that the SID that comes in is a user SID. If
1614    you pass in another type of SID then you may get unpredictable
1615    results.
1616 */
1617
1618 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1619                              size_t num_sids);
1620
1621 void winbindd_getusersids(struct winbindd_cli_state *state)
1622 {
1623         DOM_SID *user_sid;
1624
1625         /* Ensure null termination */
1626         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1627
1628         user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1629         if (user_sid == NULL) {
1630                 DEBUG(1, ("talloc failed\n"));
1631                 request_error(state);
1632                 return;
1633         }
1634
1635         if (!string_to_sid(user_sid, state->request.data.sid)) {
1636                 DEBUG(1, ("Could not get convert sid %s from string\n",
1637                           state->request.data.sid));
1638                 request_error(state);
1639                 return;
1640         }
1641
1642         winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1643                                 state);
1644 }
1645
1646 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1647                              size_t num_sids)
1648 {
1649         struct winbindd_cli_state *state =
1650                 (struct winbindd_cli_state *)private_data;
1651         char *ret = NULL;
1652         unsigned ofs, ret_size = 0;
1653         size_t i;
1654
1655         if (!success) {
1656                 request_error(state);
1657                 return;
1658         }
1659
1660         /* work out the response size */
1661         for (i = 0; i < num_sids; i++) {
1662                 fstring s;
1663                 sid_to_fstring(s, &sids[i]);
1664                 ret_size += strlen(s) + 1;
1665         }
1666
1667         /* build the reply */
1668         ret = (char *)SMB_MALLOC(ret_size);
1669         if (!ret) {
1670                 DEBUG(0, ("malloc failed\n"));
1671                 request_error(state);
1672                 return;
1673         }
1674         ofs = 0;
1675         for (i = 0; i < num_sids; i++) {
1676                 fstring s;
1677                 sid_to_fstring(s, &sids[i]);
1678                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1679                 ofs += strlen(ret+ofs) + 1;
1680         }
1681
1682         /* Send data back to client */
1683         state->response.data.num_entries = num_sids;
1684         state->response.extra_data.data = ret;
1685         state->response.length += ret_size;
1686         request_ok(state);
1687 }
1688
1689 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1690 {
1691         DOM_SID user_sid;
1692         struct winbindd_domain *domain;
1693
1694         /* Ensure null termination */
1695         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1696
1697         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1698                 DEBUG(1, ("Could not get convert sid %s from string\n",
1699                           state->request.data.sid));
1700                 request_error(state);
1701                 return;
1702         }
1703
1704         /* Get info for the domain */   
1705         if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1706                 DEBUG(0,("could not find domain entry for sid %s\n", 
1707                          sid_string_dbg(&user_sid)));
1708                 request_error(state);
1709                 return;
1710         }
1711
1712         sendto_domain(state, domain);
1713 }
1714
1715 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1716                                                     struct winbindd_cli_state *state)
1717 {
1718         DOM_SID user_sid;
1719         NTSTATUS status;
1720
1721         char *sidstring;
1722         ssize_t len;
1723         DOM_SID *groups;
1724         uint32 num_groups;
1725
1726         /* Ensure null termination */
1727         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1728
1729         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1730                 DEBUG(1, ("Could not get convert sid %s from string\n",
1731                           state->request.data.sid));
1732                 return WINBINDD_ERROR;
1733         }
1734
1735         status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1736                                                     &user_sid, &num_groups,
1737                                                     &groups);
1738         if (!NT_STATUS_IS_OK(status))
1739                 return WINBINDD_ERROR;
1740
1741         if (num_groups == 0) {
1742                 state->response.data.num_entries = 0;
1743                 state->response.extra_data.data = NULL;
1744                 return WINBINDD_OK;
1745         }
1746
1747         if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) {
1748                 DEBUG(0, ("talloc failed\n"));
1749                 return WINBINDD_ERROR;
1750         }
1751
1752         state->response.extra_data.data = SMB_STRDUP(sidstring);
1753         if (!state->response.extra_data.data) {
1754                 return WINBINDD_ERROR;
1755         }
1756         state->response.length += len+1;
1757         state->response.data.num_entries = num_groups;
1758
1759         return WINBINDD_OK;
1760 }