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