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