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