s3:winbind: Add winbindd_print_groupmem
[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 static void getgrgid_recv(void *private_data, bool success, const char *sid)
1012 {
1013         struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
1014         enum lsa_SidType name_type;
1015         DOM_SID group_sid;
1016
1017         if (success) {
1018                 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
1019                           (unsigned long)(state->request->data.gid), sid));
1020
1021                 if (!string_to_sid(&group_sid, sid)) {
1022                         DEBUG(1,("getgrgid_recv: Could not convert sid %s "
1023                                 "from string\n", sid));
1024                         request_error(state);
1025                         return;
1026                 }
1027
1028                 winbindd_getgrsid(state, group_sid);
1029                 return;
1030         }
1031
1032         /* Ok, this might be "ours", i.e. an alias */
1033         if (pdb_gid_to_sid(state->request->data.gid, &group_sid) &&
1034             lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
1035             (name_type == SID_NAME_ALIAS)) {
1036                 /* Hey, got an alias */
1037                 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
1038                           (unsigned long)(state->request->data.gid), sid));
1039                 winbindd_getgrsid(state, group_sid);
1040                 return;
1041         }
1042
1043         DEBUG(1, ("could not convert gid %lu to sid\n",
1044                   (unsigned long)state->request->data.gid));
1045         request_error(state);
1046 }
1047
1048 /* Return a group structure from a gid number */
1049 void winbindd_getgrgid(struct winbindd_cli_state *state)
1050 {
1051         gid_t gid = state->request->data.gid;
1052
1053         DEBUG(3, ("[%5lu]: getgrgid %lu\n",
1054                   (unsigned long)state->pid,
1055                   (unsigned long)gid));
1056
1057         /* always use the async interface */
1058         winbindd_gid2sid_async(state->mem_ctx, gid, getgrgid_recv, state);
1059 }
1060
1061 /*
1062  * set/get/endgrent functions
1063  */
1064
1065 /* "Rewind" file pointer for group database enumeration */
1066
1067 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
1068 {
1069         struct winbindd_domain *domain;
1070
1071         DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
1072
1073         /* Check user has enabled this */
1074
1075         if (!lp_winbind_enum_groups()) {
1076                 return False;
1077         }
1078
1079         /* Free old static data if it exists */
1080
1081         if (state->getgrent_state != NULL) {
1082                 free_getent_state(state->getgrent_state);
1083                 state->getgrent_state = NULL;
1084         }
1085
1086         /* Create sam pipes for each domain we know about */
1087
1088         for (domain = domain_list(); domain != NULL; domain = domain->next) {
1089                 struct getent_state *domain_state;
1090
1091                 /* Create a state record for this domain */
1092
1093                 /* don't add our domaina if we are a PDC or if we
1094                    are a member of a Samba domain */
1095
1096                 if ( lp_winbind_trusted_domains_only() && domain->primary )
1097                 {
1098                         continue;
1099                 }
1100
1101                 domain_state = SMB_MALLOC_P(struct getent_state);
1102                 if (!domain_state) {
1103                         DEBUG(1, ("winbindd_setgrent: "
1104                                   "malloc failed for domain_state!\n"));
1105                         return False;
1106                 }
1107
1108                 ZERO_STRUCTP(domain_state);
1109
1110                 fstrcpy(domain_state->domain_name, domain->name);
1111
1112                 /* Add to list of open domains */
1113
1114                 DLIST_ADD(state->getgrent_state, domain_state);
1115         }
1116
1117         state->getgrent_initialized = True;
1118         return True;
1119 }
1120
1121 void winbindd_setgrent(struct winbindd_cli_state *state)
1122 {
1123         if (winbindd_setgrent_internal(state)) {
1124                 request_ok(state);
1125         } else {
1126                 request_error(state);
1127         }
1128 }
1129
1130 /* Close file pointer to ntdom group database */
1131
1132 void winbindd_endgrent(struct winbindd_cli_state *state)
1133 {
1134         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
1135
1136         free_getent_state(state->getgrent_state);
1137         state->getgrent_initialized = False;
1138         state->getgrent_state = NULL;
1139         request_ok(state);
1140 }
1141
1142 /* Get the list of domain groups and domain aliases for a domain.  We fill in
1143    the sam_entries and num_sam_entries fields with domain group information.
1144    Return True if some groups were returned, False otherwise. */
1145
1146 bool get_sam_group_entries(struct getent_state *ent)
1147 {
1148         NTSTATUS status;
1149         uint32 num_entries;
1150         struct acct_info *name_list = NULL;
1151         TALLOC_CTX *mem_ctx;
1152         bool result = False;
1153         struct acct_info *sam_grp_entries = NULL;
1154         struct winbindd_domain *domain;
1155
1156         if (ent->got_sam_entries)
1157                 return False;
1158
1159         if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1160                                           ent->domain_name))) {
1161                 DEBUG(1, ("get_sam_group_entries: "
1162                           "could not create talloc context!\n"));
1163                 return False;
1164         }
1165
1166         /* Free any existing group info */
1167
1168         SAFE_FREE(ent->sam_entries);
1169         ent->num_sam_entries = 0;
1170         ent->got_sam_entries = True;
1171
1172         /* Enumerate domain groups */
1173
1174         num_entries = 0;
1175
1176         if (!(domain = find_domain_from_name(ent->domain_name))) {
1177                 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1178                           ent->domain_name));
1179                 goto done;
1180         }
1181
1182         /* always get the domain global groups */
1183
1184         status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1185                                                   &sam_grp_entries);
1186
1187         if (!NT_STATUS_IS_OK(status)) {
1188                 DEBUG(3, ("get_sam_group_entries: "
1189                           "could not enumerate domain groups! Error: %s\n",
1190                           nt_errstr(status)));
1191                 result = False;
1192                 goto done;
1193         }
1194
1195         /* Copy entries into return buffer */
1196
1197         if (num_entries) {
1198                 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1199                 if (!name_list) {
1200                         DEBUG(0,("get_sam_group_entries: Failed to malloc "
1201                                  "memory for %d domain groups!\n",
1202                                  num_entries));
1203                         result = False;
1204                         goto done;
1205                 }
1206                 memcpy(name_list, sam_grp_entries,
1207                         num_entries * sizeof(struct acct_info));
1208         }
1209
1210         ent->num_sam_entries = num_entries;
1211
1212         /* get the domain local groups if we are a member of a native win2k
1213          * domain and are not using LDAP to get the groups */
1214
1215         if ( ( lp_security() != SEC_ADS && domain->native_mode
1216                 && domain->primary) || domain->internal )
1217         {
1218                 DEBUG(4,("get_sam_group_entries: %s domain; "
1219                          "enumerating local groups as well\n",
1220                          domain->native_mode ? "Native Mode 2k":
1221                                                 "BUILTIN or local"));
1222
1223                 status = domain->methods->enum_local_groups(domain, mem_ctx,
1224                                                             &num_entries,
1225                                                             &sam_grp_entries);
1226
1227                 if ( !NT_STATUS_IS_OK(status) ) {
1228                         DEBUG(3,("get_sam_group_entries: "
1229                                 "Failed to enumerate "
1230                                 "domain local groups with error %s!\n",
1231                                 nt_errstr(status)));
1232                         num_entries = 0;
1233                 }
1234                 else
1235                         DEBUG(4,("get_sam_group_entries: "
1236                                  "Returned %d local groups\n",
1237                                  num_entries));
1238
1239                 /* Copy entries into return buffer */
1240
1241                 if ( num_entries ) {
1242                         name_list = SMB_REALLOC_ARRAY(name_list,
1243                                                       struct acct_info,
1244                                                       ent->num_sam_entries+
1245                                                         num_entries);
1246                         if (!name_list) {
1247                                 DEBUG(0,("get_sam_group_entries: "
1248                                          "Failed to realloc more memory "
1249                                          "for %d local groups!\n",
1250                                          num_entries));
1251                                 result = False;
1252                                 goto done;
1253                         }
1254
1255                         memcpy(&name_list[ent->num_sam_entries],
1256                                 sam_grp_entries,
1257                                 num_entries * sizeof(struct acct_info));
1258                 }
1259
1260                 ent->num_sam_entries += num_entries;
1261         }
1262
1263
1264         /* Fill in remaining fields */
1265
1266         ent->sam_entries = name_list;
1267         ent->sam_entry_index = 0;
1268
1269         result = (ent->num_sam_entries > 0);
1270
1271  done:
1272         talloc_destroy(mem_ctx);
1273
1274         return result;
1275 }
1276
1277 /* Fetch next group entry from ntdom database */
1278
1279 #define MAX_GETGRENT_GROUPS 500
1280
1281 void winbindd_getgrent(struct winbindd_cli_state *state)
1282 {
1283         struct getent_state *ent;
1284         struct winbindd_gr *group_list = NULL;
1285         int num_groups, group_list_ndx, gr_mem_list_len = 0;
1286         char *gr_mem_list = NULL;
1287
1288         DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1289
1290         /* Check user has enabled this */
1291
1292         if (!lp_winbind_enum_groups()) {
1293                 request_error(state);
1294                 return;
1295         }
1296
1297         num_groups = MIN(MAX_GETGRENT_GROUPS, state->request->data.num_entries);
1298
1299         if (num_groups == 0) {
1300                 request_error(state);
1301                 return;
1302         }
1303
1304         group_list = talloc_zero_array(state->mem_ctx, struct winbindd_gr,
1305                                        num_groups);
1306         if (!group_list) {
1307                 request_error(state);
1308                 return;
1309         }
1310         state->response->extra_data.data = group_list;
1311
1312         state->response->data.num_entries = 0;
1313
1314         if (!state->getgrent_initialized)
1315                 winbindd_setgrent_internal(state);
1316
1317         if (!(ent = state->getgrent_state)) {
1318                 request_error(state);
1319                 return;
1320         }
1321
1322         /* Start sending back groups */
1323
1324         for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1325                 struct acct_info *name_list = NULL;
1326                 fstring domain_group_name;
1327                 uint32 result;
1328                 gid_t group_gid;
1329                 size_t gr_mem_len;
1330                 char *gr_mem;
1331                 DOM_SID group_sid;
1332                 struct winbindd_domain *domain;
1333
1334                 /* Do we need to fetch another chunk of groups? */
1335
1336         tryagain:
1337
1338                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1339                            ent->sam_entry_index, ent->num_sam_entries));
1340
1341                 if (ent->num_sam_entries == ent->sam_entry_index) {
1342
1343                         while(ent && !get_sam_group_entries(ent)) {
1344                                 struct getent_state *next_ent;
1345
1346                                 DEBUG(10, ("freeing state info for domain %s\n",
1347                                            ent->domain_name));
1348
1349                                 /* Free state information for this domain */
1350
1351                                 SAFE_FREE(ent->sam_entries);
1352
1353                                 next_ent = ent->next;
1354                                 DLIST_REMOVE(state->getgrent_state, ent);
1355
1356                                 SAFE_FREE(ent);
1357                                 ent = next_ent;
1358                         }
1359
1360                         /* No more domains */
1361
1362                         if (!ent)
1363                                 break;
1364                 }
1365
1366                 name_list = (struct acct_info *)ent->sam_entries;
1367
1368                 if (!(domain = find_domain_from_name(ent->domain_name))) {
1369                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1370                                   ent->domain_name));
1371                         result = False;
1372                         goto done;
1373                 }
1374
1375                 /* Lookup group info */
1376
1377                 sid_copy(&group_sid, &domain->sid);
1378                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1379
1380                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->have_idmap_config
1381                                                       ? domain->name : "",
1382                                                       &group_sid, &group_gid)))
1383                 {
1384                         union unid_t id;
1385                         enum lsa_SidType type;
1386
1387                         DEBUG(10, ("SID %s not in idmap\n",
1388                                    sid_string_dbg(&group_sid)));
1389
1390                         if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1391                                 DEBUG(1,("could not look up gid for group %s\n",
1392                                          name_list[ent->sam_entry_index].acct_name));
1393                                 ent->sam_entry_index++;
1394                                 goto tryagain;
1395                         }
1396
1397                         if ((type != SID_NAME_DOM_GRP) &&
1398                             (type != SID_NAME_ALIAS) &&
1399                             (type != SID_NAME_WKN_GRP)) {
1400                                 DEBUG(1, ("Group %s is a %s, not a group\n",
1401                                           sid_type_lookup(type),
1402                                           name_list[ent->sam_entry_index].acct_name));
1403                                 ent->sam_entry_index++;
1404                                 goto tryagain;
1405                         }
1406                         group_gid = id.gid;
1407                 }
1408
1409                 DEBUG(10, ("got gid %lu for group %lu\n",
1410                            (unsigned long)group_gid,
1411                            (unsigned long)name_list[ent->sam_entry_index].rid));
1412
1413                 /* Fill in group entry */
1414
1415                 fill_domain_username(domain_group_name, ent->domain_name,
1416                          name_list[ent->sam_entry_index].acct_name, True);
1417
1418                 result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
1419                                     ent->domain_name,
1420                                     name_list[ent->sam_entry_index].acct_name,
1421                                     group_gid);
1422
1423                 /* Fill in group membership entry */
1424
1425                 if (result) {
1426                         size_t num_gr_mem = 0;
1427                         DOM_SID member_sid;
1428                         group_list[group_list_ndx].num_gr_mem = 0;
1429                         gr_mem = NULL;
1430                         gr_mem_len = 0;
1431
1432                         /* Get group membership */
1433                         if (state->request->cmd == WINBINDD_GETGRLST) {
1434                                 result = True;
1435                         } else {
1436                                 sid_copy(&member_sid, &domain->sid);
1437                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1438                                 result = fill_grent_mem(
1439                                         domain,
1440                                         NULL,
1441                                         &member_sid,
1442                                         SID_NAME_DOM_GRP,
1443                                         &num_gr_mem,
1444                                         &gr_mem, &gr_mem_len);
1445
1446                                 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1447                         }
1448                 }
1449
1450                 if (result) {
1451                         /* Append to group membership list */
1452                         gr_mem_list = (char *)SMB_REALLOC(
1453                                 gr_mem_list, gr_mem_list_len + gr_mem_len);
1454
1455                         if (!gr_mem_list &&
1456                             (group_list[group_list_ndx].num_gr_mem != 0)) {
1457                                 DEBUG(0, ("out of memory\n"));
1458                                 gr_mem_list_len = 0;
1459                                 break;
1460                         }
1461
1462                         DEBUG(10, ("list_len = %d, mem_len = %u\n",
1463                                    gr_mem_list_len, (unsigned int)gr_mem_len));
1464
1465                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1466                                gr_mem_len);
1467
1468                         SAFE_FREE(gr_mem);
1469
1470                         group_list[group_list_ndx].gr_mem_ofs =
1471                                 gr_mem_list_len;
1472
1473                         gr_mem_list_len += gr_mem_len;
1474                 }
1475
1476                 ent->sam_entry_index++;
1477
1478                 /* Add group to return list */
1479
1480                 if (result) {
1481
1482                         DEBUG(10, ("adding group num_entries = %d\n",
1483                                    state->response->data.num_entries));
1484
1485                         group_list_ndx++;
1486                         state->response->data.num_entries++;
1487
1488                         state->response->length +=
1489                                 sizeof(struct winbindd_gr);
1490
1491                 } else {
1492                         DEBUG(0, ("could not lookup domain group %s\n",
1493                                   domain_group_name));
1494                 }
1495         }
1496
1497         /* Copy the list of group memberships to the end of the extra data */
1498
1499         if (group_list_ndx == 0)
1500                 goto done;
1501
1502         state->response->extra_data.data = talloc_realloc_size(
1503                 state->mem_ctx, state->response->extra_data.data,
1504                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1505
1506         if (!state->response->extra_data.data) {
1507                 DEBUG(0, ("out of memory\n"));
1508                 group_list_ndx = 0;
1509                 SAFE_FREE(gr_mem_list);
1510                 request_error(state);
1511                 return;
1512         }
1513
1514         memcpy(&((char *)state->response->extra_data.data)
1515                [group_list_ndx * sizeof(struct winbindd_gr)],
1516                gr_mem_list, gr_mem_list_len);
1517
1518         state->response->length += gr_mem_list_len;
1519
1520         DEBUG(10, ("returning %d groups, length = %d\n",
1521                    group_list_ndx, gr_mem_list_len));
1522
1523         /* Out of domains */
1524
1525  done:
1526
1527         SAFE_FREE(gr_mem_list);
1528
1529         if (group_list_ndx > 0)
1530                 request_ok(state);
1531         else
1532                 request_error(state);
1533 }
1534
1535 /* List domain groups without mapping to unix ids */
1536 void winbindd_list_groups(struct winbindd_cli_state *state)
1537 {
1538         winbindd_list_ent(state, LIST_GROUPS);
1539 }
1540
1541 /* Get user supplementary groups.  This is much quicker than trying to
1542    invert the groups database.  We merge the groups from the gids and
1543    other_sids info3 fields as trusted domain, universal group
1544    memberships, and nested groups (win2k native mode only) are not
1545    returned by the getgroups RPC call but are present in the info3. */
1546
1547 struct getgroups_state {
1548         struct winbindd_cli_state *state;
1549         struct winbindd_domain *domain;
1550         char *domname;
1551         char *username;
1552         DOM_SID user_sid;
1553
1554         const DOM_SID *token_sids;
1555         size_t i, num_token_sids;
1556
1557         gid_t *token_gids;
1558         size_t num_token_gids;
1559 };
1560
1561
1562 /* Get user supplementary sids. This is equivalent to the
1563    winbindd_getgroups() function but it involves a SID->SIDs mapping
1564    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1565    idmap. This call is designed to be used with applications that need
1566    to do ACL evaluation themselves. Note that the cached info3 data is
1567    not used
1568
1569    this function assumes that the SID that comes in is a user SID. If
1570    you pass in another type of SID then you may get unpredictable
1571    results.
1572 */
1573
1574 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1575                              size_t num_sids);
1576
1577 void winbindd_getusersids(struct winbindd_cli_state *state)
1578 {
1579         DOM_SID *user_sid;
1580
1581         /* Ensure null termination */
1582         state->request->data.sid[sizeof(state->request->data.sid)-1]='\0';
1583
1584         user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1585         if (user_sid == NULL) {
1586                 DEBUG(1, ("talloc failed\n"));
1587                 request_error(state);
1588                 return;
1589         }
1590
1591         if (!string_to_sid(user_sid, state->request->data.sid)) {
1592                 DEBUG(1, ("Could not get convert sid %s from string\n",
1593                           state->request->data.sid));
1594                 request_error(state);
1595                 return;
1596         }
1597
1598         winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1599                                 state);
1600 }
1601
1602 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1603                              size_t num_sids)
1604 {
1605         struct winbindd_cli_state *state =
1606                 (struct winbindd_cli_state *)private_data;
1607         char *ret = NULL;
1608         unsigned ofs, ret_size = 0;
1609         size_t i;
1610
1611         if (!success) {
1612                 request_error(state);
1613                 return;
1614         }
1615
1616         /* work out the response size */
1617         for (i = 0; i < num_sids; i++) {
1618                 fstring s;
1619                 sid_to_fstring(s, &sids[i]);
1620                 ret_size += strlen(s) + 1;
1621         }
1622
1623         /* build the reply */
1624         ret = talloc_array(state->mem_ctx, char, ret_size);
1625         if (!ret) {
1626                 DEBUG(0, ("malloc failed\n"));
1627                 request_error(state);
1628                 return;
1629         }
1630         ofs = 0;
1631         for (i = 0; i < num_sids; i++) {
1632                 fstring s;
1633                 sid_to_fstring(s, &sids[i]);
1634                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1635                 ofs += strlen(ret+ofs) + 1;
1636         }
1637
1638         /* Send data back to client */
1639         state->response->data.num_entries = num_sids;
1640         state->response->extra_data.data = ret;
1641         state->response->length += ret_size;
1642         request_ok(state);
1643 }
1644
1645 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1646                                                     struct winbindd_cli_state *state)
1647 {
1648         DOM_SID user_sid;
1649         NTSTATUS status;
1650
1651         char *sidstring;
1652         ssize_t len;
1653         DOM_SID *groups;
1654         uint32 num_groups;
1655
1656         /* Ensure null termination */
1657         state->request->data.sid[sizeof(state->request->data.sid)-1]='\0';
1658
1659         if (!string_to_sid(&user_sid, state->request->data.sid)) {
1660                 DEBUG(1, ("Could not get convert sid %s from string\n",
1661                           state->request->data.sid));
1662                 return WINBINDD_ERROR;
1663         }
1664
1665         status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1666                                                     &user_sid, &num_groups,
1667                                                     &groups);
1668         if (!NT_STATUS_IS_OK(status))
1669                 return WINBINDD_ERROR;
1670
1671         if (num_groups == 0) {
1672                 state->response->data.num_entries = 0;
1673                 state->response->extra_data.data = NULL;
1674                 return WINBINDD_OK;
1675         }
1676
1677         if (!print_sidlist(state->mem_ctx,
1678                            groups, num_groups,
1679                            &sidstring, &len)) {
1680                 DEBUG(0, ("talloc failed\n"));
1681                 return WINBINDD_ERROR;
1682         }
1683
1684         state->response->extra_data.data = sidstring;
1685         state->response->length += len+1;
1686         state->response->data.num_entries = num_groups;
1687
1688         return WINBINDD_OK;
1689 }
1690
1691 enum winbindd_result winbindd_dual_getsidaliases(struct winbindd_domain *domain,
1692                                                  struct winbindd_cli_state *state)
1693 {
1694         DOM_SID *sids = NULL;
1695         size_t num_sids = 0;
1696         char *sidstr = NULL;
1697         ssize_t len;
1698         size_t i;
1699         uint32 num_aliases;
1700         uint32 *alias_rids;
1701         NTSTATUS result;
1702
1703         DEBUG(3, ("[%5lu]: getsidaliases\n", (unsigned long)state->pid));
1704
1705         sidstr = state->request->extra_data.data;
1706         if (sidstr == NULL) {
1707                 sidstr = talloc_strdup(state->mem_ctx, "\n"); /* No SID */
1708                 if (!sidstr) {
1709                         DEBUG(0, ("Out of memory\n"));
1710                         return WINBINDD_ERROR;
1711                 }
1712         }
1713
1714         DEBUG(10, ("Sidlist: %s\n", sidstr));
1715
1716         if (!parse_sidlist(state->mem_ctx, sidstr, &sids, &num_sids)) {
1717                 DEBUG(0, ("Could not parse SID list: %s\n", sidstr));
1718                 return WINBINDD_ERROR;
1719         }
1720
1721         num_aliases = 0;
1722         alias_rids = NULL;
1723
1724         result = domain->methods->lookup_useraliases(domain,
1725                                                      state->mem_ctx,
1726                                                      num_sids, sids,
1727                                                      &num_aliases,
1728                                                      &alias_rids);
1729
1730         if (!NT_STATUS_IS_OK(result)) {
1731                 DEBUG(3, ("Could not lookup_useraliases: %s\n",
1732                           nt_errstr(result)));
1733                 return WINBINDD_ERROR;
1734         }
1735
1736         num_sids = 0;
1737         sids = NULL;
1738         sidstr = NULL;
1739
1740         DEBUG(10, ("Got %d aliases\n", num_aliases));
1741
1742         for (i=0; i<num_aliases; i++) {
1743                 DOM_SID sid;
1744                 DEBUGADD(10, (" rid %d\n", alias_rids[i]));
1745                 sid_copy(&sid, &domain->sid);
1746                 sid_append_rid(&sid, alias_rids[i]);
1747                 result = add_sid_to_array(state->mem_ctx, &sid, &sids,
1748                                           &num_sids);
1749                 if (!NT_STATUS_IS_OK(result)) {
1750                         return WINBINDD_ERROR;
1751                 }
1752         }
1753
1754
1755         if (!print_sidlist(state->mem_ctx, sids, num_sids, &sidstr, &len)) {
1756                 DEBUG(0, ("Could not print_sidlist\n"));
1757                 state->response->extra_data.data = NULL;
1758                 return WINBINDD_ERROR;
1759         }
1760
1761         state->response->extra_data.data = NULL;
1762
1763         if (sidstr) {
1764                 state->response->extra_data.data = sidstr;
1765                 DEBUG(10, ("aliases_list: %s\n",
1766                            (char *)state->response->extra_data.data));
1767                 state->response->length += len+1;
1768                 state->response->data.num_entries = num_sids;
1769         }
1770
1771         return WINBINDD_OK;
1772 }
1773
1774 struct getgr_countmem {
1775         int num;
1776         size_t len;
1777 };
1778
1779 static int getgr_calc_memberlen(DATA_BLOB key, void *data, void *priv)
1780 {
1781         struct wbint_GroupMember *m = talloc_get_type_abort(
1782                 data, struct wbint_GroupMember);
1783         struct getgr_countmem *buf = (struct getgr_countmem *)priv;
1784
1785         buf->num += 1;
1786         buf->len += strlen(m->name) + 1;
1787         return 0;
1788 }
1789
1790 struct getgr_stringmem {
1791         size_t ofs;
1792         char *buf;
1793 };
1794
1795 static int getgr_unparse_members(DATA_BLOB key, void *data, void *priv)
1796 {
1797         struct wbint_GroupMember *m = talloc_get_type_abort(
1798                 data, struct wbint_GroupMember);
1799         struct getgr_stringmem *buf = (struct getgr_stringmem *)priv;
1800         int len;
1801
1802         len = strlen(m->name);
1803
1804         memcpy(buf->buf + buf->ofs, m->name, len);
1805         buf->ofs += len;
1806         buf->buf[buf->ofs] = ',';
1807         buf->ofs += 1;
1808         return 0;
1809 }
1810
1811 NTSTATUS winbindd_print_groupmembers(struct talloc_dict *members,
1812                                      TALLOC_CTX *mem_ctx,
1813                                      int *num_members, char **result)
1814 {
1815         struct getgr_countmem c;
1816         struct getgr_stringmem m;
1817         int res;
1818
1819         c.num = 0;
1820         c.len = 0;
1821
1822         res = talloc_dict_traverse(members, getgr_calc_memberlen, &c);
1823         if (res != 0) {
1824                 DEBUG(5, ("talloc_dict_traverse failed\n"));
1825                 return NT_STATUS_INTERNAL_ERROR;
1826         }
1827
1828         m.ofs = 0;
1829         m.buf = talloc_array(mem_ctx, char, c.len);
1830         if (m.buf == NULL) {
1831                 DEBUG(5, ("talloc failed\n"));
1832                 return NT_STATUS_NO_MEMORY;
1833         }
1834
1835         res = talloc_dict_traverse(members, getgr_unparse_members, &m);
1836         if (res != 0) {
1837                 DEBUG(5, ("talloc_dict_traverse failed\n"));
1838                 TALLOC_FREE(m.buf);
1839                 return NT_STATUS_INTERNAL_ERROR;
1840         }
1841         m.buf[c.len-1] = '\0';
1842
1843         *num_members = c.num;
1844         *result = m.buf;
1845         return NT_STATUS_OK;
1846 }