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