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