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