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