Fix for bug 5571
[samba.git] / source / 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",
600                                    names[i], buf_ndx));
601                         parse_add_domuser(&buf[buf_ndx], names[i], &len);
602                         buf_ndx += len;
603                         buf[buf_ndx] = ',';
604                         buf_ndx++;
605                 }
606         }
607
608         /* Allocate buffer */
609
610         if (!buf && buf_len != 0) {
611                 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
612                         DEBUG(1, ("out of memory\n"));
613                         result = False;
614                         goto done;
615                 }
616                 memset(buf, 0, buf_len);
617                 goto again;
618         }
619
620         /* Now we're done */
621
622         if (buf && buf_ndx > 0) {
623                 buf[buf_ndx - 1] = '\0';
624         }
625
626         *gr_mem = buf;
627         *gr_mem_len = buf_len;
628
629         DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem, 
630                    (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); 
631         result = True;
632
633 done:
634
635         talloc_destroy(mem_ctx);
636         
637         DEBUG(10, ("fill_grent_mem returning %d\n", result));
638
639         return result;
640 }
641
642 static void winbindd_getgrsid( struct winbindd_cli_state *state, DOM_SID group_sid );
643
644 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid, 
645                            enum lsa_SidType type )
646 {
647         struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
648         
649         if (!success) {
650                 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
651                 request_error(state);
652                 return;
653         }
654
655         if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
656                 DEBUG(5,("getgrnam_recv: not a group!\n"));
657                 request_error(state);
658                 return;
659         }       
660
661         winbindd_getgrsid( state, *sid );
662 }
663
664         
665 /* Return a group structure from a group name */
666
667 void winbindd_getgrnam(struct winbindd_cli_state *state)
668 {
669         struct winbindd_domain *domain;
670         fstring name_domain, name_group;
671         char *tmp;
672         
673         /* Ensure null termination */
674         state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
675
676         DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
677                   state->request.data.groupname));
678
679         /* Parse domain and groupname */
680         
681         memset(name_group, 0, sizeof(fstring));
682
683         tmp = state->request.data.groupname;
684
685         name_domain[0] = '\0';
686         name_group[0] = '\0';
687         
688         parse_domain_user(tmp, name_domain, name_group);
689
690         /* if no domain or our local domain and no local tdb group, default to
691          * our local domain for aliases */
692
693         if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
694                 fstrcpy(name_domain, get_global_sam_name());
695         }
696
697         /* Get info for the domain */
698
699         if ((domain = find_domain_from_name(name_domain)) == NULL) {
700                 DEBUG(3, ("could not get domain sid for domain %s\n",
701                           name_domain));
702                 request_error(state);
703                 return;
704         }
705         /* should we deal with users for our domain? */
706         
707         if ( lp_winbind_trusted_domains_only() && domain->primary) {
708                 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
709                          "getgrnam() for %s\\%s.\n", name_domain, name_group));
710                 request_error(state);
711                 return;
712         }
713
714         /* Get rid and name type from name */
715
716         ws_name_replace( name_group, WB_REPLACE_CHAR );
717         
718         winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
719                                    getgrnam_recv, WINBINDD_GETGRNAM, state );
720 }
721
722 struct getgrsid_state {
723         struct winbindd_cli_state *state;
724         struct winbindd_domain *domain;
725         char *group_name;
726         enum lsa_SidType group_type;    
727         uid_t gid;
728         DOM_SID group_sid;
729 };
730
731 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
732         {
733         struct getgrsid_state *s =
734                 (struct getgrsid_state *)private_data;
735         struct winbindd_domain *domain;
736         size_t gr_mem_len;
737         size_t num_gr_mem;
738         char *gr_mem;
739         fstring dom_name, group_name;   
740
741         if (!success) {
742                 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
743                 request_error(s->state);
744                 return;
745         }
746
747         s->gid = gid;
748
749         if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
750                 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
751                 request_error(s->state);
752                 return;
753         }
754
755         
756         /* Fill in group structure */
757
758         if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
759                 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
760                 request_error(s->state);
761                 return;
762         }
763
764         if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) ||
765             !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
766                             &num_gr_mem, &gr_mem, &gr_mem_len)) 
767         {
768                 request_error(s->state);
769                 return;
770         }
771
772         s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
773
774         /* Group membership lives at start of extra data */
775
776         s->state->response.data.gr.gr_mem_ofs = 0;
777
778         s->state->response.length += gr_mem_len;
779         s->state->response.extra_data.data = gr_mem;
780
781         request_ok(s->state);   
782         }
783
784 static void getgrsid_lookupsid_recv( void *private_data, bool success,
785                                      const char *dom_name, const char *name,
786                                      enum lsa_SidType name_type )
787 {
788         struct getgrsid_state *s = (struct getgrsid_state *)private_data;
789
790         if (!success) {
791                 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
792                 request_error(s->state);
793                 return;
794         }
795
796         /* either it's a domain group, a domain local group, or a
797            local group in an internal domain */
798
799         if ( !( (name_type==SID_NAME_DOM_GRP) ||
800                 ((name_type==SID_NAME_ALIAS) && 
801                  (s->domain->primary || s->domain->internal)) ) )
802         {
803                 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n", 
804                           dom_name, name, name_type));
805                 request_error(s->state);
806                 return;
807 }
808
809         if ( (s->group_name = talloc_asprintf( s->state->mem_ctx, 
810                                                "%s%c%s",
811                                                dom_name,
812                                                *lp_winbind_separator(),
813                                                name)) == NULL )
814 {
815                 DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
816                 request_error(s->state);
817                 return;
818         }
819
820         s->group_type = name_type;
821
822         winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
823                                getgrsid_sid2gid_recv, s);
824         }
825
826 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
827         {
828         struct getgrsid_state *s;
829
830         if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
831                 DEBUG(0, ("talloc failed\n"));
832                 request_error(state);
833                 return;
834         }
835
836         s->state = state;
837
838         if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
839                 DEBUG(3, ("Could not find domain for sid %s\n",
840                           sid_string_dbg(&group_sid)));
841                 request_error(state);
842                 return;
843         }
844
845         sid_copy(&s->group_sid, &group_sid);
846
847         winbindd_lookupsid_async( s->state->mem_ctx,  &group_sid, 
848                                   getgrsid_lookupsid_recv, s );
849 }
850
851
852 static void getgrgid_recv(void *private_data, bool success, const char *sid)
853 {
854         struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
855         enum lsa_SidType name_type;
856         DOM_SID group_sid;
857
858         if (success) {
859                 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
860                           (unsigned long)(state->request.data.gid), sid));
861
862                 string_to_sid(&group_sid, sid);
863                 winbindd_getgrsid(state, group_sid);
864                 return;
865         }
866
867         /* Ok, this might be "ours", i.e. an alias */
868         if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
869             lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
870             (name_type == SID_NAME_ALIAS)) {
871                 /* Hey, got an alias */
872                 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
873                           (unsigned long)(state->request.data.gid), sid));
874                 winbindd_getgrsid(state, group_sid);
875                 return;
876         }
877
878         DEBUG(1, ("could not convert gid %lu to sid\n", 
879                   (unsigned long)state->request.data.gid));
880         request_error(state);
881 }
882
883 /* Return a group structure from a gid number */
884 void winbindd_getgrgid(struct winbindd_cli_state *state)
885 {
886         DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, 
887                   (unsigned long)state->request.data.gid));
888
889         /* always use the async interface */
890         winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state);
891 }
892
893 /*
894  * set/get/endgrent functions
895  */
896
897 /* "Rewind" file pointer for group database enumeration */
898
899 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
900 {
901         struct winbindd_domain *domain;
902
903         DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
904
905         /* Check user has enabled this */
906
907         if (!lp_winbind_enum_groups()) {
908                 return False;
909         }               
910
911         /* Free old static data if it exists */
912         
913         if (state->getgrent_state != NULL) {
914                 free_getent_state(state->getgrent_state);
915                 state->getgrent_state = NULL;
916         }
917         
918         /* Create sam pipes for each domain we know about */
919         
920         for (domain = domain_list(); domain != NULL; domain = domain->next) {
921                 struct getent_state *domain_state;
922                 
923                 /* Create a state record for this domain */
924
925                 /* don't add our domaina if we are a PDC or if we 
926                    are a member of a Samba domain */
927                 
928                 if ( lp_winbind_trusted_domains_only() && domain->primary )
929                 {
930                         continue;
931                 }
932                                                 
933                 
934                 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
935                         DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
936                         return False;
937                 }
938                 
939                 ZERO_STRUCTP(domain_state);
940                 
941                 fstrcpy(domain_state->domain_name, domain->name);
942
943                 /* Add to list of open domains */
944                 
945                 DLIST_ADD(state->getgrent_state, domain_state);
946         }
947         
948         state->getgrent_initialized = True;
949         return True;
950 }
951
952 void winbindd_setgrent(struct winbindd_cli_state *state)
953 {
954         if (winbindd_setgrent_internal(state)) {
955                 request_ok(state);
956         } else {
957                 request_error(state);
958         }
959 }
960
961 /* Close file pointer to ntdom group database */
962
963 void winbindd_endgrent(struct winbindd_cli_state *state)
964 {
965         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
966
967         free_getent_state(state->getgrent_state);
968         state->getgrent_initialized = False;
969         state->getgrent_state = NULL;
970         request_ok(state);
971 }
972
973 /* Get the list of domain groups and domain aliases for a domain.  We fill in
974    the sam_entries and num_sam_entries fields with domain group information.  
975    Return True if some groups were returned, False otherwise. */
976
977 bool get_sam_group_entries(struct getent_state *ent)
978 {
979         NTSTATUS status;
980         uint32 num_entries;
981         struct acct_info *name_list = NULL;
982         TALLOC_CTX *mem_ctx;
983         bool result = False;
984         struct acct_info *sam_grp_entries = NULL;
985         struct winbindd_domain *domain;
986         
987         if (ent->got_sam_entries)
988                 return False;
989
990         if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
991                                           ent->domain_name))) {
992                 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); 
993                 return False;
994         }
995                 
996         /* Free any existing group info */
997
998         SAFE_FREE(ent->sam_entries);
999         ent->num_sam_entries = 0;
1000         ent->got_sam_entries = True;
1001
1002         /* Enumerate domain groups */
1003
1004         num_entries = 0;
1005
1006         if (!(domain = find_domain_from_name(ent->domain_name))) {
1007                 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
1008                 goto done;
1009         }
1010
1011         /* always get the domain global groups */
1012
1013         status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1014         
1015         if (!NT_STATUS_IS_OK(status)) {
1016                 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
1017                 result = False;
1018                 goto done;
1019         }
1020
1021         /* Copy entries into return buffer */
1022
1023         if (num_entries) {
1024                 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
1025                         DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", 
1026                                 num_entries));
1027                         result = False;
1028                         goto done;
1029                 }
1030                 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
1031         }
1032         
1033         ent->num_sam_entries = num_entries;
1034         
1035         /* get the domain local groups if we are a member of a native win2k domain
1036            and are not using LDAP to get the groups */
1037            
1038         if ( ( lp_security() != SEC_ADS && domain->native_mode 
1039                 && domain->primary) || domain->internal )
1040         {
1041                 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n", 
1042                         domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
1043                 
1044                 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1045                 
1046                 if ( !NT_STATUS_IS_OK(status) ) { 
1047                         DEBUG(3,("get_sam_group_entries: "
1048                                 "Failed to enumerate "
1049                                 "domain local groups with error %s!\n",
1050                                 nt_errstr(status)));
1051                         num_entries = 0;
1052                 }
1053                 else
1054                         DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
1055                 
1056                 /* Copy entries into return buffer */
1057
1058                 if ( num_entries ) {
1059                         if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
1060                         {
1061                                 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", 
1062                                         num_entries));
1063                                 result = False;
1064                                 goto done;
1065                         }
1066                         
1067                         memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, 
1068                                 num_entries * sizeof(struct acct_info) );
1069                 }
1070         
1071                 ent->num_sam_entries += num_entries;
1072         }
1073         
1074                 
1075         /* Fill in remaining fields */
1076
1077         ent->sam_entries = name_list;
1078         ent->sam_entry_index = 0;
1079
1080         result = (ent->num_sam_entries > 0);
1081
1082  done:
1083         talloc_destroy(mem_ctx);
1084
1085         return result;
1086 }
1087
1088 /* Fetch next group entry from ntdom database */
1089
1090 #define MAX_GETGRENT_GROUPS 500
1091
1092 void winbindd_getgrent(struct winbindd_cli_state *state)
1093 {
1094         struct getent_state *ent;
1095         struct winbindd_gr *group_list = NULL;
1096         int num_groups, group_list_ndx, gr_mem_list_len = 0;
1097         char *gr_mem_list = NULL;
1098
1099         DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1100
1101         /* Check user has enabled this */
1102
1103         if (!lp_winbind_enum_groups()) {
1104                 request_error(state);
1105                 return;
1106         }
1107
1108         num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1109
1110         if (num_groups == 0) {
1111                 request_error(state);
1112                 return;
1113         }
1114
1115         if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
1116                 request_error(state);
1117                 return;
1118         }
1119
1120         memset(state->response.extra_data.data, '\0',
1121                 num_groups * sizeof(struct winbindd_gr) );
1122
1123         state->response.data.num_entries = 0;
1124
1125         group_list = (struct winbindd_gr *)state->response.extra_data.data;
1126
1127         if (!state->getgrent_initialized)
1128                 winbindd_setgrent_internal(state);
1129
1130         if (!(ent = state->getgrent_state)) {
1131                 request_error(state);
1132                 return;
1133         }
1134
1135         /* Start sending back groups */
1136
1137         for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1138                 struct acct_info *name_list = NULL;
1139                 fstring domain_group_name;
1140                 uint32 result;
1141                 gid_t group_gid;
1142                 size_t gr_mem_len;
1143                 char *gr_mem;
1144                 DOM_SID group_sid;
1145                 struct winbindd_domain *domain;
1146                                 
1147                 /* Do we need to fetch another chunk of groups? */
1148
1149         tryagain:
1150
1151                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1152                            ent->sam_entry_index, ent->num_sam_entries));
1153
1154                 if (ent->num_sam_entries == ent->sam_entry_index) {
1155
1156                         while(ent && !get_sam_group_entries(ent)) {
1157                                 struct getent_state *next_ent;
1158
1159                                 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 
1160
1161                                 /* Free state information for this domain */
1162
1163                                 SAFE_FREE(ent->sam_entries);
1164
1165                                 next_ent = ent->next;
1166                                 DLIST_REMOVE(state->getgrent_state, ent);
1167                                 
1168                                 SAFE_FREE(ent);
1169                                 ent = next_ent;
1170                         }
1171
1172                         /* No more domains */
1173
1174                         if (!ent) 
1175                                 break;
1176                 }
1177                 
1178                 name_list = (struct acct_info *)ent->sam_entries;
1179                 
1180                 if (!(domain = 
1181                       find_domain_from_name(ent->domain_name))) {
1182                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
1183                         result = False;
1184                         goto done;
1185                 }
1186
1187                 /* Lookup group info */
1188                 
1189                 sid_copy(&group_sid, &domain->sid);
1190                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1191
1192                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) {
1193                         union unid_t id;
1194                         enum lsa_SidType type;
1195
1196                         DEBUG(10, ("SID %s not in idmap\n",
1197                                    sid_string_dbg(&group_sid)));
1198
1199                         if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1200                                 DEBUG(1, ("could not look up gid for group "
1201                                           "%s\n", 
1202                                           name_list[ent->sam_entry_index].acct_name));
1203                                 ent->sam_entry_index++;
1204                                 goto tryagain;
1205                         }
1206
1207                         if ((type != SID_NAME_DOM_GRP) &&
1208                             (type != SID_NAME_ALIAS) &&
1209                             (type != SID_NAME_WKN_GRP)) {
1210                                 DEBUG(1, ("Group %s is a %s, not a group\n",
1211                                           sid_type_lookup(type),
1212                                           name_list[ent->sam_entry_index].acct_name));
1213                                 ent->sam_entry_index++;
1214                                 goto tryagain;
1215                         }
1216                         group_gid = id.gid;
1217                 }
1218
1219                 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
1220                            (unsigned long)name_list[ent->sam_entry_index].rid));
1221                 
1222                 /* Fill in group entry */
1223
1224                 fill_domain_username(domain_group_name, ent->domain_name, 
1225                          name_list[ent->sam_entry_index].acct_name, True);
1226
1227                 result = fill_grent(&group_list[group_list_ndx], 
1228                                     ent->domain_name,
1229                                     name_list[ent->sam_entry_index].acct_name,
1230                                     group_gid);
1231
1232                 /* Fill in group membership entry */
1233
1234                 if (result) {
1235                         size_t num_gr_mem = 0;
1236                         DOM_SID member_sid;
1237                         group_list[group_list_ndx].num_gr_mem = 0;
1238                         gr_mem = NULL;
1239                         gr_mem_len = 0;
1240                         
1241                         /* Get group membership */                      
1242                         if (state->request.cmd == WINBINDD_GETGRLST) {
1243                                 result = True;
1244                         } else {
1245                                 sid_copy(&member_sid, &domain->sid);
1246                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1247                                 result = fill_grent_mem(
1248                                         domain,
1249                                         NULL,
1250                                         &member_sid,
1251                                         SID_NAME_DOM_GRP,
1252                                         &num_gr_mem,
1253                                         &gr_mem, &gr_mem_len);
1254
1255                                 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1256                         }
1257                 }
1258
1259                 if (result) {
1260                         /* Append to group membership list */
1261                         gr_mem_list = (char *)SMB_REALLOC(
1262                                 gr_mem_list, gr_mem_list_len + gr_mem_len);
1263
1264                         if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
1265                                 DEBUG(0, ("out of memory\n"));
1266                                 gr_mem_list_len = 0;
1267                                 break;
1268                         }
1269
1270                         DEBUG(10, ("list_len = %d, mem_len = %u\n",
1271                                    gr_mem_list_len, (unsigned int)gr_mem_len));
1272
1273                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1274                                gr_mem_len);
1275
1276                         SAFE_FREE(gr_mem);
1277
1278                         group_list[group_list_ndx].gr_mem_ofs = 
1279                                 gr_mem_list_len;
1280
1281                         gr_mem_list_len += gr_mem_len;
1282                 }
1283
1284                 ent->sam_entry_index++;
1285                 
1286                 /* Add group to return list */
1287                 
1288                 if (result) {
1289
1290                         DEBUG(10, ("adding group num_entries = %d\n",
1291                                    state->response.data.num_entries));
1292
1293                         group_list_ndx++;
1294                         state->response.data.num_entries++;
1295                         
1296                         state->response.length +=
1297                                 sizeof(struct winbindd_gr);
1298                         
1299                 } else {
1300                         DEBUG(0, ("could not lookup domain group %s\n", 
1301                                   domain_group_name));
1302                 }
1303         }
1304
1305         /* Copy the list of group memberships to the end of the extra data */
1306
1307         if (group_list_ndx == 0)
1308                 goto done;
1309
1310         state->response.extra_data.data = SMB_REALLOC(
1311                 state->response.extra_data.data,
1312                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1313
1314         if (!state->response.extra_data.data) {
1315                 DEBUG(0, ("out of memory\n"));
1316                 group_list_ndx = 0;
1317                 SAFE_FREE(gr_mem_list);
1318                 request_error(state);
1319                 return;
1320         }
1321
1322         memcpy(&((char *)state->response.extra_data.data)
1323                [group_list_ndx * sizeof(struct winbindd_gr)], 
1324                gr_mem_list, gr_mem_list_len);
1325
1326         state->response.length += gr_mem_list_len;
1327
1328         DEBUG(10, ("returning %d groups, length = %d\n",
1329                    group_list_ndx, gr_mem_list_len));
1330
1331         /* Out of domains */
1332
1333  done:
1334
1335         SAFE_FREE(gr_mem_list);
1336
1337         if (group_list_ndx > 0)
1338                 request_ok(state);
1339         else
1340                 request_error(state);
1341 }
1342
1343 /* List domain groups without mapping to unix ids */
1344 void winbindd_list_groups(struct winbindd_cli_state *state)
1345 {
1346         winbindd_list_ent(state, LIST_GROUPS);
1347 }
1348
1349 /* Get user supplementary groups.  This is much quicker than trying to
1350    invert the groups database.  We merge the groups from the gids and
1351    other_sids info3 fields as trusted domain, universal group
1352    memberships, and nested groups (win2k native mode only) are not
1353    returned by the getgroups RPC call but are present in the info3. */
1354
1355 struct getgroups_state {
1356         struct winbindd_cli_state *state;
1357         struct winbindd_domain *domain;
1358         char *domname;
1359         char *username;
1360         DOM_SID user_sid;
1361
1362         const DOM_SID *token_sids;
1363         size_t i, num_token_sids;
1364
1365         gid_t *token_gids;
1366         size_t num_token_gids;
1367 };
1368
1369 static void getgroups_usersid_recv(void *private_data, bool success,
1370                                    const DOM_SID *sid, enum lsa_SidType type);
1371 static void getgroups_tokensids_recv(void *private_data, bool success,
1372                                      DOM_SID *token_sids, size_t num_token_sids);
1373 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1374
1375 void winbindd_getgroups(struct winbindd_cli_state *state)
1376 {
1377         struct getgroups_state *s;
1378
1379         /* Ensure null termination */
1380         state->request.data.username
1381                 [sizeof(state->request.data.username)-1]='\0';
1382
1383         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1384                   state->request.data.username));
1385
1386         /* Parse domain and username */
1387
1388         s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1389         if (s == NULL) {
1390                 DEBUG(0, ("talloc failed\n"));
1391                 request_error(state);
1392                 return;
1393         }
1394
1395         s->state = state;
1396
1397         ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
1398
1399         if (!parse_domain_user_talloc(state->mem_ctx,
1400                                       state->request.data.username,
1401                                       &s->domname, &s->username)) {
1402                 DEBUG(5, ("Could not parse domain user: %s\n",
1403                           state->request.data.username));
1404
1405                 /* error out if we do not have nested group support */
1406
1407                 if ( !lp_winbind_nested_groups() ) {
1408                         request_error(state);
1409                         return;
1410                 }
1411
1412                 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1413                 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1414         }
1415         
1416         /* Get info for the domain (either by short domain name or 
1417            DNS name in the case of a UPN) */
1418
1419         s->domain = find_domain_from_name_noinit(s->domname);
1420         if (!s->domain) {
1421                 char *p = strchr(s->username, '@');
1422                 
1423                 if (p) {
1424                         s->domain = find_domain_from_name_noinit(p+1);                  
1425                 }
1426                 
1427         }
1428
1429         if (s->domain == NULL) {
1430                 DEBUG(7, ("could not find domain entry for domain %s\n", 
1431                           s->domname));
1432                 request_error(state);
1433                 return;
1434         }
1435
1436         if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1437                 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1438                          "getgroups() for %s\\%s.\n", s->domname,
1439                          s->username));
1440                 request_error(state);
1441                 return;
1442         }       
1443
1444         /* Get rid and name type from name.  The following costs 1 packet */
1445
1446         winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1447                                   getgroups_usersid_recv, WINBINDD_GETGROUPS, s);
1448 }
1449
1450 static void getgroups_usersid_recv(void *private_data, bool success,
1451                                    const DOM_SID *sid, enum lsa_SidType type)
1452 {
1453         struct getgroups_state *s =
1454                 (struct getgroups_state *)private_data;
1455
1456         if ((!success) ||
1457             ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1458                 request_error(s->state);
1459                 return;
1460         }
1461
1462         sid_copy(&s->user_sid, sid);
1463
1464         winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1465                                 getgroups_tokensids_recv, s);
1466 }
1467
1468 static void getgroups_tokensids_recv(void *private_data, bool success,
1469                                      DOM_SID *token_sids, size_t num_token_sids)
1470 {
1471         struct getgroups_state *s =
1472                 (struct getgroups_state *)private_data;
1473
1474         /* We need at least the user sid and the primary group in the token,
1475          * otherwise it's an error */
1476
1477         if ((!success) || (num_token_sids < 2)) {
1478                 request_error(s->state);
1479                 return;
1480         }
1481
1482         s->token_sids = token_sids;
1483         s->num_token_sids = num_token_sids;
1484         s->i = 0;
1485
1486         s->token_gids = NULL;
1487         s->num_token_gids = 0;
1488
1489         getgroups_sid2gid_recv(s, False, 0);
1490 }
1491
1492 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1493 {
1494         struct getgroups_state *s =
1495                 (struct getgroups_state *)private_data;
1496
1497         if (success) {
1498                 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1499                                         &s->token_gids,
1500                                         &s->num_token_gids)) {
1501                         return;
1502                 }
1503         }
1504
1505         if (s->i < s->num_token_sids) {
1506                 const DOM_SID *sid = &s->token_sids[s->i];
1507                 s->i += 1;
1508
1509                 if (sid_equal(sid, &s->user_sid)) {
1510                         getgroups_sid2gid_recv(s, False, 0);
1511                         return;
1512                 }
1513
1514                 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1515                                        getgroups_sid2gid_recv, s);
1516                 return;
1517         }
1518
1519         s->state->response.data.num_entries = s->num_token_gids;
1520         if (s->num_token_gids) {
1521                 /* s->token_gids are talloced */
1522                 s->state->response.extra_data.data = smb_xmemdup(s->token_gids, s->num_token_gids * sizeof(gid_t));
1523                 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1524         }
1525         request_ok(s->state);
1526 }
1527
1528 /* Get user supplementary sids. This is equivalent to the
1529    winbindd_getgroups() function but it involves a SID->SIDs mapping
1530    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1531    idmap. This call is designed to be used with applications that need
1532    to do ACL evaluation themselves. Note that the cached info3 data is
1533    not used 
1534
1535    this function assumes that the SID that comes in is a user SID. If
1536    you pass in another type of SID then you may get unpredictable
1537    results.
1538 */
1539
1540 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1541                              size_t num_sids);
1542
1543 void winbindd_getusersids(struct winbindd_cli_state *state)
1544 {
1545         DOM_SID *user_sid;
1546
1547         /* Ensure null termination */
1548         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1549
1550         user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1551         if (user_sid == NULL) {
1552                 DEBUG(1, ("talloc failed\n"));
1553                 request_error(state);
1554                 return;
1555         }
1556
1557         if (!string_to_sid(user_sid, state->request.data.sid)) {
1558                 DEBUG(1, ("Could not get convert sid %s from string\n",
1559                           state->request.data.sid));
1560                 request_error(state);
1561                 return;
1562         }
1563
1564         winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1565                                 state);
1566 }
1567
1568 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1569                              size_t num_sids)
1570 {
1571         struct winbindd_cli_state *state =
1572                 (struct winbindd_cli_state *)private_data;
1573         char *ret = NULL;
1574         unsigned ofs, ret_size = 0;
1575         size_t i;
1576
1577         if (!success) {
1578                 request_error(state);
1579                 return;
1580         }
1581
1582         /* work out the response size */
1583         for (i = 0; i < num_sids; i++) {
1584                 fstring s;
1585                 sid_to_fstring(s, &sids[i]);
1586                 ret_size += strlen(s) + 1;
1587         }
1588
1589         /* build the reply */
1590         ret = (char *)SMB_MALLOC(ret_size);
1591         if (!ret) {
1592                 DEBUG(0, ("malloc failed\n"));
1593                 request_error(state);
1594                 return;
1595         }
1596         ofs = 0;
1597         for (i = 0; i < num_sids; i++) {
1598                 fstring s;
1599                 sid_to_fstring(s, &sids[i]);
1600                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1601                 ofs += strlen(ret+ofs) + 1;
1602         }
1603
1604         /* Send data back to client */
1605         state->response.data.num_entries = num_sids;
1606         state->response.extra_data.data = ret;
1607         state->response.length += ret_size;
1608         request_ok(state);
1609 }
1610
1611 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1612 {
1613         DOM_SID user_sid;
1614         struct winbindd_domain *domain;
1615
1616         /* Ensure null termination */
1617         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1618
1619         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1620                 DEBUG(1, ("Could not get convert sid %s from string\n",
1621                           state->request.data.sid));
1622                 request_error(state);
1623                 return;
1624         }
1625
1626         /* Get info for the domain */   
1627         if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1628                 DEBUG(0,("could not find domain entry for sid %s\n", 
1629                          sid_string_dbg(&user_sid)));
1630                 request_error(state);
1631                 return;
1632         }
1633
1634         sendto_domain(state, domain);
1635 }
1636
1637 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1638                                                     struct winbindd_cli_state *state)
1639 {
1640         DOM_SID user_sid;
1641         NTSTATUS status;
1642
1643         char *sidstring;
1644         ssize_t len;
1645         DOM_SID *groups;
1646         uint32 num_groups;
1647
1648         /* Ensure null termination */
1649         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1650
1651         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1652                 DEBUG(1, ("Could not get convert sid %s from string\n",
1653                           state->request.data.sid));
1654                 return WINBINDD_ERROR;
1655         }
1656
1657         status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1658                                                     &user_sid, &num_groups,
1659                                                     &groups);
1660         if (!NT_STATUS_IS_OK(status))
1661                 return WINBINDD_ERROR;
1662
1663         if (num_groups == 0) {
1664                 state->response.data.num_entries = 0;
1665                 state->response.extra_data.data = NULL;
1666                 return WINBINDD_OK;
1667         }
1668
1669         if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) {
1670                 DEBUG(0, ("talloc failed\n"));
1671                 return WINBINDD_ERROR;
1672         }
1673
1674         state->response.extra_data.data = SMB_STRDUP(sidstring);
1675         if (!state->response.extra_data.data) {
1676                 return WINBINDD_ERROR;
1677         }
1678         state->response.length += len+1;
1679         state->response.data.num_entries = num_groups;
1680
1681         return WINBINDD_OK;
1682 }