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