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