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