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