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