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