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