BUG 1182: patch from john.klinger@lmco.com (John Klinger) to reanable the -n 'no...
[tprouty/samba.git] / source / nsswitch / 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    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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 /*********************************************************************
34 *********************************************************************/
35
36 static int gr_mem_buffer( char **buffer, char **members, int num_members )
37 {
38         int i;
39         int len = 0;
40         int idx = 0;
41
42         if ( num_members == 0 ) {
43                 *buffer = NULL;
44                 return 0;
45         }
46         
47         for ( i=0; i<num_members; i++ )
48                 len += strlen(members[i])+1;
49
50         *buffer = (char*)smb_xmalloc(len);
51         for ( i=0; i<num_members; i++ ) {
52                 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
53                 idx += strlen(members[i])+1;
54         }
55         /* terminate with NULL */
56         (*buffer)[len-1] = '\0';
57         
58         return len;     
59 }
60
61 /***************************************************************
62  Empty static struct for negative caching.
63 ****************************************************************/
64
65 /* Fill a grent structure from various other information */
66
67 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name, 
68                        const char *gr_name, gid_t unix_gid)
69 {
70         fstring full_group_name;
71         /* Fill in uid/gid */
72         fill_domain_username(full_group_name, dom_name, gr_name);
73
74         gr->gr_gid = unix_gid;
75     
76         /* Group name and password */
77     
78         safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
79         safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
80
81         return True;
82 }
83
84 /* Fill in the group membership field of a NT group given by group_sid */
85
86 static BOOL fill_grent_mem(struct winbindd_domain *domain,
87                            DOM_SID *group_sid, 
88                            enum SID_NAME_USE group_name_type, 
89                            int *num_gr_mem, char **gr_mem, int *gr_mem_len)
90 {
91         DOM_SID **sid_mem = NULL;
92         uint32 num_names = 0;
93         uint32 *name_types = NULL;
94         unsigned int buf_len, buf_ndx, i;
95         char **names = NULL, *buf;
96         BOOL result = False;
97         TALLOC_CTX *mem_ctx;
98         NTSTATUS status;
99         fstring sid_string;
100
101         if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
102                 return False;
103
104         /* Initialise group membership information */
105         
106         DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
107
108         *num_gr_mem = 0;
109         
110         if ( !((group_name_type==SID_NAME_DOM_GRP) ||
111                 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
112         {
113                 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", 
114                           sid_to_string(sid_string, group_sid), domain->name, 
115                           group_name_type));
116                 goto done;
117         }
118
119         /* Lookup group members */
120         status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names, 
121                                                   &sid_mem, &names, &name_types);
122         if (!NT_STATUS_IS_OK(status)) {
123                 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n", 
124                           sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
125
126                 goto done;
127         }
128
129         DEBUG(10, ("looked up %d names\n", num_names));
130
131         if (DEBUGLEVEL >= 10) {
132                 for (i = 0; i < num_names; i++)
133                         DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
134                                    name_types[i]));
135         }
136
137         /* Add members to list */
138
139         buf = NULL;
140         buf_len = buf_ndx = 0;
141
142  again:
143
144         for (i = 0; i < num_names; i++) {
145                 char *the_name;
146                 fstring name;
147                 int len;
148                         
149                 the_name = names[i];
150
151                 DEBUG(10, ("processing name %s\n", the_name));
152
153                 /* FIXME: need to cope with groups within groups.  These
154                    occur in Universal groups on a Windows 2000 native mode
155                    server. */
156
157                 /* make sure to allow machine accounts */
158
159                 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
160                         DEBUG(3, ("name %s isn't a domain user\n", the_name));
161                         continue;
162                 }
163
164                 /* Append domain name */
165
166                 fill_domain_username(name, domain->name, the_name);
167
168                 len = strlen(name);
169                 
170                 /* Add to list or calculate buffer length */
171
172                 if (!buf) {
173                         buf_len += len + 1; /* List is comma separated */
174                         (*num_gr_mem)++;
175                         DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
176                 } else {
177                         DEBUG(10, ("appending %s at ndx %d\n", name, len));
178                         safe_strcpy(&buf[buf_ndx], name, len);
179                         buf_ndx += len;
180                         buf[buf_ndx] = ',';
181                         buf_ndx++;
182                 }
183         }
184
185         /* Allocate buffer */
186
187         if (!buf && buf_len != 0) {
188                 if (!(buf = malloc(buf_len))) {
189                         DEBUG(1, ("out of memory\n"));
190                         result = False;
191                         goto done;
192                 }
193                 memset(buf, 0, buf_len);
194                 goto again;
195         }
196
197         if (buf && buf_ndx > 0) {
198                 buf[buf_ndx - 1] = '\0';
199         }
200
201         *gr_mem = buf;
202         *gr_mem_len = buf_len;
203
204         DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem, 
205                    buf_len, *num_gr_mem ? buf : "NULL")); 
206         result = True;
207
208 done:
209
210         talloc_destroy(mem_ctx);
211         
212         DEBUG(10, ("fill_grent_mem returning %d\n", result));
213
214         return result;
215 }
216
217 /* Return a group structure from a group name */
218
219 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
220 {
221         DOM_SID group_sid;
222         WINBINDD_GR *grp;
223         struct winbindd_domain *domain;
224         enum SID_NAME_USE name_type;
225         fstring name_domain, name_group;
226         char *tmp, *gr_mem;
227         int gr_mem_len;
228         gid_t gid;
229         
230         /* Ensure null termination */
231         state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
232
233         DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
234                   state->request.data.groupname));
235
236         /* Parse domain and groupname */
237         
238         memset(name_group, 0, sizeof(fstring));
239
240         tmp = state->request.data.groupname;
241         
242         parse_domain_user(tmp, name_domain, name_group);
243
244         /* if no domain or our local domain, then do a local tdb search */
245         
246         if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
247                 char *buffer = NULL;
248                 
249                 if ( !(grp=wb_getgrnam(name_group)) ) {
250                         DEBUG(5,("winbindd_getgrnam: lookup for %s\\%s failed\n",
251                                 name_domain, name_group));
252                         return WINBINDD_ERROR;
253                 }
254                 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
255
256                 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
257                 
258                 state->response.data.gr.gr_mem_ofs = 0;
259                 state->response.length += gr_mem_len;
260                 state->response.extra_data = buffer;    /* give the memory away */
261                 
262                 return WINBINDD_OK;
263         }
264
265         /* Get info for the domain */
266
267         if ((domain = find_domain_from_name(name_domain)) == NULL) {
268                 DEBUG(3, ("could not get domain sid for domain %s\n",
269                           name_domain));
270                 return WINBINDD_ERROR;
271         }
272         /* should we deal with users for our domain? */
273         
274         if ( lp_winbind_trusted_domains_only() && domain->primary) {
275                 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n", 
276                         name_domain, name_group));
277                 return WINBINDD_ERROR;
278         }
279
280         /* Get rid and name type from name */
281         
282         if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid, 
283                                          &name_type)) {
284                 DEBUG(1, ("group %s in domain %s does not exist\n", 
285                           name_group, name_domain));
286                 return WINBINDD_ERROR;
287         }
288
289         if ( !((name_type==SID_NAME_DOM_GRP) ||
290                 ((name_type==SID_NAME_ALIAS) && domain->primary)) )
291         {
292                 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
293                           name_group, name_type));
294                 return WINBINDD_ERROR;
295         }
296
297         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
298                 DEBUG(1, ("error converting unix gid to sid\n"));
299                 return WINBINDD_ERROR;
300         }
301
302         if (!fill_grent(&state->response.data.gr, name_domain,
303                         name_group, gid) ||
304             !fill_grent_mem(domain, &group_sid, name_type,
305                             &state->response.data.gr.num_gr_mem,
306                             &gr_mem, &gr_mem_len)) {
307                 return WINBINDD_ERROR;
308         }
309
310         /* Group membership lives at start of extra data */
311
312         state->response.data.gr.gr_mem_ofs = 0;
313
314         state->response.length += gr_mem_len;
315         state->response.extra_data = gr_mem;
316
317         return WINBINDD_OK;
318 }
319
320 /* Return a group structure from a gid number */
321
322 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
323 {
324         struct winbindd_domain *domain;
325         WINBINDD_GR *grp;
326         DOM_SID group_sid;
327         enum SID_NAME_USE name_type;
328         fstring dom_name;
329         fstring group_name;
330         int gr_mem_len;
331         char *gr_mem;
332
333         DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, 
334                   (unsigned long)state->request.data.gid));
335
336         /* Bug out if the gid isn't in the winbind range */
337
338         if ((state->request.data.gid < server_state.gid_low) ||
339             (state->request.data.gid > server_state.gid_high))
340                 return WINBINDD_ERROR;
341
342         /* alway try local tdb lookup first */
343         if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
344                 char *buffer = NULL;
345                 
346                 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
347                 
348                 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
349                 
350                 state->response.data.gr.gr_mem_ofs = 0;
351                 state->response.length += gr_mem_len;
352                 state->response.extra_data = buffer;    /* give away the memory */
353                 
354                 return WINBINDD_OK;
355         }
356
357         /* Get rid from gid */
358         if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
359                 DEBUG(1, ("could not convert gid %lu to rid\n", 
360                           (unsigned long)state->request.data.gid));
361                 return WINBINDD_ERROR;
362         }
363
364         /* Get name from sid */
365
366         if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
367                 DEBUG(1, ("could not lookup sid\n"));
368                 return WINBINDD_ERROR;
369         }
370
371         /* Fill in group structure */
372
373         domain = find_domain_from_sid(&group_sid);
374
375         if (!domain) {
376                 DEBUG(1,("Can't find domain from sid\n"));
377                 return WINBINDD_ERROR;
378         }
379
380         if ( !((name_type==SID_NAME_DOM_GRP) ||
381                ((name_type==SID_NAME_ALIAS) && domain->primary) ))
382         {
383                 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
384                           group_name, name_type));
385                 return WINBINDD_ERROR;
386         }
387
388         if (!fill_grent(&state->response.data.gr, dom_name, group_name, 
389                         state->request.data.gid) ||
390             !fill_grent_mem(domain, &group_sid, name_type,
391                             &state->response.data.gr.num_gr_mem,
392                             &gr_mem, &gr_mem_len))
393                 return WINBINDD_ERROR;
394
395         /* Group membership lives at start of extra data */
396
397         state->response.data.gr.gr_mem_ofs = 0;
398
399         state->response.length += gr_mem_len;
400         state->response.extra_data = gr_mem;
401
402         return WINBINDD_OK;
403 }
404
405 /*
406  * set/get/endgrent functions
407  */
408
409 /* "Rewind" file pointer for group database enumeration */
410
411 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
412 {
413         struct winbindd_domain *domain;
414
415         DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
416
417         /* Check user has enabled this */
418
419         if (!lp_winbind_enum_groups())
420                 return WINBINDD_ERROR;
421
422         /* Free old static data if it exists */
423         
424         if (state->getgrent_state != NULL) {
425                 free_getent_state(state->getgrent_state);
426                 state->getgrent_state = NULL;
427         }
428         
429         /* Create sam pipes for each domain we know about */
430         
431         for (domain = domain_list(); domain != NULL; domain = domain->next) {
432                 struct getent_state *domain_state;
433                 
434                 
435                 /* don't add our domaina if we are a PDC or if we 
436                    are a member of a Samba domain */
437                 
438                 if ( (IS_DC || lp_winbind_trusted_domains_only())
439                         && domain->primary )
440                 {
441                         continue;
442                 }
443                                                 
444                 /* Create a state record for this domain */
445                 
446                 if ((domain_state = (struct getent_state *)
447                      malloc(sizeof(struct getent_state))) == NULL) {
448                         DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
449                         return WINBINDD_ERROR;
450                 }
451                 
452                 ZERO_STRUCTP(domain_state);
453                 
454                 fstrcpy(domain_state->domain_name, domain->name);
455
456                 /* Add to list of open domains */
457                 
458                 DLIST_ADD(state->getgrent_state, domain_state);
459         }
460         
461         return WINBINDD_OK;
462 }
463
464 /* Close file pointer to ntdom group database */
465
466 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
467 {
468         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
469
470         free_getent_state(state->getgrent_state);
471         state->getgrent_state = NULL;
472         
473         return WINBINDD_OK;
474 }
475
476 /* Get the list of domain groups and domain aliases for a domain.  We fill in
477    the sam_entries and num_sam_entries fields with domain group information.  
478    The dispinfo_ndx field is incremented to the index of the next group to 
479    fetch. Return True if some groups were returned, False otherwise. */
480
481 #define MAX_FETCH_SAM_ENTRIES 100
482
483 static BOOL get_sam_group_entries(struct getent_state *ent)
484 {
485         NTSTATUS status;
486         uint32 num_entries;
487         struct acct_info *name_list = NULL, *tmp_name_list = NULL;
488         TALLOC_CTX *mem_ctx;
489         BOOL result = False;
490         struct acct_info *sam_grp_entries = NULL;
491         struct winbindd_domain *domain;
492         
493         if (ent->got_sam_entries)
494                 return False;
495
496         if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
497                                           ent->domain_name))) {
498                 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); 
499                 return False;
500         }
501                 
502         /* Free any existing group info */
503
504         SAFE_FREE(ent->sam_entries);
505         ent->num_sam_entries = 0;
506         ent->got_sam_entries = True;
507
508         /* Enumerate domain groups */
509
510         num_entries = 0;
511
512         if (!(domain = find_domain_from_name(ent->domain_name))) {
513                 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
514                 goto done;
515         }
516
517         /* always get the domain global groups */
518
519         status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
520         
521         if (!NT_STATUS_IS_OK(status)) {
522                 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
523                 result = False;
524                 goto done;
525         }
526
527         /* Copy entries into return buffer */
528
529         if (num_entries) {
530                 if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
531                         DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", 
532                                 num_entries));
533                         result = False;
534                         goto done;
535                 }
536                 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
537         }
538         
539         ent->num_sam_entries = num_entries;
540         
541         /* get the domain local groups if we are a member of a native win2k domain
542            and are not using LDAP to get the groups */
543            
544         if ( lp_security() != SEC_ADS && domain->native_mode 
545                 && domain->primary )
546         {
547                 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
548                 
549                 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
550                 
551                 if ( !NT_STATUS_IS_OK(status) ) { 
552                         DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
553                         num_entries = 0;
554                 }
555                 else
556                         DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
557                 
558                 /* Copy entries into return buffer */
559
560                 if ( num_entries ) {
561                         if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
562                         {
563                                 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", 
564                                         num_entries));
565                                 result = False;
566                                 SAFE_FREE( name_list );
567                                 goto done;
568                         }
569                         
570                         name_list = tmp_name_list;
571                                 
572                         memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, 
573                                 num_entries * sizeof(struct acct_info) );
574                 }
575         
576                 ent->num_sam_entries += num_entries;
577         }
578         
579                 
580         /* Fill in remaining fields */
581
582         ent->sam_entries = name_list;
583         ent->sam_entry_index = 0;
584
585         result = (ent->num_sam_entries > 0);
586
587  done:
588         talloc_destroy(mem_ctx);
589
590         return result;
591 }
592
593 /* Fetch next group entry from ntdom database */
594
595 #define MAX_GETGRENT_GROUPS 500
596
597 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
598 {
599         struct getent_state *ent;
600         struct winbindd_gr *group_list = NULL;
601         int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
602         char *new_extra_data, *gr_mem_list = NULL;
603
604         DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
605
606         /* Check user has enabled this */
607
608         if (!lp_winbind_enum_groups())
609                 return WINBINDD_ERROR;
610
611         num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
612
613         if ((state->response.extra_data = 
614              malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
615                 return WINBINDD_ERROR;
616
617         state->response.data.num_entries = 0;
618
619         group_list = (struct winbindd_gr *)state->response.extra_data;
620
621         if (!(ent = state->getgrent_state))
622                 return WINBINDD_ERROR;
623
624         /* Start sending back groups */
625
626         for (i = 0; i < num_groups; i++) {
627                 struct acct_info *name_list = NULL;
628                 fstring domain_group_name;
629                 uint32 result;
630                 gid_t group_gid;
631                 int gr_mem_len;
632                 char *gr_mem, *new_gr_mem_list;
633                 DOM_SID group_sid;
634                 struct winbindd_domain *domain;
635                                 
636                 /* Do we need to fetch another chunk of groups? */
637
638         tryagain:
639
640                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
641                            ent->sam_entry_index, ent->num_sam_entries));
642
643                 if (ent->num_sam_entries == ent->sam_entry_index) {
644
645                         while(ent && !get_sam_group_entries(ent)) {
646                                 struct getent_state *next_ent;
647
648                                 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 
649
650                                 /* Free state information for this domain */
651
652                                 SAFE_FREE(ent->sam_entries);
653
654                                 next_ent = ent->next;
655                                 DLIST_REMOVE(state->getgrent_state, ent);
656                                 
657                                 SAFE_FREE(ent);
658                                 ent = next_ent;
659                         }
660
661                         /* No more domains */
662
663                         if (!ent) 
664                                 break;
665                 }
666                 
667                 name_list = ent->sam_entries;
668                 
669                 if (!(domain = 
670                       find_domain_from_name(ent->domain_name))) {
671                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
672                         result = False;
673                         goto done;
674                 }
675
676                 /* Lookup group info */
677                 
678                 sid_copy(&group_sid, &domain->sid);
679                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
680
681                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
682                         
683                         DEBUG(1, ("could not look up gid for group %s\n", 
684                                   name_list[ent->sam_entry_index].acct_name));
685                         
686                         ent->sam_entry_index++;
687                         goto tryagain;
688                 }
689
690                 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
691                            (unsigned long)name_list[ent->sam_entry_index].rid));
692                 
693                 /* Fill in group entry */
694
695                 fill_domain_username(domain_group_name, ent->domain_name, 
696                          name_list[ent->sam_entry_index].acct_name);
697
698                 result = fill_grent(&group_list[group_list_ndx], 
699                                     ent->domain_name,
700                                     name_list[ent->sam_entry_index].acct_name,
701                                     group_gid);
702
703                 /* Fill in group membership entry */
704
705                 if (result) {
706                         DOM_SID member_sid;
707                         group_list[group_list_ndx].num_gr_mem = 0;
708                         gr_mem = NULL;
709                         gr_mem_len = 0;
710                         
711                         /* Get group membership */                      
712                         if (state->request.cmd == WINBINDD_GETGRLST) {
713                                 result = True;
714                         } else {
715                                 sid_copy(&member_sid, &domain->sid);
716                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
717                                 result = fill_grent_mem(
718                                         domain,
719                                         &member_sid,
720                                         SID_NAME_DOM_GRP,
721                                         &group_list[group_list_ndx].num_gr_mem, 
722                                         &gr_mem, &gr_mem_len);
723                         }
724                 }
725
726                 if (result) {
727                         /* Append to group membership list */
728                         new_gr_mem_list = Realloc(
729                                 gr_mem_list,
730                                 gr_mem_list_len + gr_mem_len);
731
732                         if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
733                                 DEBUG(0, ("out of memory\n"));
734                                 SAFE_FREE(gr_mem_list);
735                                 gr_mem_list_len = 0;
736                                 break;
737                         }
738
739                         DEBUG(10, ("list_len = %d, mem_len = %d\n",
740                                    gr_mem_list_len, gr_mem_len));
741
742                         gr_mem_list = new_gr_mem_list;
743
744                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
745                                gr_mem_len);
746
747                         SAFE_FREE(gr_mem);
748
749                         group_list[group_list_ndx].gr_mem_ofs = 
750                                 gr_mem_list_len;
751
752                         gr_mem_list_len += gr_mem_len;
753                 }
754
755                 ent->sam_entry_index++;
756                 
757                 /* Add group to return list */
758                 
759                 if (result) {
760
761                         DEBUG(10, ("adding group num_entries = %d\n",
762                                    state->response.data.num_entries));
763
764                         group_list_ndx++;
765                         state->response.data.num_entries++;
766                         
767                         state->response.length +=
768                                 sizeof(struct winbindd_gr);
769                         
770                 } else {
771                         DEBUG(0, ("could not lookup domain group %s\n", 
772                                   domain_group_name));
773                 }
774         }
775
776         /* Copy the list of group memberships to the end of the extra data */
777
778         if (group_list_ndx == 0)
779                 goto done;
780
781         new_extra_data = Realloc(
782                 state->response.extra_data,
783                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
784
785         if (!new_extra_data) {
786                 DEBUG(0, ("out of memory\n"));
787                 group_list_ndx = 0;
788                 SAFE_FREE(state->response.extra_data);
789                 SAFE_FREE(gr_mem_list);
790
791                 return WINBINDD_ERROR;
792         }
793
794         state->response.extra_data = new_extra_data;
795
796         memcpy(&((char *)state->response.extra_data)
797                [group_list_ndx * sizeof(struct winbindd_gr)], 
798                gr_mem_list, gr_mem_list_len);
799
800         SAFE_FREE(gr_mem_list);
801
802         state->response.length += gr_mem_list_len;
803
804         DEBUG(10, ("returning %d groups, length = %d\n",
805                    group_list_ndx, gr_mem_list_len));
806
807         /* Out of domains */
808
809  done:
810
811         return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
812 }
813
814 /* List domain groups without mapping to unix ids */
815
816 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
817 {
818         uint32 total_entries = 0;
819         struct winbindd_domain *domain;
820         const char *which_domain;
821         char *extra_data = NULL;
822         char *ted = NULL;
823         unsigned int extra_data_len = 0, i;
824
825         DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
826
827         /* Ensure null termination */
828         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
829         which_domain = state->request.domain_name;
830         
831         /* Enumerate over trusted domains */
832
833         for (domain = domain_list(); domain; domain = domain->next) {
834                 struct getent_state groups;
835
836                 /* if we have a domain name restricting the request and this
837                    one in the list doesn't match, then just bypass the remainder
838                    of the loop */
839                    
840                 if ( *which_domain && !strequal(which_domain, domain->name) )
841                         continue;
842                         
843                 ZERO_STRUCT(groups);
844
845                 /* Get list of sam groups */
846                 
847                 fstrcpy(groups.domain_name, domain->name);
848
849                 get_sam_group_entries(&groups);
850                         
851                 if (groups.num_sam_entries == 0) {
852                         /* this domain is empty or in an error state */
853                         continue;
854                 }
855
856                 /* keep track the of the total number of groups seen so 
857                    far over all domains */
858                 total_entries += groups.num_sam_entries;
859                 
860                 /* Allocate some memory for extra data.  Note that we limit
861                    account names to sizeof(fstring) = 128 characters.  */               
862                 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
863  
864                 if (!ted) {
865                         DEBUG(0,("failed to enlarge buffer!\n"));
866                         SAFE_FREE(extra_data);
867                         return WINBINDD_ERROR;
868                 } else
869                         extra_data = ted;
870
871                 /* Pack group list into extra data fields */
872                 for (i = 0; i < groups.num_sam_entries; i++) {
873                         char *group_name = ((struct acct_info *)
874                                             groups.sam_entries)[i].acct_name; 
875                         fstring name;
876
877                         fill_domain_username(name, domain->name, group_name);
878                         /* Append to extra data */                      
879                         memcpy(&extra_data[extra_data_len], name, 
880                                strlen(name));
881                         extra_data_len += strlen(name);
882                         extra_data[extra_data_len++] = ',';
883                 }
884
885                 SAFE_FREE(groups.sam_entries);
886         }
887
888         /* Assign extra_data fields in response structure */
889         if (extra_data) {
890                 extra_data[extra_data_len - 1] = '\0';
891                 state->response.extra_data = extra_data;
892                 state->response.length += extra_data_len;
893         }
894
895         /* No domains may have responded but that's still OK so don't
896            return an error. */
897
898         return WINBINDD_OK;
899 }
900
901 /* Get user supplementary groups.  This is much quicker than trying to
902    invert the groups database.  We merge the groups from the gids and
903    other_sids info3 fields as trusted domain, universal group
904    memberships, and nested groups (win2k native mode only) are not
905    returned by the getgroups RPC call but are present in the info3. */
906
907 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
908 {
909         fstring name_domain, name_user;
910         DOM_SID user_sid, group_sid;
911         enum SID_NAME_USE name_type;
912         uint32 num_groups = 0;
913         uint32 num_gids = 0;
914         NTSTATUS status;
915         DOM_SID **user_grpsids;
916         struct winbindd_domain *domain;
917         enum winbindd_result result = WINBINDD_ERROR;
918         gid_t *gid_list;
919         unsigned int i;
920         TALLOC_CTX *mem_ctx;
921         NET_USER_INFO_3 *info3 = NULL;
922         
923         /* Ensure null termination */
924         state->request.data.username[sizeof(state->request.data.username)-1]='\0';
925
926         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
927                   state->request.data.username));
928
929         if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
930                                           state->request.data.username)))
931                 return WINBINDD_ERROR;
932
933         /* Parse domain and username */
934
935         parse_domain_user(state->request.data.username, 
936                           name_domain, name_user);
937         
938         /* Get info for the domain */
939         
940         if ((domain = find_domain_from_name(name_domain)) == NULL) {
941                 DEBUG(7, ("could not find domain entry for domain %s\n", 
942                           name_domain));
943                 goto done;
944         }
945
946         if ( domain->primary && lp_winbind_trusted_domains_only()) {
947                 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n", 
948                         name_domain, name_user));
949                 return WINBINDD_ERROR;
950         }       
951         
952         /* Get rid and name type from name.  The following costs 1 packet */
953
954         if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid, 
955                                          &name_type)) {
956                 DEBUG(1, ("user '%s' does not exist\n", name_user));
957                 goto done;
958         }
959
960         if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
961                 DEBUG(1, ("name '%s' is not a user name: %d\n", 
962                           name_user, name_type));
963                 goto done;
964         }
965
966         /* Treat the info3 cache as authoritative as the
967            lookup_usergroups() function may return cached data. */
968
969         if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
970
971                 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
972                            info3->num_groups2, info3->num_other_sids));
973
974                 num_groups = info3->num_other_sids + info3->num_groups2;
975                 gid_list = calloc(sizeof(gid_t), num_groups);
976
977                 /* Go through each other sid and convert it to a gid */
978
979                 for (i = 0; i < info3->num_other_sids; i++) {
980                         fstring name;
981                         fstring dom_name;
982                         enum SID_NAME_USE sid_type;
983
984                         /* Is this sid known to us?  It can either be
985                            a trusted domain sid or a foreign sid. */
986
987                         if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid, 
988                                 dom_name, name, &sid_type))
989                         {
990                                 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n", 
991                                            sid_string_static(&info3->other_sids[i].sid)));
992                                 continue;
993                         }
994
995                         /* Check it is a domain group or an alias (domain local group) 
996                            in a win2k native mode domain. */
997                         
998                         if ( !((sid_type==SID_NAME_DOM_GRP) ||
999                                 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1000                         {
1001                                 DEBUG(10, ("winbindd_getgroups: sid type %d "
1002                                            "for %s is not a domain group\n",
1003                                            sid_type,
1004                                            sid_string_static(
1005                                                    &info3->other_sids[i].sid)));
1006                                 continue;
1007                         }
1008
1009                         /* Map to a gid */
1010
1011                         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&info3->other_sids[i].sid, &gid_list[num_gids], 0)) )
1012                         {
1013                                 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1014                                            sid_string_static(&info3->other_sids[i].sid)));
1015                                 continue;
1016                         }
1017
1018                         /* We've jumped through a lot of hoops to get here */
1019
1020                         DEBUG(10, ("winbindd_getgroups: mapped other sid %s to "
1021                                    "gid %lu\n", sid_string_static(
1022                                            &info3->other_sids[i].sid),
1023                                    (unsigned long)gid_list[num_gids]));
1024
1025                         num_gids++;
1026                 }
1027
1028                 for (i = 0; i < info3->num_groups2; i++) {
1029                 
1030                         /* create the group SID */
1031                         
1032                         sid_copy( &group_sid, &domain->sid );
1033                         sid_append_rid( &group_sid, info3->gids[i].g_rid );
1034
1035                         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid_list[num_gids], 0)) ) {
1036                                 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1037                                            sid_string_static(&group_sid)));
1038                         }
1039
1040                         num_gids++;
1041                 }
1042
1043                 SAFE_FREE(info3);
1044
1045         } else {
1046                 status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1047                                                     &user_sid, &num_groups, 
1048                                                     &user_grpsids);
1049                 if (!NT_STATUS_IS_OK(status)) 
1050                         goto done;
1051
1052                 gid_list = malloc(sizeof(gid_t) * num_groups);
1053
1054                 if (state->response.extra_data)
1055                         goto done;
1056
1057                 for (i = 0; i < num_groups; i++) {
1058                         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_grpsids[i], &gid_list[num_gids], 0))) {
1059                                 DEBUG(1, ("unable to convert group sid %s to gid\n", 
1060                                           sid_string_static(user_grpsids[i])));
1061                                 continue;
1062                         }
1063                         num_gids++;
1064                 }
1065         }
1066
1067         /* Send data back to client */
1068
1069         state->response.data.num_entries = num_gids;
1070         state->response.extra_data = gid_list;
1071         state->response.length += num_gids * sizeof(gid_t);
1072
1073         result = WINBINDD_OK;
1074
1075  done:
1076
1077         talloc_destroy(mem_ctx);
1078
1079         return result;
1080 }
1081
1082
1083 /* Get user supplementary sids. This is equivalent to the
1084    winbindd_getgroups() function but it involves a SID->SIDs mapping
1085    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1086    idmap. This call is designed to be used with applications that need
1087    to do ACL evaluation themselves. Note that the cached info3 data is
1088    not used 
1089
1090    this function assumes that the SID that comes in is a user SID. If
1091    you pass in another type of SID then you may get unpredictable
1092    results.
1093 */
1094 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1095 {
1096         DOM_SID user_sid;
1097         NTSTATUS status;
1098         DOM_SID **user_grpsids;
1099         struct winbindd_domain *domain;
1100         enum winbindd_result result = WINBINDD_ERROR;
1101         unsigned int i;
1102         TALLOC_CTX *mem_ctx;
1103         char *ret = NULL;
1104         uint32 num_groups;
1105         unsigned ofs, ret_size = 0;
1106
1107         /* Ensure null termination */
1108         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1109
1110         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1111                 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1112                 return WINBINDD_ERROR;
1113         }
1114
1115         if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1116                                     state->request.data.username))) {
1117                 return WINBINDD_ERROR;
1118         }
1119
1120         /* Get info for the domain */   
1121         if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1122                 DEBUG(0,("could not find domain entry for sid %s\n", 
1123                           sid_string_static(&user_sid)));
1124                 goto done;
1125         }
1126         
1127         status = domain->methods->lookup_usergroups(domain, mem_ctx, 
1128                                                     &user_sid, &num_groups, 
1129                                                     &user_grpsids);
1130         if (!NT_STATUS_IS_OK(status)) 
1131                 goto done;
1132
1133         if (num_groups == 0) {
1134                 goto no_groups;
1135         }
1136
1137         /* work out the response size */
1138         for (i = 0; i < num_groups; i++) {
1139                 const char *s = sid_string_static(user_grpsids[i]);
1140                 ret_size += strlen(s) + 1;
1141         }
1142
1143         /* build the reply */
1144         ret = malloc(ret_size);
1145         if (!ret) goto done;
1146         ofs = 0;
1147         for (i = 0; i < num_groups; i++) {
1148                 const char *s = sid_string_static(user_grpsids[i]);
1149                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1150                 ofs += strlen(ret+ofs) + 1;
1151         }
1152
1153 no_groups:
1154         /* Send data back to client */
1155         state->response.data.num_entries = num_groups;
1156         state->response.extra_data = ret;
1157         state->response.length += ret_size;
1158         result = WINBINDD_OK;
1159
1160  done:
1161         talloc_destroy(mem_ctx);
1162
1163         return result;
1164 }
1165