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