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