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