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