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