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