r23619: Fix compile warning in fill_grent_mem() caused by mismatched counter size.
[ira/wip.git] / source3 / nsswitch / winbindd_group.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon for ntdom nss module
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jeremy Allison 2001.
8    Copyright (C) Gerald (Jerry) Carter 2003.
9    Copyright (C) Volker Lendecke 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
29 extern BOOL opt_nocache;
30
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
33
34 static void add_member(const char *domain, const char *user,
35            char **pp_members, size_t *p_num_members)
36 {
37         fstring name;
38
39         fill_domain_username(name, domain, user, True);
40         safe_strcat(name, ",", sizeof(name)-1);
41         string_append(pp_members, name);
42         *p_num_members += 1;
43 }
44
45 /**********************************************************************
46  Add member users resulting from sid. Expand if it is a domain group.
47 **********************************************************************/
48
49 static void add_expanded_sid(const DOM_SID *sid, char **pp_members, size_t *p_num_members)
50 {
51         DOM_SID dom_sid;
52         uint32 rid;
53         struct winbindd_domain *domain;
54         size_t i;
55
56         char *domain_name = NULL;
57         char *name = NULL;
58         enum lsa_SidType type;
59
60         uint32 num_names;
61         DOM_SID *sid_mem;
62         char **names;
63         uint32 *types;
64
65         NTSTATUS result;
66
67         TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
68
69         if (mem_ctx == NULL) {
70                 DEBUG(1, ("talloc_init failed\n"));
71                 return;
72         }
73
74         sid_copy(&dom_sid, sid);
75         sid_split_rid(&dom_sid, &rid);
76
77         domain = find_lookup_domain_from_sid(sid);
78
79         if (domain == NULL) {
80                 DEBUG(3, ("Could not find domain for sid %s\n",
81                           sid_string_static(sid)));
82                 goto done;
83         }
84
85         result = domain->methods->sid_to_name(domain, mem_ctx, sid,
86                                               &domain_name, &name, &type);
87
88         if (!NT_STATUS_IS_OK(result)) {
89                 DEBUG(3, ("sid_to_name failed for sid %s\n",
90                           sid_string_static(sid)));
91                 goto done;
92         }
93
94         DEBUG(10, ("Found name %s, type %d\n", name, type));
95
96         if (type == SID_NAME_USER) {
97                 add_member(domain_name, name, pp_members, p_num_members);
98                 goto done;
99         }
100
101         if (type != SID_NAME_DOM_GRP) {
102                 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
103                            name));
104                 goto done;
105         }
106
107         /* Expand the domain group, this must be done via the target domain */
108
109         domain = find_domain_from_sid(sid);
110
111         if (domain == NULL) {
112                 DEBUG(3, ("Could not find domain from SID %s\n",
113                           sid_string_static(sid)));
114                 goto done;
115         }
116
117         result = domain->methods->lookup_groupmem(domain, mem_ctx,
118                                                   sid, &num_names,
119                                                   &sid_mem, &names,
120                                                   &types);
121
122         if (!NT_STATUS_IS_OK(result)) {
123                 DEBUG(10, ("Could not lookup group members for %s: %s\n",
124                            name, nt_errstr(result)));
125                 goto done;
126         }
127
128         for (i=0; i<num_names; i++) {
129                 DEBUG(10, ("Adding group member SID %s\n",
130                            sid_string_static(&sid_mem[i])));
131
132                 if (types[i] != SID_NAME_USER) {
133                         DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
134                                   "Ignoring.\n", names[i], name));
135                         continue;
136                 }
137
138                 add_member(domain->name, names[i], pp_members, p_num_members);
139         }
140
141  done:
142         talloc_destroy(mem_ctx);
143         return;
144 }
145
146 static BOOL fill_passdb_alias_grmem(struct winbindd_domain *domain,
147                              DOM_SID *group_sid, 
148                              size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
149 {
150         DOM_SID *members;
151         size_t i, num_members;
152
153         *num_gr_mem = 0;
154         *gr_mem = NULL;
155         *gr_mem_len = 0;
156
157         if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
158                                                &num_members)))
159                 return True;
160
161         for (i=0; i<num_members; i++) {
162                 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
163         }
164
165         TALLOC_FREE(members);
166
167         if (*gr_mem != NULL) {
168                 size_t len;
169
170                 /* We have at least one member, strip off the last "," */
171                 len = strlen(*gr_mem);
172                 (*gr_mem)[len-1] = '\0';
173                 *gr_mem_len = len;
174         }
175
176         return True;
177 }
178
179 /* Fill a grent structure from various other information */
180
181 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name, 
182                        const char *gr_name, gid_t unix_gid)
183 {
184         fstring full_group_name;
185
186         fill_domain_username( full_group_name, dom_name, gr_name, True );
187
188         gr->gr_gid = unix_gid;
189     
190         /* Group name and password */
191     
192         safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
193         safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
194
195         return True;
196 }
197
198 /***********************************************************************
199  If "enum users" is set to false, and the group being looked
200  up is the Domain Users SID: S-1-5-domain-513, then for the
201  list of members check if the querying user is in that group,
202  and if so only return that user as the gr_mem array.
203  We can change this to a different parameter than "enum users"
204  if neccessaey, or parameterize the group list we do this for.
205 ***********************************************************************/
206
207 static BOOL fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
208                                      struct winbindd_domain *domain,
209                                      struct winbindd_cli_state *state,
210                                      DOM_SID *group_sid,
211                                      enum lsa_SidType group_name_type,
212                                      size_t *num_gr_mem, char **gr_mem, 
213                                      size_t *gr_mem_len)
214 {
215         DOM_SID querying_user_sid;
216         DOM_SID *pquerying_user_sid = NULL;
217         uint32 num_groups = 0;
218         DOM_SID *user_sids = NULL;
219         BOOL u_in_group = False;
220         NTSTATUS status;
221         int i;
222         unsigned int buf_len = 0;
223         char *buf = NULL;
224
225         DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
226                   domain->name ));
227
228         if (state) {
229                 uid_t ret_uid = (uid_t)-1;
230                 if (sys_getpeereid(state->sock, &ret_uid)==0) {
231                         /* We know who's asking - look up their SID if
232                            it's one we've mapped before. */
233                         status = idmap_uid_to_sid(&querying_user_sid, ret_uid);
234                         if (NT_STATUS_IS_OK(status)) {
235                                 pquerying_user_sid = &querying_user_sid;
236                                 DEBUG(10,("fill_grent_mem_domain_users: querying uid %u -> %s\n",
237                                           (unsigned int)ret_uid,
238                                           sid_string_static(pquerying_user_sid) ));
239                         }
240                 }
241         }
242
243         /* Only look up if it was a winbindd user in this domain. */
244         if (pquerying_user_sid &&
245             (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
246                 
247                 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
248                           sid_string_static(pquerying_user_sid) ));
249                 
250                 status = domain->methods->lookup_usergroups(domain,
251                                                             mem_ctx,
252                                                             pquerying_user_sid,
253                                                             &num_groups,
254                                                             &user_sids);
255                 if (!NT_STATUS_IS_OK(status)) {
256                         DEBUG(1, ("fill_grent_mem_domain_users: lookup_usergroups failed "
257                                   "for sid %s in domain %s (error: %s)\n", 
258                                   sid_string_static(pquerying_user_sid),
259                                   domain->name,
260                                   nt_errstr(status)));
261                         return False;                   
262                 }
263
264                 for (i = 0; i < num_groups; i++) {
265                         if (sid_equal(group_sid, &user_sids[i])) {
266                                 /* User is in Domain Users, add their name
267                                    as the only group member. */
268                                 u_in_group = True;
269                                 break;
270                         }
271                 }
272         }
273         
274         if (u_in_group) {
275                 size_t len = 0;
276                 char *domainname = NULL;
277                 char *username = NULL;
278                 fstring name;
279                 enum lsa_SidType type;
280                 
281                 DEBUG(10,("fill_grent_mem_domain_users: sid %s in 'Domain Users' in domain %s\n",
282                           sid_string_static(pquerying_user_sid), domain->name ));
283                 
284                 status = domain->methods->sid_to_name(domain, mem_ctx,
285                                                       pquerying_user_sid,
286                                                       &domainname,
287                                                       &username,
288                                                       &type);
289                 if (!NT_STATUS_IS_OK(status)) {
290                         DEBUG(1, ("could not lookup username for user "
291                                   "sid %s in domain %s (error: %s)\n", 
292                                   sid_string_static(pquerying_user_sid),
293                                   domain->name,
294                                   nt_errstr(status)));
295                         return False;                   
296                 }
297                 fill_domain_username(name, domain->name, username, True);
298                 len = strlen(name);
299                 buf_len = len + 1;
300                 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
301                         DEBUG(1, ("out of memory\n"));
302                         return False;                   
303                 }
304                 memcpy(buf, name, buf_len);
305                 
306                 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
307                           "'Domain Users' in domain %s\n",
308                           name, domain->name ));
309                 
310                 /* user is the only member */
311                 *num_gr_mem = 1;
312         }
313                 
314         *gr_mem = buf;
315         *gr_mem_len = buf_len;
316         
317         DEBUG(10, ("fill_grent_mem_domain_users: num_mem = %u, len = %u, mem = %s\n", 
318                    (unsigned int)*num_gr_mem, 
319                    (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); 
320
321         return True;
322 }                                        
323
324 /***********************************************************************
325  Add names to a list.  Assumes  a canonical version of the string
326  in DOMAIN\user
327 ***********************************************************************/
328
329 static int namecmp( const void *a, const void *b )
330 {
331         return StrCaseCmp( * (char * const *) a, * (char * const *) b); 
332 }
333
334 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx, 
335                                    char ***list, uint32 *n_list, 
336                                    char **names, uint32 n_names )
337 {
338         char **new_list = NULL; 
339         uint32 n_new_list = 0;  
340         int i, j;       
341
342         if ( !names || (n_names == 0) )
343                 return NT_STATUS_OK;
344         
345         /* Alloc the maximum size we'll need */
346
347         if ( *list == NULL ) {
348                 if ( (new_list = TALLOC_ARRAY( ctx, char *, n_names )) == NULL ) 
349                         return NT_STATUS_NO_MEMORY;
350                 n_new_list = n_names;           
351         } else {
352                 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *, 
353                                                  (*n_list) + n_names );
354                 if ( !new_list )
355                         return NT_STATUS_NO_MEMORY;
356                 n_new_list = (*n_list) + n_names;
357         }
358
359         /* Add all names */
360
361         for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
362                 new_list[i] = talloc_strdup( new_list, names[j] );
363         }
364
365         /* search for duplicates for sorting and looking for matching
366            neighbors */
367         
368         qsort( new_list, n_new_list, sizeof(char*), QSORT_CAST namecmp );
369         
370         for ( i=1; i<n_new_list; i++ ) {
371                 if ( strcmp( new_list[i-1], new_list[i] ) == 0 ) {                      
372                         memmove( &new_list[i-1], &new_list[i], 
373                                  sizeof(char*)*(n_new_list-i) );
374                         n_new_list--;
375                 }
376         }
377
378         *list = new_list;
379         *n_list = n_new_list;   
380
381         return NT_STATUS_OK;    
382 }
383
384 /***********************************************************************
385 ***********************************************************************/
386
387 static NTSTATUS expand_groups( TALLOC_CTX *ctx, 
388                                struct winbindd_domain *d,
389                                DOM_SID *glist, uint32 n_glist,
390                                DOM_SID **new_glist, uint32 *n_new_glist,
391                                char ***members, uint32 *n_members )
392 {
393         int i, j;       
394         NTSTATUS status = NT_STATUS_OK;
395         uint32 num_names = 0;
396         uint32 *name_types = NULL;
397         char **names = NULL;
398         DOM_SID *sid_mem = NULL;
399         TALLOC_CTX *tmp_ctx = NULL;
400         DOM_SID *new_groups = NULL;
401         size_t new_groups_size = 0;     
402         
403         *members = NULL;
404         *n_members = 0; 
405         *new_glist = NULL;
406         *n_new_glist = 0;       
407         
408         for ( i=0; i<n_glist; i++ ) {
409                 tmp_ctx = talloc_new( ctx );
410
411                 /* Lookup the group membership */
412
413                 status = d->methods->lookup_groupmem(d, tmp_ctx, 
414                                                      &glist[i], &num_names,
415                                                      &sid_mem, &names, 
416                                                      &name_types);
417                 if ( !NT_STATUS_IS_OK(status) ) 
418                         goto out;
419                                 
420                 /* Separate users and groups into two lists */
421
422                 for ( j=0; j<num_names; j++ ) {
423
424                         /* Users */
425                         if ( name_types[j] == SID_NAME_USER ||
426                              name_types[j] == SID_NAME_COMPUTER )
427                         {
428                                 status = add_names_to_list( ctx, members, 
429                                                             n_members,
430                                                             names+j, 1 );
431                                 if ( !NT_STATUS_IS_OK(status) )
432                                         goto out;
433
434                                 continue;                               
435                         } 
436
437                         /* Groups */
438                         if ( name_types[j] == SID_NAME_DOM_GRP ||
439                              name_types[j] == SID_NAME_ALIAS )
440                         {
441                                 BOOL ret;
442                                 
443                                 ret = add_sid_to_array_unique( ctx, 
444                                                                &sid_mem[j],
445                                                                &new_groups, 
446                                                                &new_groups_size );
447                                 if ( !ret ) {
448                                         status = NT_STATUS_NO_MEMORY;
449                                         goto out;
450                                 }
451
452                                 continue;                               
453                         }
454                 }
455
456                 TALLOC_FREE( tmp_ctx );
457         }
458
459         new_glist = &new_groups;
460         *n_new_glist = (uint32)new_groups_size; 
461         
462  out:
463         TALLOC_FREE( tmp_ctx );
464         
465         return status;  
466 }
467
468 /***********************************************************************
469  Fill in the group membership field of a NT group given by group_sid
470 ***********************************************************************/
471
472 static BOOL fill_grent_mem(struct winbindd_domain *domain,
473                            struct winbindd_cli_state *state,
474                            DOM_SID *group_sid, 
475                            enum lsa_SidType group_name_type, 
476                            size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
477 {
478         uint32 num_names = 0;
479         unsigned int buf_len = 0, buf_ndx = 0, i;
480         char **names = NULL, *buf = NULL;
481         BOOL result = False;
482         TALLOC_CTX *mem_ctx;
483         uint32 group_rid;
484         DOM_SID *glist = NULL;
485         DOM_SID *new_glist = NULL;
486         uint32 n_glist, n_new_glist;    
487         int max_depth = lp_winbind_expand_groups();     
488
489         if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
490                 return False;
491
492         DEBUG(10, ("group SID %s\n", sid_string_static(group_sid)));
493
494         /* Initialize with no members */
495
496         *num_gr_mem = 0;
497
498         /* HACK ALERT!! This whole routine does not cope with group members
499          * from more than one domain, ie aliases. Thus we have to work it out
500          * ourselves in a special routine. */
501
502         if (domain->internal) {
503                 result = fill_passdb_alias_grmem(domain, group_sid,
504                                                num_gr_mem,
505                                                gr_mem, gr_mem_len);
506                 goto done;
507         }
508
509         /* Verify name type */
510
511         if ( !((group_name_type==SID_NAME_DOM_GRP) ||
512                ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
513         {
514                 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", 
515                           sid_string_static(group_sid), domain->name, 
516                           group_name_type));
517                 goto done;
518         }
519
520         /* OPTIMIZATION / HACK. See comment in
521            fill_grent_mem_domusers() */
522
523         sid_peek_rid( group_sid, &group_rid );
524         if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
525                 result = fill_grent_mem_domusers( mem_ctx, domain, state, 
526                                                   group_sid, group_name_type,
527                                                   num_gr_mem, gr_mem,
528                                                   gr_mem_len );
529                 goto done;              
530         }
531
532         /* Real work goes here.  Create a list of group names to
533            expand startign with the initial one.  Pass that to 
534            expand_groups() which returns a list of more group names
535            to expand.  Do this up to the max search depth. */
536
537         if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
538                 result = False;
539                 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
540                 goto done;
541         }
542         sid_copy( &glist[0], group_sid );       
543         n_glist = 1;    
544
545         for ( i=0; i<max_depth && glist; i++ ) {
546                 uint32 n_members = 0;
547                 char **members = NULL;
548                 NTSTATUS nt_status;             
549
550                 nt_status = expand_groups( mem_ctx, domain,
551                                            glist, n_glist, 
552                                            &new_glist, &n_new_glist,
553                                            &members, &n_members);
554                 if ( !NT_STATUS_IS_OK(nt_status) ) {
555                         result = False;                 
556                         goto done;
557                 }               
558                 
559                 /* Add new group members to list */
560
561                 nt_status = add_names_to_list( mem_ctx, &names, &num_names, 
562                                                members, n_members );
563                 if ( !NT_STATUS_IS_OK(nt_status) ) {
564                         result = False;                 
565                         goto done;
566                 }
567                 
568                 TALLOC_FREE( members );         
569
570                 /* If we have no more groups to expand, break out
571                    early */
572
573                 if ( !&new_glist )
574                         break;
575
576                 /* One more round */
577                 TALLOC_FREE(glist);
578                 glist = 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 }