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