79d8e2970ed002b083fa0d108944cf1eefbb18ea
[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    Copyright (C) Gerald (Jerry) Carter 2003.
9    Copyright (C) Volker Lendecke 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
29 extern BOOL opt_nocache;
30
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
33
34 /***************************************************************
35  Empty static struct for negative caching.
36 ****************************************************************/
37
38 /* Fill a grent structure from various other information */
39
40 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name, 
41                        const char *gr_name, gid_t unix_gid)
42 {
43         fstring full_group_name;
44
45         fill_domain_username( full_group_name, dom_name, gr_name, True );
46
47         gr->gr_gid = unix_gid;
48     
49         /* Group name and password */
50     
51         safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
52         safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
53
54         return True;
55 }
56
57 /* Fill in the group membership field of a NT group given by group_sid */
58
59 static BOOL fill_grent_mem(struct winbindd_domain *domain,
60                            struct winbindd_cli_state *state,
61                            DOM_SID *group_sid, 
62                            enum lsa_SidType group_name_type, 
63                            size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
64 {
65         DOM_SID *sid_mem = NULL;
66         uint32 num_names = 0;
67         uint32 *name_types = NULL;
68         unsigned int buf_len = 0, buf_ndx = 0, i;
69         char **names = NULL, *buf = NULL;
70         BOOL result = False;
71         TALLOC_CTX *mem_ctx;
72         NTSTATUS status;
73         uint32 group_rid;
74         fstring sid_string;
75
76         if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
77                 return False;
78
79         /* Initialise group membership information */
80         
81         DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
82
83         *num_gr_mem = 0;
84
85         /* HACK ALERT!! This whole routine does not cope with group members
86          * from more than one domain, ie aliases. Thus we have to work it out
87          * ourselves in a special routine. */
88
89         if (domain->internal)
90                 return fill_passdb_alias_grmem(domain, group_sid,
91                                                num_gr_mem,
92                                                gr_mem, gr_mem_len);
93         
94         if ( !((group_name_type==SID_NAME_DOM_GRP) ||
95                 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
96         {
97                 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", 
98                           sid_to_string(sid_string, group_sid), domain->name, 
99                           group_name_type));
100                 goto done;
101         }
102
103         /* OPTIMIZATION / HACK. */
104         /* If "enum users" is set to false, and the group being looked
105            up is the Domain Users SID: S-1-5-domain-513, then for the
106            list of members check if the querying user is in that group,
107            and if so only return that user as the gr_mem array.
108            We can change this to a different parameter than "enum users"
109            if neccessaey, or parameterize the group list we do this for. */
110
111         sid_peek_rid( group_sid, &group_rid );
112         if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
113                 DOM_SID querying_user_sid;
114                 DOM_SID *pquerying_user_sid = NULL;
115                 uint32 num_groups = 0;
116                 DOM_SID *user_sids = NULL;
117                 BOOL u_in_group = False;
118
119                 DEBUG(10,("fill_grent_mem: optimized lookup for sid %s domain %s\n",
120                         sid_to_string(sid_string, group_sid), domain->name ));
121
122                 if (state) {
123                         uid_t ret_uid = (uid_t)-1;
124                         if (sys_getpeereid(state->sock, &ret_uid)==0) {
125                                 /* We know who's asking - look up their SID if
126                                    it's one we've mapped before. */
127                                 status = idmap_uid_to_sid(&querying_user_sid,
128                                                         ret_uid,
129                                                         IDMAP_FLAG_QUERY_ONLY|IDMAP_FLAG_CACHE_ONLY);
130                                 if (NT_STATUS_IS_OK(status)) {
131                                         pquerying_user_sid = &querying_user_sid;
132                                         DEBUG(10,("fill_grent_mem: querying uid %u -> %s\n",
133                                                 (unsigned int)ret_uid,
134                                                 sid_to_string(sid_string, pquerying_user_sid) ));
135                                 }
136                         }
137                 }
138
139                 /* Only look up if it was a winbindd user in this domain. */
140                 if (pquerying_user_sid &&
141                                 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
142
143                         DEBUG(10,("fill_grent_mem: querying user = %s\n",
144                                 sid_to_string(sid_string, pquerying_user_sid) ));
145
146                         status = domain->methods->lookup_usergroups(domain,
147                                                         mem_ctx,
148                                                         pquerying_user_sid,
149                                                         &num_groups,
150                                                         &user_sids);
151                         if (!NT_STATUS_IS_OK(status)) {
152                                 DEBUG(1, ("fill_grent_mem: lookup_usergroups failed "
153                                         "for sid %s in domain %s (error: %s)\n", 
154                                         sid_to_string(sid_string, pquerying_user_sid),
155                                         domain->name,
156                                         nt_errstr(status)));
157                                 goto done;
158                         }
159
160                         for (i = 0; i < num_groups; i++) {
161                                 if (sid_equal(group_sid, &user_sids[i])) {
162                                         /* User is in Domain Users, add their name
163                                            as the only group member. */
164                                         u_in_group = True;
165                                         break;
166                                 }
167                         }
168                 }
169
170                 if (u_in_group) {
171                         size_t len = 0;
172                         char *domainname = NULL;
173                         char *username = NULL;
174                         fstring name;
175                         enum lsa_SidType type;
176
177                         DEBUG(10,("fill_grent_mem: sid %s in 'Domain Users' in domain %s\n",
178                                 sid_to_string(sid_string, pquerying_user_sid), domain->name ));
179
180                         status = domain->methods->sid_to_name(domain, mem_ctx,
181                                                                 pquerying_user_sid,
182                                                                 &domainname,
183                                                                 &username,
184                                                                 &type);
185                         if (!NT_STATUS_IS_OK(status)) {
186                                 DEBUG(1, ("could not lookup username for user "
187                                         "sid %s in domain %s (error: %s)\n", 
188                                         sid_to_string(sid_string, pquerying_user_sid),
189                                         domain->name,
190                                         nt_errstr(status)));
191                                 goto done;
192                         }
193                         fill_domain_username(name, domain->name, username, True);
194                         len = strlen(name);
195                         buf_len = len + 1;
196                         if (!(buf = (char *)SMB_MALLOC(buf_len))) {
197                                 DEBUG(1, ("out of memory\n"));
198                                 goto done;
199                         }
200                         memcpy(buf, name, buf_len);
201
202                         DEBUG(10,("fill_grent_mem: user %s in 'Domain Users' in domain %s\n",
203                                 name, domain->name ));
204                 }
205                 
206                 *gr_mem = buf;
207                 *gr_mem_len = buf_len;
208
209                 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem, 
210                    (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); 
211                 result = True;
212                 goto done;
213         }
214
215         /* Lookup group members */
216         status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names, 
217                                                   &sid_mem, &names, &name_types);
218         if (!NT_STATUS_IS_OK(status)) {
219                 DEBUG(1, ("could not lookup membership for group sid %s in domain %s (error: %s)\n", 
220                           sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
221                 goto done;
222         }
223
224         DEBUG(10, ("looked up %d names\n", num_names));
225
226         if (DEBUGLEVEL >= 10) {
227                 for (i = 0; i < num_names; i++)
228                         DEBUG(10, ("\t%20s %s %d\n", names[i],
229                                    sid_string_static(&sid_mem[i]),
230                                    name_types[i]));
231         }
232
233         /* Add members to list */
234
235  again:
236
237         for (i = 0; i < num_names; i++) {
238                 char *the_name;
239                 fstring name;
240                 int len;
241                         
242                 the_name = names[i];
243
244                 DEBUG(10, ("processing name %s\n", the_name));
245
246                 /* FIXME: need to cope with groups within groups.  These
247                    occur in Universal groups on a Windows 2000 native mode
248                    server. */
249
250                 /* make sure to allow machine accounts */
251
252                 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
253                         DEBUG(3, ("name %s isn't a domain user (%s)\n", the_name, sid_type_lookup(name_types[i])));
254                         continue;
255                 }
256
257                 /* Append domain name */
258
259                 fill_domain_username(name, domain->name, the_name, True);
260
261                 len = strlen(name);
262                 
263                 /* Add to list or calculate buffer length */
264
265                 if (!buf) {
266                         buf_len += len + 1; /* List is comma separated */
267                         (*num_gr_mem)++;
268                         DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
269                 } else {
270                         DEBUG(10, ("appending %s at ndx %d\n", name, buf_ndx));
271                         safe_strcpy(&buf[buf_ndx], name, len);
272                         buf_ndx += len;
273                         buf[buf_ndx] = ',';
274                         buf_ndx++;
275                 }
276         }
277
278         /* Allocate buffer */
279
280         if (!buf && buf_len != 0) {
281                 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
282                         DEBUG(1, ("out of memory\n"));
283                         result = False;
284                         goto done;
285                 }
286                 memset(buf, 0, buf_len);
287                 goto again;
288         }
289
290         if (buf && buf_ndx > 0) {
291                 buf[buf_ndx - 1] = '\0';
292         }
293
294         *gr_mem = buf;
295         *gr_mem_len = buf_len;
296
297         DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem, 
298                    (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); 
299         result = True;
300
301 done:
302
303         talloc_destroy(mem_ctx);
304         
305         DEBUG(10, ("fill_grent_mem returning %d\n", result));
306
307         return result;
308 }
309
310 /* Return a group structure from a group name */
311
312 void winbindd_getgrnam(struct winbindd_cli_state *state)
313 {
314         DOM_SID group_sid, tmp_sid;
315         uint32 grp_rid;
316         struct winbindd_domain *domain;
317         enum lsa_SidType name_type;
318         fstring name_domain, name_group;
319         char *tmp, *gr_mem;
320         size_t gr_mem_len;
321         size_t num_gr_mem;
322         gid_t gid;
323         union unid_t id;
324         NTSTATUS status;
325         
326         /* Ensure null termination */
327         state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
328
329         DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
330                   state->request.data.groupname));
331
332         /* Parse domain and groupname */
333         
334         memset(name_group, 0, sizeof(fstring));
335
336         tmp = state->request.data.groupname;
337         
338         parse_domain_user(tmp, name_domain, name_group);
339
340         /* if no domain or our local domain and no local tdb group, default to
341          * our local domain for aliases */
342
343         if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
344                 fstrcpy(name_domain, get_global_sam_name());
345         }
346
347         /* Get info for the domain */
348
349         if ((domain = find_domain_from_name(name_domain)) == NULL) {
350                 DEBUG(3, ("could not get domain sid for domain %s\n",
351                           name_domain));
352                 request_error(state);
353                 return;
354         }
355         /* should we deal with users for our domain? */
356         
357         if ( lp_winbind_trusted_domains_only() && domain->primary) {
358                 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
359                          "getgrnam() for %s\\%s.\n", name_domain, name_group));
360                 request_error(state);
361                 return;
362         }
363
364         /* Get rid and name type from name */
365         
366         if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, domain->name,
367                                          name_group, &group_sid, &name_type)) {
368                 DEBUG(1, ("group %s in domain %s does not exist\n", 
369                           name_group, name_domain));
370                 request_error(state);
371                 return;
372         }
373
374         if ( !((name_type==SID_NAME_DOM_GRP) ||
375                ((name_type==SID_NAME_ALIAS) && domain->primary) ||
376                ((name_type==SID_NAME_ALIAS) && domain->internal) ||
377                ((name_type==SID_NAME_WKN_GRP) && domain->internal)) )
378         {
379                 DEBUG(1, ("name '%s' is not a local, domain or builtin "
380                           "group: %d\n", name_group, name_type));
381                 request_error(state);
382                 return;
383         }
384
385         /* Make sure that the group SID is within the domain of the
386            original domain */
387
388         sid_copy( &tmp_sid, &group_sid );
389         sid_split_rid( &tmp_sid, &grp_rid );
390         if ( !sid_equal( &tmp_sid, &domain->sid ) ) {
391                 DEBUG(3,("winbindd_getgrnam: group %s resolves to a SID in the wrong domain [%s]\n", 
392                         state->request.data.groupname, sid_string_static(&group_sid)));
393                 request_error(state);
394                 return;
395         }
396
397         
398
399         /* Try to get the GID */
400
401         status = idmap_sid_to_gid(&group_sid, &gid, 0);
402
403         if (NT_STATUS_IS_OK(status)) {
404                 goto got_gid;
405         }
406
407         /* Maybe it's one of our aliases in passdb */
408
409         if (pdb_sid_to_id(&group_sid, &id, &name_type) &&
410             ((name_type == SID_NAME_ALIAS) ||
411              (name_type == SID_NAME_WKN_GRP))) {
412                 gid = id.gid;
413                 goto got_gid;
414         }
415
416         DEBUG(1, ("error converting unix gid to sid\n"));
417         request_error(state);
418         return;
419
420  got_gid:
421
422         if (!fill_grent(&state->response.data.gr, name_domain,
423                         name_group, gid) ||
424             !fill_grent_mem(domain, state, &group_sid, name_type,
425                             &num_gr_mem,
426                             &gr_mem, &gr_mem_len)) {
427                 request_error(state);
428                 return;
429         }
430
431         state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
432
433         /* Group membership lives at start of extra data */
434
435         state->response.data.gr.gr_mem_ofs = 0;
436
437         state->response.length += gr_mem_len;
438         state->response.extra_data.data = gr_mem;
439         request_ok(state);
440 }
441
442 static void getgrgid_got_sid(struct winbindd_cli_state *state, DOM_SID group_sid)
443 {
444         struct winbindd_domain *domain;
445         enum lsa_SidType name_type;
446         fstring dom_name;
447         fstring group_name;
448         size_t gr_mem_len;
449         size_t num_gr_mem;
450         char *gr_mem;
451
452         /* Get name from sid */
453
454         if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, dom_name,
455                                          group_name, &name_type)) {
456                 DEBUG(1, ("could not lookup sid\n"));
457                 request_error(state);
458                 return;
459         }
460
461         /* Fill in group structure */
462
463         domain = find_domain_from_sid_noinit(&group_sid);
464
465         if (!domain) {
466                 DEBUG(1,("Can't find domain from sid\n"));
467                 request_error(state);
468                 return;
469         }
470
471         if ( !((name_type==SID_NAME_DOM_GRP) ||
472                ((name_type==SID_NAME_ALIAS) && domain->primary) ||
473                ((name_type==SID_NAME_ALIAS) && domain->internal)) )
474         {
475                 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
476                           group_name, name_type));
477                 request_error(state);
478                 return;
479         }
480
481         if (!fill_grent(&state->response.data.gr, dom_name, group_name, 
482                         state->request.data.gid) ||
483             !fill_grent_mem(domain, state, &group_sid, name_type,
484                             &num_gr_mem,
485                             &gr_mem, &gr_mem_len)) {
486                 request_error(state);
487                 return;
488         }
489
490         state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
491
492         /* Group membership lives at start of extra data */
493
494         state->response.data.gr.gr_mem_ofs = 0;
495
496         state->response.length += gr_mem_len;
497         state->response.extra_data.data = gr_mem;
498
499         request_ok(state);
500 }
501
502 static void getgrgid_recv(void *private_data, BOOL success, const char *sid)
503 {
504         struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
505         enum lsa_SidType name_type;
506         DOM_SID group_sid;
507
508         if (success) {
509                 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
510                           (unsigned long)(state->request.data.gid), sid));
511
512                 string_to_sid(&group_sid, sid);
513                 getgrgid_got_sid(state, group_sid);
514                 return;
515         }
516
517         /* Ok, this might be "ours", i.e. an alias */
518         if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
519             lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
520             (name_type == SID_NAME_ALIAS)) {
521                 /* Hey, got an alias */
522                 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
523                           (unsigned long)(state->request.data.gid), sid));
524                 getgrgid_got_sid(state, group_sid);
525                 return;
526         }
527
528         DEBUG(1, ("could not convert gid %lu to sid\n", 
529                   (unsigned long)state->request.data.gid));
530         request_error(state);
531 }
532
533 /* Return a group structure from a gid number */
534 void winbindd_getgrgid(struct winbindd_cli_state *state)
535 {
536         DOM_SID group_sid;
537         NTSTATUS status;
538
539         DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, 
540                   (unsigned long)state->request.data.gid));
541
542         /* Bug out if the gid isn't in the winbind range */
543
544         if ((state->request.data.gid < server_state.gid_low) ||
545             (state->request.data.gid > server_state.gid_high)) {
546                 request_error(state);
547                 return;
548         }
549
550         /* Get sid from gid */
551
552         status = idmap_gid_to_sid(&group_sid, state->request.data.gid, IDMAP_FLAG_NONE);
553         if (NT_STATUS_IS_OK(status)) {
554                 /* This is a remote one */
555                 getgrgid_got_sid(state, group_sid);
556                 return;
557         }
558
559         DEBUG(10,("winbindd_getgrgid: gid %lu not found in cache, try with the async interface\n",
560                   (unsigned long)state->request.data.gid));
561
562         winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state);
563 }
564
565 /*
566  * set/get/endgrent functions
567  */
568
569 /* "Rewind" file pointer for group database enumeration */
570
571 static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state)
572 {
573         struct winbindd_domain *domain;
574
575         DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
576
577         /* Check user has enabled this */
578
579         if (!lp_winbind_enum_groups()) {
580                 return False;
581         }               
582
583         /* Free old static data if it exists */
584         
585         if (state->getgrent_state != NULL) {
586                 free_getent_state(state->getgrent_state);
587                 state->getgrent_state = NULL;
588         }
589         
590         /* Create sam pipes for each domain we know about */
591         
592         for (domain = domain_list(); domain != NULL; domain = domain->next) {
593                 struct getent_state *domain_state;
594                 
595                 /* Create a state record for this domain */
596
597                 /* don't add our domaina if we are a PDC or if we 
598                    are a member of a Samba domain */
599                 
600                 if ( lp_winbind_trusted_domains_only() && domain->primary )
601                 {
602                         continue;
603                 }
604                                                 
605                 
606                 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
607                         DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
608                         return False;
609                 }
610                 
611                 ZERO_STRUCTP(domain_state);
612                 
613                 fstrcpy(domain_state->domain_name, domain->name);
614
615                 /* Add to list of open domains */
616                 
617                 DLIST_ADD(state->getgrent_state, domain_state);
618         }
619         
620         state->getgrent_initialized = True;
621         return True;
622 }
623
624 void winbindd_setgrent(struct winbindd_cli_state *state)
625 {
626         if (winbindd_setgrent_internal(state)) {
627                 request_ok(state);
628         } else {
629                 request_error(state);
630         }
631 }
632
633 /* Close file pointer to ntdom group database */
634
635 void winbindd_endgrent(struct winbindd_cli_state *state)
636 {
637         DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
638
639         free_getent_state(state->getgrent_state);
640         state->getgrent_initialized = False;
641         state->getgrent_state = NULL;
642         request_ok(state);
643 }
644
645 /* Get the list of domain groups and domain aliases for a domain.  We fill in
646    the sam_entries and num_sam_entries fields with domain group information.  
647    The dispinfo_ndx field is incremented to the index of the next group to 
648    fetch. Return True if some groups were returned, False otherwise. */
649
650 static BOOL get_sam_group_entries(struct getent_state *ent)
651 {
652         NTSTATUS status;
653         uint32 num_entries;
654         struct acct_info *name_list = NULL;
655         TALLOC_CTX *mem_ctx;
656         BOOL result = False;
657         struct acct_info *sam_grp_entries = NULL;
658         struct winbindd_domain *domain;
659         
660         if (ent->got_sam_entries)
661                 return False;
662
663         if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
664                                           ent->domain_name))) {
665                 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); 
666                 return False;
667         }
668                 
669         /* Free any existing group info */
670
671         SAFE_FREE(ent->sam_entries);
672         ent->num_sam_entries = 0;
673         ent->got_sam_entries = True;
674
675         /* Enumerate domain groups */
676
677         num_entries = 0;
678
679         if (!(domain = find_domain_from_name(ent->domain_name))) {
680                 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
681                 goto done;
682         }
683
684         /* always get the domain global groups */
685
686         status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
687         
688         if (!NT_STATUS_IS_OK(status)) {
689                 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
690                 result = False;
691                 goto done;
692         }
693
694         /* Copy entries into return buffer */
695
696         if (num_entries) {
697                 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
698                         DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", 
699                                 num_entries));
700                         result = False;
701                         goto done;
702                 }
703                 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
704         }
705         
706         ent->num_sam_entries = num_entries;
707         
708         /* get the domain local groups if we are a member of a native win2k domain
709            and are not using LDAP to get the groups */
710            
711         if ( ( lp_security() != SEC_ADS && domain->native_mode 
712                 && domain->primary) || domain->internal )
713         {
714                 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n", 
715                         domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
716                 
717                 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
718                 
719                 if ( !NT_STATUS_IS_OK(status) ) { 
720                         DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
721                         num_entries = 0;
722                 }
723                 else
724                         DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
725                 
726                 /* Copy entries into return buffer */
727
728                 if ( num_entries ) {
729                         if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
730                         {
731                                 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", 
732                                         num_entries));
733                                 result = False;
734                                 goto done;
735                         }
736                         
737                         memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, 
738                                 num_entries * sizeof(struct acct_info) );
739                 }
740         
741                 ent->num_sam_entries += num_entries;
742         }
743         
744                 
745         /* Fill in remaining fields */
746
747         ent->sam_entries = name_list;
748         ent->sam_entry_index = 0;
749
750         result = (ent->num_sam_entries > 0);
751
752  done:
753         talloc_destroy(mem_ctx);
754
755         return result;
756 }
757
758 /* Fetch next group entry from ntdom database */
759
760 #define MAX_GETGRENT_GROUPS 500
761
762 void winbindd_getgrent(struct winbindd_cli_state *state)
763 {
764         struct getent_state *ent;
765         struct winbindd_gr *group_list = NULL;
766         int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
767         char *gr_mem_list = NULL;
768
769         DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
770
771         /* Check user has enabled this */
772
773         if (!lp_winbind_enum_groups()) {
774                 request_error(state);
775                 return;
776         }
777
778         num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
779
780         if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
781                 request_error(state);
782                 return;
783         }
784
785         memset(state->response.extra_data.data, '\0',
786                 num_groups * sizeof(struct winbindd_gr) );
787
788         state->response.data.num_entries = 0;
789
790         group_list = (struct winbindd_gr *)state->response.extra_data.data;
791
792         if (!state->getgrent_initialized)
793                 winbindd_setgrent_internal(state);
794
795         if (!(ent = state->getgrent_state)) {
796                 request_error(state);
797                 return;
798         }
799
800         /* Start sending back groups */
801
802         for (i = 0; i < num_groups; i++) {
803                 struct acct_info *name_list = NULL;
804                 fstring domain_group_name;
805                 uint32 result;
806                 gid_t group_gid;
807                 size_t gr_mem_len;
808                 char *gr_mem;
809                 DOM_SID group_sid;
810                 struct winbindd_domain *domain;
811                                 
812                 /* Do we need to fetch another chunk of groups? */
813
814         tryagain:
815
816                 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
817                            ent->sam_entry_index, ent->num_sam_entries));
818
819                 if (ent->num_sam_entries == ent->sam_entry_index) {
820
821                         while(ent && !get_sam_group_entries(ent)) {
822                                 struct getent_state *next_ent;
823
824                                 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 
825
826                                 /* Free state information for this domain */
827
828                                 SAFE_FREE(ent->sam_entries);
829
830                                 next_ent = ent->next;
831                                 DLIST_REMOVE(state->getgrent_state, ent);
832                                 
833                                 SAFE_FREE(ent);
834                                 ent = next_ent;
835                         }
836
837                         /* No more domains */
838
839                         if (!ent) 
840                                 break;
841                 }
842                 
843                 name_list = (struct acct_info *)ent->sam_entries;
844                 
845                 if (!(domain = 
846                       find_domain_from_name(ent->domain_name))) {
847                         DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
848                         result = False;
849                         goto done;
850                 }
851
852                 /* Lookup group info */
853                 
854                 sid_copy(&group_sid, &domain->sid);
855                 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
856
857                 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid,
858                                                     &group_gid, 0))) {
859                         union unid_t id;
860                         enum lsa_SidType type;
861
862                         DEBUG(10, ("SID %s not in idmap\n",
863                                    sid_string_static(&group_sid)));
864
865                         if (!pdb_sid_to_id(&group_sid, &id, &type)) {
866                                 DEBUG(1, ("could not look up gid for group "
867                                           "%s\n", 
868                                           name_list[ent->sam_entry_index].acct_name));
869                                 ent->sam_entry_index++;
870                                 goto tryagain;
871                         }
872
873                         if ((type != SID_NAME_DOM_GRP) &&
874                             (type != SID_NAME_ALIAS) &&
875                             (type != SID_NAME_WKN_GRP)) {
876                                 DEBUG(1, ("Group %s is a %s, not a group\n",
877                                           sid_type_lookup(type),
878                                           name_list[ent->sam_entry_index].acct_name));
879                                 ent->sam_entry_index++;
880                                 goto tryagain;
881                         }
882                         group_gid = id.gid;
883                 }
884
885                 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
886                            (unsigned long)name_list[ent->sam_entry_index].rid));
887                 
888                 /* Fill in group entry */
889
890                 fill_domain_username(domain_group_name, ent->domain_name, 
891                          name_list[ent->sam_entry_index].acct_name, True);
892
893                 result = fill_grent(&group_list[group_list_ndx], 
894                                     ent->domain_name,
895                                     name_list[ent->sam_entry_index].acct_name,
896                                     group_gid);
897
898                 /* Fill in group membership entry */
899
900                 if (result) {
901                         size_t num_gr_mem = 0;
902                         DOM_SID member_sid;
903                         group_list[group_list_ndx].num_gr_mem = 0;
904                         gr_mem = NULL;
905                         gr_mem_len = 0;
906                         
907                         /* Get group membership */                      
908                         if (state->request.cmd == WINBINDD_GETGRLST) {
909                                 result = True;
910                         } else {
911                                 sid_copy(&member_sid, &domain->sid);
912                                 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
913                                 result = fill_grent_mem(
914                                         domain,
915                                         NULL,
916                                         &member_sid,
917                                         SID_NAME_DOM_GRP,
918                                         &num_gr_mem,
919                                         &gr_mem, &gr_mem_len);
920
921                                 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
922                         }
923                 }
924
925                 if (result) {
926                         /* Append to group membership list */
927                         gr_mem_list = (char *)SMB_REALLOC(
928                                 gr_mem_list, gr_mem_list_len + gr_mem_len);
929
930                         if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
931                                 DEBUG(0, ("out of memory\n"));
932                                 gr_mem_list_len = 0;
933                                 break;
934                         }
935
936                         DEBUG(10, ("list_len = %d, mem_len = %u\n",
937                                    gr_mem_list_len, (unsigned int)gr_mem_len));
938
939                         memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
940                                gr_mem_len);
941
942                         SAFE_FREE(gr_mem);
943
944                         group_list[group_list_ndx].gr_mem_ofs = 
945                                 gr_mem_list_len;
946
947                         gr_mem_list_len += gr_mem_len;
948                 }
949
950                 ent->sam_entry_index++;
951                 
952                 /* Add group to return list */
953                 
954                 if (result) {
955
956                         DEBUG(10, ("adding group num_entries = %d\n",
957                                    state->response.data.num_entries));
958
959                         group_list_ndx++;
960                         state->response.data.num_entries++;
961                         
962                         state->response.length +=
963                                 sizeof(struct winbindd_gr);
964                         
965                 } else {
966                         DEBUG(0, ("could not lookup domain group %s\n", 
967                                   domain_group_name));
968                 }
969         }
970
971         /* Copy the list of group memberships to the end of the extra data */
972
973         if (group_list_ndx == 0)
974                 goto done;
975
976         state->response.extra_data.data = SMB_REALLOC(
977                 state->response.extra_data.data,
978                 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
979
980         if (!state->response.extra_data.data) {
981                 DEBUG(0, ("out of memory\n"));
982                 group_list_ndx = 0;
983                 SAFE_FREE(gr_mem_list);
984                 request_error(state);
985                 return;
986         }
987
988         memcpy(&((char *)state->response.extra_data.data)
989                [group_list_ndx * sizeof(struct winbindd_gr)], 
990                gr_mem_list, gr_mem_list_len);
991
992         state->response.length += gr_mem_list_len;
993
994         DEBUG(10, ("returning %d groups, length = %d\n",
995                    group_list_ndx, gr_mem_list_len));
996
997         /* Out of domains */
998
999  done:
1000
1001         SAFE_FREE(gr_mem_list);
1002
1003         if (group_list_ndx > 0)
1004                 request_ok(state);
1005         else
1006                 request_error(state);
1007 }
1008
1009 /* List domain groups without mapping to unix ids */
1010
1011 void winbindd_list_groups(struct winbindd_cli_state *state)
1012 {
1013         uint32 total_entries = 0;
1014         struct winbindd_domain *domain;
1015         const char *which_domain;
1016         char *extra_data = NULL;
1017         unsigned int extra_data_len = 0, i;
1018
1019         DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
1020
1021         /* Ensure null termination */
1022         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
1023         which_domain = state->request.domain_name;
1024         
1025         /* Enumerate over trusted domains */
1026
1027         for (domain = domain_list(); domain; domain = domain->next) {
1028                 struct getent_state groups;
1029
1030                 /* if we have a domain name restricting the request and this
1031                    one in the list doesn't match, then just bypass the remainder
1032                    of the loop */
1033                    
1034                 if ( *which_domain && !strequal(which_domain, domain->name) )
1035                         continue;
1036                         
1037                 ZERO_STRUCT(groups);
1038
1039                 /* Get list of sam groups */
1040                 
1041                 fstrcpy(groups.domain_name, domain->name);
1042
1043                 get_sam_group_entries(&groups);
1044                         
1045                 if (groups.num_sam_entries == 0) {
1046                         /* this domain is empty or in an error state */
1047                         continue;
1048                 }
1049
1050                 /* keep track the of the total number of groups seen so 
1051                    far over all domains */
1052                 total_entries += groups.num_sam_entries;
1053                 
1054                 /* Allocate some memory for extra data.  Note that we limit
1055                    account names to sizeof(fstring) = 128 characters.  */               
1056                 extra_data = (char *)SMB_REALLOC(
1057                         extra_data, sizeof(fstring) * total_entries);
1058  
1059                 if (!extra_data) {
1060                         DEBUG(0,("failed to enlarge buffer!\n"));
1061                         request_error(state);
1062                         return;
1063                 }
1064
1065                 /* Pack group list into extra data fields */
1066                 for (i = 0; i < groups.num_sam_entries; i++) {
1067                         char *group_name = ((struct acct_info *)
1068                                             groups.sam_entries)[i].acct_name; 
1069                         fstring name;
1070
1071                         fill_domain_username(name, domain->name, group_name, True);
1072                         /* Append to extra data */                      
1073                         memcpy(&extra_data[extra_data_len], name, 
1074                                strlen(name));
1075                         extra_data_len += strlen(name);
1076                         extra_data[extra_data_len++] = ',';
1077                 }
1078
1079                 SAFE_FREE(groups.sam_entries);
1080         }
1081
1082         /* Assign extra_data fields in response structure */
1083         if (extra_data) {
1084                 extra_data[extra_data_len - 1] = '\0';
1085                 state->response.extra_data.data = extra_data;
1086                 state->response.length += extra_data_len;
1087         }
1088
1089         /* No domains may have responded but that's still OK so don't
1090            return an error. */
1091
1092         request_ok(state);
1093 }
1094
1095 /* Get user supplementary groups.  This is much quicker than trying to
1096    invert the groups database.  We merge the groups from the gids and
1097    other_sids info3 fields as trusted domain, universal group
1098    memberships, and nested groups (win2k native mode only) are not
1099    returned by the getgroups RPC call but are present in the info3. */
1100
1101 struct getgroups_state {
1102         struct winbindd_cli_state *state;
1103         struct winbindd_domain *domain;
1104         char *domname;
1105         char *username;
1106         DOM_SID user_sid;
1107
1108         const DOM_SID *token_sids;
1109         size_t i, num_token_sids;
1110
1111         gid_t *token_gids;
1112         size_t num_token_gids;
1113 };
1114
1115 static void getgroups_usersid_recv(void *private_data, BOOL success,
1116                                    const DOM_SID *sid, enum lsa_SidType type);
1117 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1118                                      DOM_SID *token_sids, size_t num_token_sids);
1119 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
1120
1121 void winbindd_getgroups(struct winbindd_cli_state *state)
1122 {
1123         struct getgroups_state *s;
1124
1125         /* Ensure null termination */
1126         state->request.data.username
1127                 [sizeof(state->request.data.username)-1]='\0';
1128
1129         DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1130                   state->request.data.username));
1131
1132         /* when using "winbind use default domain" we need to avoid that
1133          * initgroups() requests from NSS hit our DC too badly for accounts
1134          * that will never be on the remote DC */
1135
1136         if (lp_winbind_use_default_domain()) {
1137                 
1138                 const char **list = lp_winbind_initgroups_blacklist();
1139                 int i;
1140
1141                 if (!list || !list[0]) {
1142                         goto parse;
1143                 }
1144
1145                 for (i=0; list[i] != NULL; i++) {
1146         
1147                         if (strequal(state->request.data.username, list[i])) {
1148                                 DEBUG(3,("ignoring blacklisted user [%s] for getgroups\n", 
1149                                         state->request.data.username));
1150                                 request_ok(state);
1151                                 return;
1152                         }
1153                 }
1154         }
1155  parse:
1156         /* Parse domain and username */
1157
1158         s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1159         if (s == NULL) {
1160                 DEBUG(0, ("talloc failed\n"));
1161                 request_error(state);
1162                 return;
1163         }
1164
1165         s->state = state;
1166
1167         if (!parse_domain_user_talloc(state->mem_ctx,
1168                                       state->request.data.username,
1169                                       &s->domname, &s->username)) {
1170                 DEBUG(5, ("Could not parse domain user: %s\n",
1171                           state->request.data.username));
1172
1173                 /* error out if we do not have nested group support */
1174
1175                 if ( !lp_winbind_nested_groups() ) {
1176                         request_error(state);
1177                         return;
1178                 }
1179
1180                 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1181                 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1182         }
1183         
1184         /* Get info for the domain */
1185
1186         s->domain = find_domain_from_name_noinit(s->domname);
1187
1188         if (s->domain == NULL) {
1189                 DEBUG(7, ("could not find domain entry for domain %s\n", 
1190                           s->domname));
1191                 request_error(state);
1192                 return;
1193         }
1194
1195         if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1196                 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting "
1197                          "getgroups() for %s\\%s.\n", s->domname,
1198                          s->username));
1199                 request_error(state);
1200                 return;
1201         }       
1202
1203         /* Get rid and name type from name.  The following costs 1 packet */
1204
1205         winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1206                                   getgroups_usersid_recv, s);
1207 }
1208
1209 static void getgroups_usersid_recv(void *private_data, BOOL success,
1210                                    const DOM_SID *sid, enum lsa_SidType type)
1211 {
1212         struct getgroups_state *s =
1213                 (struct getgroups_state *)private_data;
1214
1215         if ((!success) ||
1216             ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1217                 request_error(s->state);
1218                 return;
1219         }
1220
1221         sid_copy(&s->user_sid, sid);
1222
1223         winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1224                                 getgroups_tokensids_recv, s);
1225 }
1226
1227 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1228                                      DOM_SID *token_sids, size_t num_token_sids)
1229 {
1230         struct getgroups_state *s =
1231                 (struct getgroups_state *)private_data;
1232
1233         /* We need at least the user sid and the primary group in the token,
1234          * otherwise it's an error */
1235
1236         if ((!success) || (num_token_sids < 2)) {
1237                 request_error(s->state);
1238                 return;
1239         }
1240
1241         s->token_sids = token_sids;
1242         s->num_token_sids = num_token_sids;
1243         s->i = 0;
1244
1245         s->token_gids = NULL;
1246         s->num_token_gids = 0;
1247
1248         getgroups_sid2gid_recv(s, False, 0);
1249 }
1250
1251 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
1252 {
1253         struct getgroups_state *s =
1254                 (struct getgroups_state *)private_data;
1255
1256         if (success)
1257                 add_gid_to_array_unique(NULL, gid,
1258                                         &s->token_gids,
1259                                         &s->num_token_gids);
1260
1261         if (s->i < s->num_token_sids) {
1262                 const DOM_SID *sid = &s->token_sids[s->i];
1263                 s->i += 1;
1264
1265                 if (sid_equal(sid, &s->user_sid)) {
1266                         getgroups_sid2gid_recv(s, False, 0);
1267                         return;
1268                 }
1269
1270                 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1271                                        getgroups_sid2gid_recv, s);
1272                 return;
1273         }
1274
1275         s->state->response.data.num_entries = s->num_token_gids;
1276         s->state->response.extra_data.data = s->token_gids;
1277         s->state->response.length += s->num_token_gids * sizeof(gid_t);
1278         request_ok(s->state);
1279 }
1280
1281 /* Get user supplementary sids. This is equivalent to the
1282    winbindd_getgroups() function but it involves a SID->SIDs mapping
1283    rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1284    idmap. This call is designed to be used with applications that need
1285    to do ACL evaluation themselves. Note that the cached info3 data is
1286    not used 
1287
1288    this function assumes that the SID that comes in is a user SID. If
1289    you pass in another type of SID then you may get unpredictable
1290    results.
1291 */
1292
1293 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1294                              size_t num_sids);
1295
1296 void winbindd_getusersids(struct winbindd_cli_state *state)
1297 {
1298         DOM_SID *user_sid;
1299
1300         /* Ensure null termination */
1301         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1302
1303         user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1304         if (user_sid == NULL) {
1305                 DEBUG(1, ("talloc failed\n"));
1306                 request_error(state);
1307                 return;
1308         }
1309
1310         if (!string_to_sid(user_sid, state->request.data.sid)) {
1311                 DEBUG(1, ("Could not get convert sid %s from string\n",
1312                           state->request.data.sid));
1313                 request_error(state);
1314                 return;
1315         }
1316
1317         winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1318                                 state);
1319 }
1320
1321 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1322                              size_t num_sids)
1323 {
1324         struct winbindd_cli_state *state =
1325                 (struct winbindd_cli_state *)private_data;
1326         char *ret = NULL;
1327         unsigned ofs, ret_size = 0;
1328         size_t i;
1329
1330         if (!success) {
1331                 request_error(state);
1332                 return;
1333         }
1334
1335         /* work out the response size */
1336         for (i = 0; i < num_sids; i++) {
1337                 const char *s = sid_string_static(&sids[i]);
1338                 ret_size += strlen(s) + 1;
1339         }
1340
1341         /* build the reply */
1342         ret = (char *)SMB_MALLOC(ret_size);
1343         if (!ret) {
1344                 DEBUG(0, ("malloc failed\n"));
1345                 request_error(state);
1346                 return;
1347         }
1348         ofs = 0;
1349         for (i = 0; i < num_sids; i++) {
1350                 const char *s = sid_string_static(&sids[i]);
1351                 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1352                 ofs += strlen(ret+ofs) + 1;
1353         }
1354
1355         /* Send data back to client */
1356         state->response.data.num_entries = num_sids;
1357         state->response.extra_data.data = ret;
1358         state->response.length += ret_size;
1359         request_ok(state);
1360 }
1361
1362 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1363 {
1364         DOM_SID user_sid;
1365         struct winbindd_domain *domain;
1366
1367         /* Ensure null termination */
1368         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1369
1370         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1371                 DEBUG(1, ("Could not get convert sid %s from string\n",
1372                           state->request.data.sid));
1373                 request_error(state);
1374                 return;
1375         }
1376
1377         /* Get info for the domain */   
1378         if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1379                 DEBUG(0,("could not find domain entry for sid %s\n", 
1380                          sid_string_static(&user_sid)));
1381                 request_error(state);
1382                 return;
1383         }
1384
1385         sendto_domain(state, domain);
1386 }
1387
1388 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1389                                                     struct winbindd_cli_state *state)
1390 {
1391         DOM_SID user_sid;
1392         NTSTATUS status;
1393
1394         char *sidstring;
1395         ssize_t len;
1396         DOM_SID *groups;
1397         uint32 num_groups;
1398
1399         /* Ensure null termination */
1400         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1401
1402         if (!string_to_sid(&user_sid, state->request.data.sid)) {
1403                 DEBUG(1, ("Could not get convert sid %s from string\n",
1404                           state->request.data.sid));
1405                 return WINBINDD_ERROR;
1406         }
1407
1408         status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1409                                                     &user_sid, &num_groups,
1410                                                     &groups);
1411         if (!NT_STATUS_IS_OK(status))
1412                 return WINBINDD_ERROR;
1413
1414         if (num_groups == 0) {
1415                 state->response.data.num_entries = 0;
1416                 state->response.extra_data.data = NULL;
1417                 return WINBINDD_OK;
1418         }
1419
1420         if (!print_sidlist(NULL, groups, num_groups, &sidstring, &len)) {
1421                 DEBUG(0, ("malloc failed\n"));
1422                 return WINBINDD_ERROR;
1423         }
1424
1425         state->response.extra_data.data = sidstring;
1426         state->response.length += len+1;
1427         state->response.data.num_entries = num_groups;
1428
1429         return WINBINDD_OK;
1430 }