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