1/*
2   Unix SMB/CIFS implementation.
3
4   Winbind daemon - user related functions
5
6   Copyright (C) Tim Potter 2000
7   Copyright (C) Jeremy Allison 2001.
8   Copyright (C) Gerald (Jerry) Carter 2003.
9
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or
13   (at your option) any later version.
14
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23*/
24
25#include "includes.h"
26#include "winbindd.h"
27
28#undef DBGC_CLASS
29#define DBGC_CLASS DBGC_WINBIND
30
31extern userdom_struct current_user_info;
32
33/* Fill a pwent structure with information we have obtained */
34
35static BOOL winbindd_fill_pwent(char *dom_name, char *user_name,
36				DOM_SID *user_sid, DOM_SID *group_sid,
37				char *full_name, struct winbindd_pw *pw)
38{
39	fstring output_username;
40	char *homedir;
41	char *shell;
42	fstring sid_string;
43
44	if (!pw || !dom_name || !user_name)
45		return False;
46
47	/* Resolve the uid number */
48
49	if (!NT_STATUS_IS_OK(idmap_sid_to_uid(user_sid, &pw->pw_uid, 0))) {
50		DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid)));
51		return False;
52	}
53
54	/* Resolve the gid number */
55
56	if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &pw->pw_gid, 0))) {
57		DEBUG(1, ("error getting group id for sid %s\n", sid_to_string(sid_string, group_sid)));
58		return False;
59	}
60
61	strlower_m(user_name);
62
63	/* Username */
64
65	fill_domain_username(output_username, dom_name, user_name);
66
67	safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
68
69	/* Full name (gecos) */
70
71	safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
72
73	/* Home directory and shell - use template config parameters.  The
74	   defaults are /tmp for the home directory and /bin/false for
75	   shell. */
76
77	/* The substitution of %U and %D in the 'template homedir' is done
78	   by alloc_sub_specified() below. */
79
80	fstrcpy(current_user_info.domain, dom_name);
81
82	homedir = alloc_sub_specified(lp_template_homedir(), user_name, dom_name, pw->pw_uid, pw->pw_gid);
83
84	if (!homedir)
85		return False;
86
87	safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
88
89	SAFE_FREE(homedir);
90
91	shell = alloc_sub_specified(lp_template_shell(), user_name, dom_name, pw->pw_uid, pw->pw_gid);
92
93	if (!shell)
94		return False;
95
96	safe_strcpy(pw->pw_shell, shell,
97		    sizeof(pw->pw_shell) - 1);
98
99	SAFE_FREE(shell);
100
101	/* Password - set to "x" as we can't generate anything useful here.
102	   Authentication can be done using the pam_winbind module. */
103
104	safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
105
106	return True;
107}
108
109/* Return a password structure from a username.  */
110
111enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state)
112{
113	WINBIND_USERINFO user_info;
114	WINBINDD_PW *pw;
115	DOM_SID user_sid;
116	NTSTATUS status;
117	fstring name_domain, name_user;
118	enum SID_NAME_USE name_type;
119	struct winbindd_domain *domain;
120	TALLOC_CTX *mem_ctx;
121
122	/* Ensure null termination */
123	state->request.data.username[sizeof(state->request.data.username)-1]='\0';
124
125	DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
126		  state->request.data.username));
127
128	/* Parse domain and username */
129
130	parse_domain_user(state->request.data.username,
131			  name_domain, name_user);
132
133	/* if this is our local domain (or no domain), the do a local tdb search */
134
135	if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
136		if ( !(pw = wb_getpwnam(name_user)) ) {
137			DEBUG(5,("winbindd_getpwnam: lookup for %s\\%s failed\n",
138				name_domain, name_user));
139			return WINBINDD_ERROR;
140		}
141		memcpy( &state->response.data.pw, pw, sizeof(WINBINDD_PW) );
142		return WINBINDD_OK;
143	}
144
145	/* should we deal with users for our domain? */
146
147	if ((domain = find_domain_from_name(name_domain)) == NULL) {
148		DEBUG(5, ("no such domain: %s\n", name_domain));
149		return WINBINDD_ERROR;
150	}
151
152	if ( domain->primary && lp_winbind_trusted_domains_only()) {
153		DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n",
154			name_domain, name_user));
155		return WINBINDD_ERROR;
156	}
157
158	/* Get rid and name type from name */
159
160	if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid, &name_type)) {
161		DEBUG(1, ("user '%s' does not exist\n", name_user));
162		return WINBINDD_ERROR;
163	}
164
165	if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
166		DEBUG(1, ("name '%s' is not a user name: %d\n", name_user,
167			  name_type));
168		return WINBINDD_ERROR;
169	}
170
171	/* Get some user info. */
172
173	if (!(mem_ctx = talloc_init("winbindd_getpwnam([%s]\\[%s])",
174					  name_domain, name_user))) {
175		DEBUG(1, ("out of memory\n"));
176		return WINBINDD_ERROR;
177	}
178
179	status = domain->methods->query_user(domain, mem_ctx, &user_sid,
180					     &user_info);
181
182	if (!NT_STATUS_IS_OK(status)) {
183		DEBUG(1, ("error getting user info for user '[%s]\\[%s]'\n",
184			  name_domain, name_user));
185		talloc_destroy(mem_ctx);
186		return WINBINDD_ERROR;
187	}
188
189	/* Now take all this information and fill in a passwd structure */
190	if (!winbindd_fill_pwent(name_domain, user_info.acct_name,
191				 user_info.user_sid, user_info.group_sid,
192				 user_info.full_name,
193				 &state->response.data.pw)) {
194		talloc_destroy(mem_ctx);
195		return WINBINDD_ERROR;
196	}
197
198	talloc_destroy(mem_ctx);
199
200	return WINBINDD_OK;
201}
202
203/* Return a password structure given a uid number */
204
205enum winbindd_result winbindd_getpwuid(struct winbindd_cli_state *state)
206{
207	DOM_SID user_sid;
208	struct winbindd_domain *domain;
209	WINBINDD_PW *pw;
210	fstring dom_name;
211	fstring user_name;
212	enum SID_NAME_USE name_type;
213	WINBIND_USERINFO user_info;
214	TALLOC_CTX *mem_ctx;
215	NTSTATUS status;
216	gid_t gid;
217
218	/* Bug out if the uid isn't in the winbind range */
219
220	if ((state->request.data.uid < server_state.uid_low ) ||
221	    (state->request.data.uid > server_state.uid_high))
222		return WINBINDD_ERROR;
223
224	DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid,
225		  (unsigned long)state->request.data.uid));
226
227	/* always try local tdb first */
228
229	if ( (pw = wb_getpwuid(state->request.data.uid)) != NULL ) {
230		memcpy( &state->response.data.pw, pw, sizeof(WINBINDD_PW) );
231		return WINBINDD_OK;
232	}
233
234	/* Get rid from uid */
235
236	if (!NT_STATUS_IS_OK(idmap_uid_to_sid(&user_sid, state->request.data.uid))) {
237		DEBUG(1, ("could not convert uid %lu to SID\n",
238			  (unsigned long)state->request.data.uid));
239		return WINBINDD_ERROR;
240	}
241
242	/* Get name and name type from rid */
243
244	if (!winbindd_lookup_name_by_sid(&user_sid, dom_name, user_name, &name_type)) {
245		fstring temp;
246
247		sid_to_string(temp, &user_sid);
248		DEBUG(1, ("could not lookup sid %s\n", temp));
249		return WINBINDD_ERROR;
250	}
251
252	domain = find_domain_from_sid(&user_sid);
253
254	if (!domain) {
255		DEBUG(1,("Can't find domain from sid\n"));
256		return WINBINDD_ERROR;
257	}
258
259	/* Get some user info */
260
261	if (!(mem_ctx = talloc_init("winbind_getpwuid(%lu)",
262				    (unsigned long)state->request.data.uid))) {
263
264		DEBUG(1, ("out of memory\n"));
265		return WINBINDD_ERROR;
266	}
267
268	status = domain->methods->query_user(domain, mem_ctx, &user_sid,
269					     &user_info);
270
271	if (!NT_STATUS_IS_OK(status)) {
272		DEBUG(1, ("error getting user info for user '%s'\n",
273			  user_name));
274		talloc_destroy(mem_ctx);
275		return WINBINDD_ERROR;
276	}
277
278	/* Check group has a gid number */
279
280	if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_info.group_sid, &gid, 0))) {
281		DEBUG(1, ("error getting group id for user %s\n", user_name));
282		talloc_destroy(mem_ctx);
283		return WINBINDD_ERROR;
284	}
285
286	/* Fill in password structure */
287
288	if (!winbindd_fill_pwent(domain->name, user_info.acct_name, user_info.user_sid,
289				 user_info.group_sid,
290				 user_info.full_name, &state->response.data.pw)) {
291		talloc_destroy(mem_ctx);
292		return WINBINDD_ERROR;
293	}
294
295	talloc_destroy(mem_ctx);
296
297	return WINBINDD_OK;
298}
299
300/*
301 * set/get/endpwent functions
302 */
303
304/* Rewind file pointer for ntdom passwd database */
305
306enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
307{
308	struct winbindd_domain *domain;
309
310	DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
311
312	/* Check user has enabled this */
313
314	if (!lp_winbind_enum_users())
315		return WINBINDD_ERROR;
316
317	/* Free old static data if it exists */
318
319	if (state->getpwent_state != NULL) {
320		free_getent_state(state->getpwent_state);
321		state->getpwent_state = NULL;
322	}
323
324#if 0	/* JERRY */
325	/* add any local users we have */
326
327	if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
328		return WINBINDD_ERROR;
329
330	ZERO_STRUCTP(domain_state);
331
332	/* Add to list of open domains */
333
334	DLIST_ADD(state->getpwent_state, domain_state);
335#endif
336
337	/* Create sam pipes for each domain we know about */
338
339	for(domain = domain_list(); domain != NULL; domain = domain->next) {
340		struct getent_state *domain_state;
341
342
343		/* don't add our domaina if we are a PDC or if we
344		   are a member of a Samba domain */
345
346		if ( (IS_DC || lp_winbind_trusted_domains_only())
347			&& strequal(domain->name, lp_workgroup()) )
348		{
349			continue;
350		}
351
352		/* Create a state record for this domain */
353
354		if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL)
355			return WINBINDD_ERROR;
356
357		ZERO_STRUCTP(domain_state);
358
359		fstrcpy(domain_state->domain_name, domain->name);
360
361		/* Add to list of open domains */
362
363		DLIST_ADD(state->getpwent_state, domain_state);
364	}
365
366	state->getpwent_initialized = True;
367
368	return WINBINDD_OK;
369}
370
371/* Close file pointer to ntdom passwd database */
372
373enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
374{
375	DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
376
377	free_getent_state(state->getpwent_state);
378	state->getpwent_initialized = False;
379	state->getpwent_state = NULL;
380
381	return WINBINDD_OK;
382}
383
384/* Get partial list of domain users for a domain.  We fill in the sam_entries,
385   and num_sam_entries fields with domain user information.  The dispinfo_ndx
386   field is incremented to the index of the next user to fetch.  Return True if
387   some users were returned, False otherwise. */
388
389static BOOL get_sam_user_entries(struct getent_state *ent)
390{
391	NTSTATUS status;
392	uint32 num_entries;
393	WINBIND_USERINFO *info;
394	struct getpwent_user *name_list = NULL;
395	BOOL result = False;
396	TALLOC_CTX *mem_ctx;
397	struct winbindd_domain *domain;
398	struct winbindd_methods *methods;
399	unsigned int i;
400
401	if (ent->num_sam_entries)
402		return False;
403
404	if (!(mem_ctx = talloc_init("get_sam_user_entries(%s)",
405				    ent->domain_name)))
406		return False;
407
408	if (!(domain = find_domain_from_name(ent->domain_name))) {
409		DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
410			  ent->domain_name));
411		return False;
412	}
413
414	methods = domain->methods;
415
416	/* Free any existing user info */
417
418	SAFE_FREE(ent->sam_entries);
419	ent->num_sam_entries = 0;
420
421	/* Call query_user_list to get a list of usernames and user rids */
422
423	num_entries = 0;
424
425	status = methods->query_user_list(domain, mem_ctx, &num_entries,
426					  &info);
427
428	if (num_entries) {
429		struct getpwent_user *tnl;
430
431		tnl = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries);
432
433		if (!tnl) {
434			DEBUG(0,("get_sam_user_entries realloc failed.\n"));
435			SAFE_FREE(name_list);
436			goto done;
437		} else
438			name_list = tnl;
439	}
440
441	for (i = 0; i < num_entries; i++) {
442		/* Store account name and gecos */
443		if (!info[i].acct_name) {
444			fstrcpy(name_list[ent->num_sam_entries + i].name, "");
445		} else {
446			fstrcpy(name_list[ent->num_sam_entries + i].name,
447				info[i].acct_name);
448		}
449		if (!info[i].full_name) {
450			fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
451		} else {
452			fstrcpy(name_list[ent->num_sam_entries + i].gecos,
453				info[i].full_name);
454		}
455
456		/* User and group ids */
457		sid_copy(&name_list[ent->num_sam_entries+i].user_sid, info[i].user_sid);
458		sid_copy(&name_list[ent->num_sam_entries+i].group_sid, info[i].group_sid);
459	}
460
461	ent->num_sam_entries += num_entries;
462
463	/* Fill in remaining fields */
464
465	ent->sam_entries = name_list;
466	ent->sam_entry_index = 0;
467	result = ent->num_sam_entries > 0;
468
469 done:
470
471	talloc_destroy(mem_ctx);
472
473	return result;
474}
475
476/* Fetch next passwd entry from ntdom database */
477
478#define MAX_GETPWENT_USERS 500
479
480enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
481{
482	struct getent_state *ent;
483	struct winbindd_pw *user_list;
484	int num_users, user_list_ndx = 0, i;
485
486	DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
487
488	/* Check user has enabled this */
489
490	if (!lp_winbind_enum_users())
491		return WINBINDD_ERROR;
492
493	/* Allocate space for returning a chunk of users */
494
495	num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
496
497	if ((state->response.extra_data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL)
498		return WINBINDD_ERROR;
499
500	memset(state->response.extra_data, 0, num_users *
501	       sizeof(struct winbindd_pw));
502
503	user_list = (struct winbindd_pw *)state->response.extra_data;
504
505	if (!state->getpwent_initialized)
506		winbindd_setpwent(state);
507
508	if (!(ent = state->getpwent_state))
509		return WINBINDD_ERROR;
510
511	/* Start sending back users */
512
513	for (i = 0; i < num_users; i++) {
514		struct getpwent_user *name_list = NULL;
515		uint32 result;
516
517		/* Do we need to fetch another chunk of users? */
518
519		if (ent->num_sam_entries == ent->sam_entry_index) {
520
521			while(ent && !get_sam_user_entries(ent)) {
522				struct getent_state *next_ent;
523
524				/* Free state information for this domain */
525
526				SAFE_FREE(ent->sam_entries);
527
528				next_ent = ent->next;
529				DLIST_REMOVE(state->getpwent_state, ent);
530
531				SAFE_FREE(ent);
532				ent = next_ent;
533			}
534
535			/* No more domains */
536
537			if (!ent)
538				break;
539		}
540
541		name_list = ent->sam_entries;
542
543		/* Lookup user info */
544
545		result = winbindd_fill_pwent(
546			ent->domain_name,
547			name_list[ent->sam_entry_index].name,
548			&name_list[ent->sam_entry_index].user_sid,
549			&name_list[ent->sam_entry_index].group_sid,
550			name_list[ent->sam_entry_index].gecos,
551			&user_list[user_list_ndx]);
552
553		ent->sam_entry_index++;
554
555		/* Add user to return list */
556
557		if (result) {
558
559			user_list_ndx++;
560			state->response.data.num_entries++;
561			state->response.length +=
562				sizeof(struct winbindd_pw);
563
564		} else
565			DEBUG(1, ("could not lookup domain user %s\n",
566				  name_list[ent->sam_entry_index].name));
567	}
568
569	/* Out of domains */
570
571	return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
572}
573
574/* List domain users without mapping to unix ids */
575
576enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
577{
578	struct winbindd_domain *domain;
579	WINBIND_USERINFO *info;
580	const char *which_domain;
581	uint32 num_entries = 0, total_entries = 0;
582	char *ted, *extra_data = NULL;
583	int extra_data_len = 0;
584	TALLOC_CTX *mem_ctx;
585	enum winbindd_result rv = WINBINDD_ERROR;
586
587	DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
588
589	if (!(mem_ctx = talloc_init("winbindd_list_users")))
590		return WINBINDD_ERROR;
591
592	/* Ensure null termination */
593	state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
594	which_domain = state->request.domain_name;
595
596	/* Enumerate over trusted domains */
597
598	for (domain = domain_list(); domain; domain = domain->next) {
599		NTSTATUS status;
600		struct winbindd_methods *methods;
601		unsigned int i;
602
603		/* if we have a domain name restricting the request and this
604		   one in the list doesn't match, then just bypass the remainder
605		   of the loop */
606
607		if ( *which_domain && !strequal(which_domain, domain->name) )
608			continue;
609
610		methods = domain->methods;
611
612		/* Query display info */
613		status = methods->query_user_list(domain, mem_ctx,
614						  &num_entries, &info);
615
616		if (num_entries == 0)
617			continue;
618
619		/* Allocate some memory for extra data */
620		total_entries += num_entries;
621
622		ted = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
623
624		if (!ted) {
625			DEBUG(0,("failed to enlarge buffer!\n"));
626			SAFE_FREE(extra_data);
627			goto done;
628		} else
629			extra_data = ted;
630
631		/* Pack user list into extra data fields */
632
633		for (i = 0; i < num_entries; i++) {
634			fstring acct_name, name;
635
636			if (!info[i].acct_name) {
637				fstrcpy(acct_name, "");
638			} else {
639				fstrcpy(acct_name, info[i].acct_name);
640			}
641
642			fill_domain_username(name, domain->name, acct_name);
643
644				/* Append to extra data */
645			memcpy(&extra_data[extra_data_len], name,
646			       strlen(name));
647			extra_data_len += strlen(name);
648			extra_data[extra_data_len++] = ',';
649		}
650        }
651
652	/* Assign extra_data fields in response structure */
653
654	if (extra_data) {
655		extra_data[extra_data_len - 1] = '\0';
656		state->response.extra_data = extra_data;
657		state->response.length += extra_data_len;
658	}
659
660	/* No domains responded but that's still OK so don't return an
661	   error. */
662
663	rv = WINBINDD_OK;
664
665 done:
666
667	talloc_destroy(mem_ctx);
668
669	return rv;
670}
671