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