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