Merge branch 'master' of ssh://git.samba.org/data/git/samba
[bbaumbach/samba-autobuild/.git] / source3 / winbindd / winbindd_group.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon for ntdom nss module
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jeremy Allison 2001.
8    Copyright (C) Gerald (Jerry) Carter 2003.
9    Copyright (C) Volker Lendecke 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27
28 extern bool opt_nocache;
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
32
33 static void add_member(const char *domain, const char *user,
34            char **pp_members, size_t *p_num_members)
35 {
36         fstring name;
37
38         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(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                 string_to_sid(&group_sid, sid);
995                 winbindd_getgrsid(state, group_sid);
996                 return;
997         }
998
999         /* Ok, this might be "ours", i.e. an alias */
1000         if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
1001             lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
1002             (name_type == SID_NAME_ALIAS)) {
1003                 /* Hey, got an alias */
1004                 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
1005                           (unsigned long)(state->request.data.gid), sid));
1006                 winbindd_getgrsid(state, group_sid);
1007                 return;
1008         }
1009
1010         DEBUG(1, ("could not convert gid %lu to sid\n",
1011                   (unsigned long)state->request.data.gid));
1012         request_error(state);
1013 }
1014
1015 /* Return a group structure from a gid number */
1016 void winbindd_getgrgid(struct winbindd_cli_state *state)
1017 {
1018         gid_t gid = state->request.data.gid;
1019
1020         DEBUG(3, ("[%5lu]: getgrgid %lu\n",
1021                   (unsigned long)state->pid,
1022                   (unsigned long)gid));
1023
1024         /* always use the async interface */
1025         winbindd_gid2sid_async(state->mem_ctx, gid, getgrgid_recv, state);
1026 }
1027
1028 /*
1029  * set/get/endgrent functions
1030  */
1031
1032 /* "Rewind" file pointer for group database enumeration */
1033
1034 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
1035 {
1036         struct winbindd_domain *domain;
1037
1038         DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
1039
1040         /* Check user has enabled this */
1041
1042         if (!lp_winbind_enum_groups()) {
1043                 return False;
1044         }
1045
1046         /* Free old static data if it exists */
1047
1048         if (state->getgrent_state != NULL) {
1049                 free_getent_state(state->getgrent_state);
1050                 state->getgrent_state = NULL;
1051         }
1052
1053         /* Create sam pipes for each domain we know about */
1054
1055         for (domain = domain_list(); domain != NULL; domain = domain->next) {
1056                 struct getent_state *domain_state;
1057
1058                 /* Create a state record for this domain */
1059
1060                 /* don't add our domaina if we are a PDC or if we
1061                    are a member of a Samba domain */
1062
1063                 if ( lp_winbind_trusted_domains_only() && domain->primary )
1064                 {
1065                         continue;
1066                 }
1067
1068                 domain_state = SMB_MALLOC_P(struct getent_state);
1069                 if (!domain_state) {
1070                         DEBUG(1, ("winbindd_setgrent: "
1071                                   "malloc failed for domain_state!\n"));
1072                         return False;
1073                 }
1074
1075                 ZERO_STRUCTP(domain_state);
1076
1077                 fstrcpy(domain_state->domain_name, domain->name);
1078
1079                 /* Add to list of open domains */
1080
1081                 DLIST_ADD(state->getgrent_state, domain_state);
1082         }
1083
1084         state->getgrent_initialized = True;
1085         return True;
1086 }
1087
1088 void winbindd_setgrent(struct winbindd_cli_state *state)
1089 {
1090         if (winbindd_setgrent_internal(state)) {
1091                 request_ok(state);
1092         } else {
1093                 request_error(state);
1094         }
1095 }
1096
1097 /* Close file pointer to ntdom group database */
1098
1099 void winbindd_endgrent(struct winbindd_cli_state *state)
1100 {
1101         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
1102
1103         free_getent_state(state->getgrent_state);
1104         state->getgrent_initialized = False;
1105         state->getgrent_state = NULL;
1106         request_ok(state);
1107 }
1108
1109 /* Get the list of domain groups and domain aliases for a domain.  We fill in
1110    the sam_entries and num_sam_entries fields with domain group information.
1111    Return True if some groups were returned, False otherwise. */
1112
1113 bool get_sam_group_entries(struct getent_state *ent)
1114 {
1115         NTSTATUS status;
1116         uint32 num_entries;
1117         struct acct_info *name_list = NULL;
1118         TALLOC_CTX *mem_ctx;
1119         bool result = False;
1120         struct acct_info *sam_grp_entries = NULL;
1121         struct winbindd_domain *domain;
1122
1123         if (ent->got_sam_entries)
1124                 return False;
1125
1126         if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1127                                           ent->domain_name))) {
1128                 DEBUG(1, ("get_sam_group_entries: "
1129                           "could not create talloc context!\n"));
1130                 return False;
1131         }
1132
1133         /* Free any existing group info */
1134
1135         SAFE_FREE(ent->sam_entries);
1136         ent->num_sam_entries = 0;
1137         ent->got_sam_entries = True;
1138
1139         /* Enumerate domain groups */
1140
1141         num_entries = 0;
1142
1143         if (!(domain = find_domain_from_name(ent->domain_name))) {
1144                 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1145                           ent->domain_name));
1146                 goto done;
1147         }
1148
1149         /* always get the domain global groups */
1150
1151         status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1152                                                   &sam_grp_entries);
1153
1154         if (!NT_STATUS_IS_OK(status)) {
1155                 DEBUG(3, ("get_sam_group_entries: "
1156                           "could not enumerate domain groups! Error: %s\n",
1157                           nt_errstr(status)));
1158                 result = False;
1159                 goto done;
1160         }
1161
1162         /* Copy entries into return buffer */
1163
1164         if (num_entries) {
1165                 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1166                 if (!name_list) {
1167                         DEBUG(0,("get_sam_group_entries: Failed to malloc "
1168                                  "memory for %d domain groups!\n",
1169                                  num_entries));
1170                         result = False;
1171                         goto done;
1172                 }
1173                 memcpy(name_list, sam_grp_entries,
1174                         num_entries * sizeof(struct acct_info));
1175         }
1176
1177         ent->num_sam_entries = num_entries;
1178
1179         /* get the domain local groups if we are a member of a native win2k
1180          * domain and are not using LDAP to get the groups */
1181
1182         if ( ( lp_security() != SEC_ADS && domain->native_mode
1183                 && domain->primary) || domain->internal )
1184         {
1185                 DEBUG(4,("get_sam_group_entries: %s domain; "
1186                          "enumerating local groups as well\n",
1187                          domain->native_mode ? "Native Mode 2k":
1188                                                 "BUILTIN or local"));
1189
1190                 status = domain->methods->enum_local_groups(domain, mem_ctx,
1191                                                             &num_entries,
1192                                                             &sam_grp_entries);
1193
1194                 if ( !NT_STATUS_IS_OK(status) ) {
1195                         DEBUG(3,("get_sam_group_entries: "
1196                                 "Failed to enumerate "
1197                                 "domain local groups with error %s!\n",
1198                                 nt_errstr(status)));
1199                         num_entries = 0;
1200                 }
1201                 else
1202                         DEBUG(4,("get_sam_group_entries: "
1203                                  "Returned %d local groups\n",
1204                                  num_entries));
1205
1206                 /* Copy entries into return buffer */
1207
1208                 if ( num_entries ) {
1209                         name_list = SMB_REALLOC_ARRAY(name_list,
1210                                                       struct acct_info,
1211                                                       ent->num_sam_entries+
1212                                                         num_entries);
1213                         if (!name_list) {
1214                                 DEBUG(0,("get_sam_group_entries: "
1215                                          "Failed to realloc more memory "
1216                                          "for %d local groups!\n",
1217                                          num_entries));
1218                                 result = False;
1219                                 goto done;
1220                         }
1221
1222                         memcpy(&name_list[ent->num_sam_entries],
1223                                 sam_grp_entries,
1224                                 num_entries * sizeof(struct acct_info));
1225                 }
1226
1227                 ent->num_sam_entries += num_entries;
1228         }
1229
1230
1231         /* Fill in remaining fields */
1232
1233         ent->sam_entries = name_list;
1234         ent->sam_entry_index = 0;
1235
1236         result = (ent->num_sam_entries > 0);
1237
1238  done:
1239         talloc_destroy(mem_ctx);
1240
1241         return result;
1242 }
1243
1244 /* Fetch next group entry from ntdom database */
1245
1246 #define MAX_GETGRENT_GROUPS 500
1247
1248 void winbindd_getgrent(struct winbindd_cli_state *state)
1249 {
1250         struct getent_state *ent;
1251         struct winbindd_gr *group_list = NULL;
1252         int num_groups, group_list_ndx, gr_mem_list_len = 0;
1253         char *gr_mem_list = NULL;
1254
1255         DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1256
1257         /* Check user has enabled this */
1258
1259         if (!lp_winbind_enum_groups()) {
1260                 request_error(state);
1261                 return;
1262         }
1263
1264         num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1265
1266         if (num_groups == 0) {
1267                 request_error(state);
1268                 return;
1269         }
1270
1271         group_list = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups);
1272         if (!group_list) {
1273                 request_error(state);
1274                 return;
1275         }
1276         /* will be freed by process_request() */
1277         state->response.extra_data.data = group_list;
1278
1279         memset(state->response.extra_data.data, '\0',
1280                 num_groups * sizeof(struct winbindd_gr) );
1281
1282         state->response.data.num_entries = 0;
1283
1284         if (!state->getgrent_initialized)
1285                 winbindd_setgrent_internal(state);
1286
1287         if (!(ent = state->getgrent_state)) {
1288                 request_error(state);
1289                 return;
1290         }
1291
1292         /* Start sending back groups */
1293
1294         for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1295                 struct acct_info *name_list = NULL;
1296                 fstring domain_group_name;
1297                 uint32 result;
1298                 gid_t group_gid;
1299                 size_t gr_mem_len;
1300                 char *gr_mem;
1301                 DOM_SID group_sid;
1302                 struct winbindd_domain *domain;
1303
1304                 /* Do we need to fetch another chunk of groups? */
1305
1306         tryagain:
1307
1308                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1309                            ent->sam_entry_index, ent->num_sam_entries));
1310
1311                 if (ent->num_sam_entries == ent->sam_entry_index) {
1312
1313                         while(ent && !get_sam_group_entries(ent)) {
1314                                 struct getent_state *next_ent;
1315
1316                                 DEBUG(10, ("freeing state info for domain %s\n",
1317                                            ent->domain_name));
1318
1319                                 /* Free state information for this domain */
1320
1321                                 SAFE_FREE(ent->sam_entries);
1322
1323                                 next_ent = ent->next;
1324                                 DLIST_REMOVE(state->getgrent_state, ent);
1325
1326                                 SAFE_FREE(ent);
1327                                 ent = next_ent;
1328                         }
1329
1330                         /* No more domains */
1331
1332                         if (!ent)
1333                                 break;
1334                 }
1335
1336                 name_list = (struct acct_info *)ent->sam_entries;
1337
1338                 if (!(domain = find_domain_from_name(ent->domain_name))) {
1339                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1340                                   ent->domain_name));
1341                         result = False;
1342                         goto done;
1343                 }
1344
1345                 /* Lookup group info */
1346
1347                 sid_copy(&group_sid, &domain->sid);
1348                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1349
1350                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->name, &group_sid,
1351                                                       &group_gid))) {
1352                         union unid_t id;
1353                         enum lsa_SidType type;
1354
1355                         DEBUG(10, ("SID %s not in idmap\n",
1356                                    sid_string_dbg(&group_sid)));
1357
1358                         if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1359                                 DEBUG(1,("could not look up gid for group %s\n",
1360                                          name_list[ent->sam_entry_index].acct_name));
1361                                 ent->sam_entry_index++;
1362                                 goto tryagain;
1363                         }
1364
1365                         if ((type != SID_NAME_DOM_GRP) &&
1366                             (type != SID_NAME_ALIAS) &&
1367                             (type != SID_NAME_WKN_GRP)) {
1368                                 DEBUG(1, ("Group %s is a %s, not a group\n",
1369                                           sid_type_lookup(type),
1370                                           name_list[ent->sam_entry_index].acct_name));
1371                                 ent->sam_entry_index++;
1372                                 goto tryagain;
1373                         }
1374                         group_gid = id.gid;
1375                 }
1376
1377                 DEBUG(10, ("got gid %lu for group %lu\n",
1378                            (unsigned long)group_gid,
1379                            (unsigned long)name_list[ent->sam_entry_index].rid));
1380
1381                 /* Fill in group entry */
1382
1383                 fill_domain_username(domain_group_name, ent->domain_name,
1384                          name_list[ent->sam_entry_index].acct_name, True);
1385
1386                 result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
1387                                     ent->domain_name,
1388                                     name_list[ent->sam_entry_index].acct_name,
1389                                     group_gid);
1390
1391                 /* Fill in group membership entry */
1392
1393                 if (result) {
1394                         size_t num_gr_mem = 0;
1395                         DOM_SID member_sid;
1396                         group_list[group_list_ndx].num_gr_mem = 0;
1397                         gr_mem = NULL;
1398                         gr_mem_len = 0;
1399
1400                         /* Get group membership */
1401                         if (state->request.cmd == WINBINDD_GETGRLST) {
1402                                 result = True;
1403                         } else {
1404                                 sid_copy(&member_sid, &domain->sid);
1405                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1406                                 result = fill_grent_mem(
1407                                         domain,
1408                                         NULL,
1409                                         &member_sid,
1410                                         SID_NAME_DOM_GRP,
1411                                         &num_gr_mem,
1412                                         &gr_mem, &gr_mem_len);
1413
1414                                 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1415                         }
1416                 }
1417
1418                 if (result) {
1419                         /* Append to group membership list */
1420                         gr_mem_list = (char *)SMB_REALLOC(
1421                                 gr_mem_list, gr_mem_list_len + gr_mem_len);
1422
1423                         if (!gr_mem_list &&
1424                             (group_list[group_list_ndx].num_gr_mem != 0)) {
1425                                 DEBUG(0, ("out of memory\n"));
1426                                 gr_mem_list_len = 0;
1427                                 break;
1428                         }
1429
1430                         DEBUG(10, ("list_len = %d, mem_len = %u\n",
1431                                    gr_mem_list_len, (unsigned int)gr_mem_len));
1432
1433                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1434                                gr_mem_len);
1435
1436                         SAFE_FREE(gr_mem);
1437
1438                         group_list[group_list_ndx].gr_mem_ofs =
1439                                 gr_mem_list_len;
1440
1441                         gr_mem_list_len += gr_mem_len;
1442                 }
1443
1444                 ent->sam_entry_index++;
1445
1446                 /* Add group to return list */
1447
1448                 if (result) {
1449
1450                         DEBUG(10, ("adding group num_entries = %d\n",
1451                                    state->response.data.num_entries));
1452
1453                         group_list_ndx++;
1454                         state->response.data.num_entries++;
1455
1456                         state->response.length +=
1457                                 sizeof(struct winbindd_gr);
1458
1459                 } else {
1460                         DEBUG(0, ("could not lookup domain group %s\n",
1461                                   domain_group_name));
1462                 }
1463         }
1464
1465         /* Copy the list of group memberships to the end of the extra data */
1466
1467         if (group_list_ndx == 0)
1468                 goto done;
1469
1470         state->response.extra_data.data = SMB_REALLOC(
1471                 state->response.extra_data.data,
1472                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1473
1474         if (!state->response.extra_data.data) {
1475                 DEBUG(0, ("out of memory\n"));
1476                 group_list_ndx = 0;
1477                 SAFE_FREE(gr_mem_list);
1478                 request_error(state);
1479                 return;
1480         }
1481
1482         memcpy(&((char *)state->response.extra_data.data)
1483                [group_list_ndx * sizeof(struct winbindd_gr)],
1484                gr_mem_list, gr_mem_list_len);
1485
1486         state->response.length += gr_mem_list_len;
1487
1488         DEBUG(10, ("returning %d groups, length = %d\n",
1489                    group_list_ndx, gr_mem_list_len));
1490
1491         /* Out of domains */
1492
1493  done:
1494
1495         SAFE_FREE(gr_mem_list);
1496
1497         if (group_list_ndx > 0)
1498                 request_ok(state);
1499         else
1500                 request_error(state);
1501 }
1502
1503 /* List domain groups without mapping to unix ids */
1504 void winbindd_list_groups(struct winbindd_cli_state *state)
1505 {
1506         winbindd_list_ent(state, LIST_GROUPS);
1507 }
1508
1509 /* Get user supplementary groups.  This is much quicker than trying to
1510    invert the groups database.  We merge the groups from the gids and
1511    other_sids info3 fields as trusted domain, universal group
1512    memberships, and nested groups (win2k native mode only) are not
1513    returned by the getgroups RPC call but are present in the info3. */
1514
1515 struct getgroups_state {
1516         struct winbindd_cli_state *state;
1517         struct winbindd_domain *domain;
1518         char *domname;
1519         char *username;
1520         DOM_SID user_sid;
1521
1522         const DOM_SID *token_sids;
1523         size_t i, num_token_sids;
1524
1525         gid_t *token_gids;
1526         size_t num_token_gids;
1527 };
1528
1529 static void getgroups_usersid_recv(void *private_data, bool success,
1530                                    const DOM_SID *sid, enum lsa_SidType type);
1531 static void getgroups_tokensids_recv(void *private_data, bool success,
1532                                      DOM_SID *token_sids, size_t num_token_sids);
1533 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1534
1535 void winbindd_getgroups(struct winbindd_cli_state *state)
1536 {
1537         struct getgroups_state *s;
1538         char *real_name = NULL;
1539         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1540
1541         /* Ensure null termination */
1542         state->request.data.username
1543                 [sizeof(state->request.data.username)-1]='\0';
1544
1545         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1546                   state->request.data.username));
1547
1548         /* Parse domain and username */
1549
1550         s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1551         if (s == NULL) {
1552                 DEBUG(0, ("talloc failed\n"));
1553                 request_error(state);
1554                 return;
1555         }
1556
1557         s->state = state;
1558
1559         nt_status = normalize_name_unmap(state->mem_ctx,
1560                                          state->request.data.username,
1561                                          &real_name);
1562
1563         /* Reset the real_name pointer if we didn't do anything
1564            productive in the above call */
1565         if (!NT_STATUS_IS_OK(nt_status) &&
1566             !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
1567         {
1568                 real_name = state->request.data.username;
1569         }
1570
1571         if (!parse_domain_user_talloc(state->mem_ctx, real_name,
1572                                       &s->domname, &s->username)) {
1573                 DEBUG(5, ("Could not parse domain user: %s\n",
1574                           real_name));
1575
1576                 /* error out if we do not have nested group support */
1577
1578                 if ( !lp_winbind_nested_groups() ) {
1579                         request_error(state);
1580                         return;
1581                 }
1582
1583                 s->domname = talloc_strdup(state->mem_ctx,
1584                                            get_global_sam_name());
1585                 s->username = talloc_strdup(state->mem_ctx,
1586                                             state->request.data.username);
1587         }
1588
1589         /* Get info for the domain (either by short domain name or
1590            DNS name in the case of a UPN) */
1591
1592         s->domain = find_domain_from_name_noinit(s->domname);
1593         if (!s->domain) {
1594                 char *p = strchr(s->username, '@');
1595
1596                 if (p) {
1597                         s->domain = find_domain_from_name_noinit(p+1);
1598                 }
1599
1600         }
1601
1602         if (s->domain == NULL) {
1603                 DEBUG(7, ("could not find domain entry for domain %s\n",
1604                           s->domname));
1605                 request_error(state);
1606                 return;
1607         }
1608
1609         if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1610                 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1611                          "getgroups() for %s\\%s.\n", s->domname,
1612                          s->username));
1613                 request_error(state);
1614                 return;
1615         }
1616
1617         /* Get rid and name type from name.  The following costs 1 packet */
1618
1619         winbindd_lookupname_async(state->mem_ctx,
1620                                   s->domname, s->username,
1621                                   getgroups_usersid_recv,
1622                                   WINBINDD_GETGROUPS, s);
1623 }
1624
1625 static void getgroups_usersid_recv(void *private_data, bool success,
1626                                    const DOM_SID *sid, enum lsa_SidType type)
1627 {
1628         struct getgroups_state *s =
1629                 (struct getgroups_state *)private_data;
1630
1631         if ((!success) ||
1632             ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1633                 request_error(s->state);
1634                 return;
1635         }
1636
1637         sid_copy(&s->user_sid, sid);
1638
1639         winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1640                                 getgroups_tokensids_recv, s);
1641 }
1642
1643 static void getgroups_tokensids_recv(void *private_data, bool success,
1644                                      DOM_SID *token_sids, size_t num_token_sids)
1645 {
1646         struct getgroups_state *s =
1647                 (struct getgroups_state *)private_data;
1648
1649         /* We need at least the user sid and the primary group in the token,
1650          * otherwise it's an error */
1651
1652         if ((!success) || (num_token_sids < 2)) {
1653                 request_error(s->state);
1654                 return;
1655         }
1656
1657         s->token_sids = token_sids;
1658         s->num_token_sids = num_token_sids;
1659         s->i = 0;
1660
1661         s->token_gids = NULL;
1662         s->num_token_gids = 0;
1663
1664         getgroups_sid2gid_recv(s, False, 0);
1665 }
1666
1667 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1668 {
1669         struct getgroups_state *s =
1670                 (struct getgroups_state *)private_data;
1671
1672         if (success) {
1673                 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1674                                         &s->token_gids,
1675                                         &s->num_token_gids)) {
1676                         return;
1677                 }
1678         }
1679
1680         if (s->i < s->num_token_sids) {
1681                 const DOM_SID *sid = &s->token_sids[s->i];
1682                 s->i += 1;
1683
1684                 if (sid_equal(sid, &s->user_sid)) {
1685                         getgroups_sid2gid_recv(s, False, 0);
1686                         return;
1687                 }
1688
1689                 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1690                                        getgroups_sid2gid_recv, s);
1691                 return;
1692         }
1693
1694         s->state->response.data.num_entries = s->num_token_gids;
1695         if (s->num_token_gids) {
1696                 /* s->token_gids are talloced */
1697                 s->state->response.extra_data.data =
1698                         smb_xmemdup(s->token_gids,
1699                                         s->num_token_gids * sizeof(gid_t));
1700                 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1701         }
1702         request_ok(s->state);
1703 }
1704
1705 /* Get user supplementary sids. This is equivalent to the
1706    winbindd_getgroups() function but it involves a SID->SIDs mapping
1707    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1708    idmap. This call is designed to be used with applications that need
1709    to do ACL evaluation themselves. Note that the cached info3 data is
1710    not used
1711
1712    this function assumes that the SID that comes in is a user SID. If
1713    you pass in another type of SID then you may get unpredictable
1714    results.
1715 */
1716
1717 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1718                              size_t num_sids);
1719
1720 void winbindd_getusersids(struct winbindd_cli_state *state)
1721 {
1722         DOM_SID *user_sid;
1723
1724         /* Ensure null termination */
1725         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1726
1727         user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1728         if (user_sid == NULL) {
1729                 DEBUG(1, ("talloc failed\n"));
1730                 request_error(state);
1731                 return;
1732         }
1733
1734         if (!string_to_sid(user_sid, state->request.data.sid)) {
1735                 DEBUG(1, ("Could not get convert sid %s from string\n",
1736                           state->request.data.sid));
1737                 request_error(state);
1738                 return;
1739         }
1740
1741         winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1742                                 state);
1743 }
1744
1745 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1746                              size_t num_sids)
1747 {
1748         struct winbindd_cli_state *state =
1749                 (struct winbindd_cli_state *)private_data;
1750         char *ret = NULL;
1751         unsigned ofs, ret_size = 0;
1752         size_t i;
1753
1754         if (!success) {
1755                 request_error(state);
1756                 return;
1757         }
1758
1759         /* work out the response size */
1760         for (i = 0; i < num_sids; i++) {
1761                 fstring s;
1762                 sid_to_fstring(s, &sids[i]);
1763                 ret_size += strlen(s) + 1;
1764         }
1765
1766         /* build the reply */
1767         ret = (char *)SMB_MALLOC(ret_size);
1768         if (!ret) {
1769                 DEBUG(0, ("malloc failed\n"));
1770                 request_error(state);
1771                 return;
1772         }
1773         ofs = 0;
1774         for (i = 0; i < num_sids; i++) {
1775                 fstring s;
1776                 sid_to_fstring(s, &sids[i]);
1777                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1778                 ofs += strlen(ret+ofs) + 1;
1779         }
1780
1781         /* Send data back to client */
1782         state->response.data.num_entries = num_sids;
1783         state->response.extra_data.data = ret;
1784         state->response.length += ret_size;
1785         request_ok(state);
1786 }
1787
1788 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1789 {
1790         DOM_SID user_sid;
1791         struct winbindd_domain *domain;
1792
1793         /* Ensure null termination */
1794         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1795
1796         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1797                 DEBUG(1, ("Could not get convert sid %s from string\n",
1798                           state->request.data.sid));
1799                 request_error(state);
1800                 return;
1801         }
1802
1803         /* Get info for the domain */
1804         if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1805                 DEBUG(0,("could not find domain entry for sid %s\n",
1806                          sid_string_dbg(&user_sid)));
1807                 request_error(state);
1808                 return;
1809         }
1810
1811         sendto_domain(state, domain);
1812 }
1813
1814 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1815                                                     struct winbindd_cli_state *state)
1816 {
1817         DOM_SID user_sid;
1818         NTSTATUS status;
1819
1820         char *sidstring;
1821         ssize_t len;
1822         DOM_SID *groups;
1823         uint32 num_groups;
1824
1825         /* Ensure null termination */
1826         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1827
1828         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1829                 DEBUG(1, ("Could not get convert sid %s from string\n",
1830                           state->request.data.sid));
1831                 return WINBINDD_ERROR;
1832         }
1833
1834         status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1835                                                     &user_sid, &num_groups,
1836                                                     &groups);
1837         if (!NT_STATUS_IS_OK(status))
1838                 return WINBINDD_ERROR;
1839
1840         if (num_groups == 0) {
1841                 state->response.data.num_entries = 0;
1842                 state->response.extra_data.data = NULL;
1843                 return WINBINDD_OK;
1844         }
1845
1846         if (!print_sidlist(state->mem_ctx,
1847                            groups, num_groups,
1848                            &sidstring, &len)) {
1849                 DEBUG(0, ("talloc failed\n"));
1850                 return WINBINDD_ERROR;
1851         }
1852
1853         state->response.extra_data.data = SMB_STRDUP(sidstring);
1854         if (!state->response.extra_data.data) {
1855                 return WINBINDD_ERROR;
1856         }
1857         state->response.length += len+1;
1858         state->response.data.num_entries = num_groups;
1859
1860         return WINBINDD_OK;
1861 }