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