1/*	$NetBSD: auth.c,v 1.4 2011/07/25 03:03:10 christos Exp $	*/
2/* $OpenBSD: auth.c,v 1.94 2011/05/23 03:33:38 djm Exp $ */
3/*
4 * Copyright (c) 2000 Markus Friedl.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "includes.h"
28__RCSID("$NetBSD: auth.c,v 1.4 2011/07/25 03:03:10 christos Exp $");
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <sys/param.h>
32
33#include <errno.h>
34#include <fcntl.h>
35#include <libgen.h>
36#include <login_cap.h>
37#include <paths.h>
38#include <pwd.h>
39#include <stdarg.h>
40#include <stdio.h>
41#include <string.h>
42#include <unistd.h>
43
44#include "xmalloc.h"
45#include "match.h"
46#include "groupaccess.h"
47#include "log.h"
48#include "buffer.h"
49#include "servconf.h"
50#include "key.h"
51#include "hostfile.h"
52#include "auth.h"
53#include "auth-options.h"
54#include "canohost.h"
55#include "uidswap.h"
56#include "misc.h"
57#include "packet.h"
58#ifdef GSSAPI
59#include "ssh-gss.h"
60#endif
61#include "authfile.h"
62#include "monitor_wrap.h"
63
64#ifdef HAVE_LOGIN_CAP
65#include <login_cap.h>
66#endif
67
68/* import */
69extern ServerOptions options;
70extern int use_privsep;
71
72/* Debugging messages */
73Buffer auth_debug;
74int auth_debug_init;
75
76/*
77 * Check if the user is allowed to log in via ssh. If user is listed
78 * in DenyUsers or one of user's groups is listed in DenyGroups, false
79 * will be returned. If AllowUsers isn't empty and user isn't listed
80 * there, or if AllowGroups isn't empty and one of user's groups isn't
81 * listed there, false will be returned.
82 * If the user's shell is not executable, false will be returned.
83 * Otherwise true is returned.
84 */
85int
86allowed_user(struct passwd * pw)
87{
88#ifdef HAVE_LOGIN_CAP
89	extern login_cap_t *lc;
90	int match_name, match_ip;
91	char *cap_hlist, *hp;
92#endif
93	struct stat st;
94	const char *hostname = NULL, *ipaddr = NULL;
95	u_int i;
96
97	/* Shouldn't be called if pw is NULL, but better safe than sorry... */
98	if (!pw || !pw->pw_name)
99		return 0;
100
101#ifdef HAVE_LOGIN_CAP
102	hostname = get_canonical_hostname(1);
103	ipaddr = get_remote_ipaddr();
104
105	lc = login_getclass(pw->pw_class);
106
107	/*
108	 * Check the deny list.
109	 */
110	cap_hlist = login_getcapstr(lc, "host.deny", NULL, NULL);
111	if (cap_hlist != NULL) {
112		hp = strtok(cap_hlist, ",");
113		while (hp != NULL) {
114			match_name = match_hostname(hostname,
115			    hp, strlen(hp));
116			match_ip = match_hostname(ipaddr,
117			    hp, strlen(hp));
118			/*
119			 * Only a positive match here causes a "deny".
120			 */
121			if (match_name > 0 || match_ip > 0) {
122				free(cap_hlist);
123				login_close(lc);
124				return 0;
125			}
126			hp = strtok(NULL, ",");
127		}
128		free(cap_hlist);
129	}
130
131	/*
132	 * Check the allow list.  If the allow list exists, and the
133	 * remote host is not in it, the user is implicitly denied.
134	 */
135	cap_hlist = login_getcapstr(lc, "host.allow", NULL, NULL);
136	if (cap_hlist != NULL) {
137		hp = strtok(cap_hlist, ",");
138		if (hp == NULL) {
139			/* Just in case there's an empty string... */
140			free(cap_hlist);
141			login_close(lc);
142			return 0;
143		}
144		while (hp != NULL) {
145			match_name = match_hostname(hostname,
146			    hp, strlen(hp));
147			match_ip = match_hostname(ipaddr,
148			    hp, strlen(hp));
149			/*
150			 * Negative match causes an immediate "deny".
151			 * Positive match causes us to break out
152			 * of the loop (allowing a fallthrough).
153			 */
154			if (match_name < 0 || match_ip < 0) {
155				free(cap_hlist);
156				login_close(lc);
157				return 0;
158			}
159			if (match_name > 0 || match_ip > 0)
160				break;
161			hp = strtok(NULL, ",");
162		}
163		free(cap_hlist);
164		if (hp == NULL) {
165			login_close(lc);
166			return 0;
167		}
168	}
169
170	login_close(lc);
171#endif
172
173#ifdef USE_PAM
174	if (!options.use_pam) {
175#endif
176	/*
177	 * password/account expiration.
178	 */
179	if (pw->pw_change || pw->pw_expire) {
180		struct timeval tv;
181
182		(void)gettimeofday(&tv, (struct timezone *)NULL);
183		if (pw->pw_expire) {
184			if (tv.tv_sec >= pw->pw_expire) {
185				logit("User %.100s not allowed because account has expired",
186				    pw->pw_name);
187				return 0;	/* expired */
188			}
189		}
190#ifdef _PASSWORD_CHGNOW
191		if (pw->pw_change == _PASSWORD_CHGNOW) {
192			logit("User %.100s not allowed because password needs to be changed",
193			    pw->pw_name);
194
195			return 0;	/* can't force password change (yet) */
196		}
197#endif
198		if (pw->pw_change) {
199			if (tv.tv_sec >= pw->pw_change) {
200				logit("User %.100s not allowed because password has expired",
201				    pw->pw_name);
202				return 0;	/* expired */
203			}
204		}
205	}
206#ifdef USE_PAM
207	}
208#endif
209
210	/*
211	 * Deny if shell does not exist or is not executable unless we
212	 * are chrooting.
213	 */
214	/*
215	 * XXX Should check to see if it is executable by the
216	 * XXX requesting user.  --thorpej
217	 */
218	if (options.chroot_directory == NULL ||
219	    strcasecmp(options.chroot_directory, "none") == 0) {
220		char *shell = xstrdup((pw->pw_shell[0] == '\0') ?
221		    _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */
222
223		if (stat(shell, &st) != 0) {
224			logit("User %.100s not allowed because shell %.100s "
225			    "does not exist", pw->pw_name, shell);
226			xfree(shell);
227			return 0;
228		}
229		if (S_ISREG(st.st_mode) == 0 ||
230		    (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
231			logit("User %.100s not allowed because shell %.100s "
232			    "is not executable", pw->pw_name, shell);
233			xfree(shell);
234			return 0;
235		}
236		xfree(shell);
237	}
238	/*
239	 * XXX Consider nuking {Allow,Deny}{Users,Groups}.  We have the
240	 * XXX login_cap(3) mechanism which covers all other types of
241	 * XXX logins, too.
242	 */
243
244	if (options.num_deny_users > 0 || options.num_allow_users > 0 ||
245	    options.num_deny_groups > 0 || options.num_allow_groups > 0) {
246		hostname = get_canonical_hostname(options.use_dns);
247		ipaddr = get_remote_ipaddr();
248	}
249
250	/* Return false if user is listed in DenyUsers */
251	if (options.num_deny_users > 0) {
252		for (i = 0; i < options.num_deny_users; i++)
253			if (match_user(pw->pw_name, hostname, ipaddr,
254			    options.deny_users[i])) {
255				logit("User %.100s from %.100s not allowed "
256				    "because listed in DenyUsers",
257				    pw->pw_name, hostname);
258				return 0;
259			}
260	}
261	/* Return false if AllowUsers isn't empty and user isn't listed there */
262	if (options.num_allow_users > 0) {
263		for (i = 0; i < options.num_allow_users; i++)
264			if (match_user(pw->pw_name, hostname, ipaddr,
265			    options.allow_users[i]))
266				break;
267		/* i < options.num_allow_users iff we break for loop */
268		if (i >= options.num_allow_users) {
269			logit("User %.100s from %.100s not allowed because "
270			    "not listed in AllowUsers", pw->pw_name, hostname);
271			return 0;
272		}
273	}
274	if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
275		/* Get the user's group access list (primary and supplementary) */
276		if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
277			logit("User %.100s from %.100s not allowed because "
278			    "not in any group", pw->pw_name, hostname);
279			return 0;
280		}
281
282		/* Return false if one of user's groups is listed in DenyGroups */
283		if (options.num_deny_groups > 0)
284			if (ga_match(options.deny_groups,
285			    options.num_deny_groups)) {
286				ga_free();
287				logit("User %.100s from %.100s not allowed "
288				    "because a group is listed in DenyGroups",
289				    pw->pw_name, hostname);
290				return 0;
291			}
292		/*
293		 * Return false if AllowGroups isn't empty and one of user's groups
294		 * isn't listed there
295		 */
296		if (options.num_allow_groups > 0)
297			if (!ga_match(options.allow_groups,
298			    options.num_allow_groups)) {
299				ga_free();
300				logit("User %.100s from %.100s not allowed "
301				    "because none of user's groups are listed "
302				    "in AllowGroups", pw->pw_name, hostname);
303				return 0;
304			}
305		ga_free();
306	}
307	/* We found no reason not to let this user try to log on... */
308	return 1;
309}
310
311void
312auth_log(Authctxt *authctxt, int authenticated, const char *method,
313    const char *info)
314{
315	void (*authlog) (const char *fmt,...) = verbose;
316	const char *authmsg;
317
318	if (use_privsep && !mm_is_monitor() && !authctxt->postponed)
319		return;
320
321	/* Raise logging level */
322	if (authenticated == 1 ||
323	    !authctxt->valid ||
324	    authctxt->failures >= options.max_authtries / 2 ||
325	    strcmp(method, "password") == 0)
326		authlog = logit;
327
328	if (authctxt->postponed)
329		authmsg = "Postponed";
330	else
331		authmsg = authenticated ? "Accepted" : "Failed";
332
333	authlog("%s %s for %s%.100s from %.200s port %d%s",
334	    authmsg,
335	    method,
336	    authctxt->valid ? "" : "invalid user ",
337	    authctxt->user,
338	    get_remote_ipaddr(),
339	    get_remote_port(),
340	    info);
341}
342
343/*
344 * Check whether root logins are disallowed.
345 */
346int
347auth_root_allowed(const char *method)
348{
349	switch (options.permit_root_login) {
350	case PERMIT_YES:
351		return 1;
352	case PERMIT_NO_PASSWD:
353		if (strcmp(method, "password") != 0)
354			return 1;
355		break;
356	case PERMIT_FORCED_ONLY:
357		if (forced_command) {
358			logit("Root login accepted for forced command.");
359			return 1;
360		}
361		break;
362	}
363	logit("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr());
364	return 0;
365}
366
367
368/*
369 * Given a template and a passwd structure, build a filename
370 * by substituting % tokenised options. Currently, %% becomes '%',
371 * %h becomes the home directory and %u the username.
372 *
373 * This returns a buffer allocated by xmalloc.
374 */
375char *
376expand_authorized_keys(const char *filename, struct passwd *pw)
377{
378	char *file, ret[MAXPATHLEN];
379	int i;
380
381	file = percent_expand(filename, "h", pw->pw_dir,
382	    "u", pw->pw_name, (char *)NULL);
383
384	/*
385	 * Ensure that filename starts anchored. If not, be backward
386	 * compatible and prepend the '%h/'
387	 */
388	if (*file == '/')
389		return (file);
390
391	i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file);
392	if (i < 0 || (size_t)i >= sizeof(ret))
393		fatal("expand_authorized_keys: path too long");
394	xfree(file);
395	return (xstrdup(ret));
396}
397
398char *
399authorized_principals_file(struct passwd *pw)
400{
401	if (options.authorized_principals_file == NULL)
402		return NULL;
403	return expand_authorized_keys(options.authorized_principals_file, pw);
404}
405
406/* return ok if key exists in sysfile or userfile */
407HostStatus
408check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
409    const char *sysfile, const char *userfile)
410{
411	char *user_hostfile;
412	struct stat st;
413	HostStatus host_status;
414	struct hostkeys *hostkeys;
415	const struct hostkey_entry *found;
416
417	hostkeys = init_hostkeys();
418	load_hostkeys(hostkeys, host, sysfile);
419	if (userfile != NULL) {
420		user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
421		if (options.strict_modes &&
422		    (stat(user_hostfile, &st) == 0) &&
423		    ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
424		    (st.st_mode & 022) != 0)) {
425			logit("Authentication refused for %.100s: "
426			    "bad owner or modes for %.200s",
427			    pw->pw_name, user_hostfile);
428			auth_debug_add("Ignored %.200s: bad ownership or modes",
429			    user_hostfile);
430		} else {
431			temporarily_use_uid(pw);
432			load_hostkeys(hostkeys, host, user_hostfile);
433			restore_uid();
434		}
435		xfree(user_hostfile);
436	}
437	host_status = check_key_in_hostkeys(hostkeys, key, &found);
438	if (host_status == HOST_REVOKED)
439		error("WARNING: revoked key for %s attempted authentication",
440		    found->host);
441	else if (host_status == HOST_OK)
442		debug("%s: key for %s found at %s:%ld", __func__,
443		    found->host, found->file, found->line);
444	else
445		debug("%s: key for host %s not found", __func__, host);
446
447	free_hostkeys(hostkeys);
448
449	return host_status;
450}
451
452
453/*
454 * Check a given file for security. This is defined as all components
455 * of the path to the file must be owned by either the owner of
456 * of the file or root and no directories must be group or world writable.
457 *
458 * XXX Should any specific check be done for sym links ?
459 *
460 * Takes an open file descriptor, the file name, a uid and and
461 * error buffer plus max size as arguments.
462 *
463 * Returns 0 on success and -1 on failure
464 */
465static int
466secure_filename(FILE *f, const char *file, struct passwd *pw,
467    char *err, size_t errlen)
468{
469	uid_t uid = pw->pw_uid;
470	char buf[MAXPATHLEN], homedir[MAXPATHLEN];
471	char *cp;
472	int comparehome = 0;
473	struct stat st;
474
475	if (realpath(file, buf) == NULL) {
476		snprintf(err, errlen, "realpath %s failed: %s", file,
477		    strerror(errno));
478		return -1;
479	}
480	if (realpath(pw->pw_dir, homedir) != NULL)
481		comparehome = 1;
482
483	/* check the open file to avoid races */
484	if (fstat(fileno(f), &st) < 0 ||
485	    (st.st_uid != 0 && st.st_uid != uid) ||
486	    (st.st_mode & 022) != 0) {
487		snprintf(err, errlen, "bad ownership or modes for file %s",
488		    buf);
489		return -1;
490	}
491
492	/* for each component of the canonical path, walking upwards */
493	for (;;) {
494		if ((cp = dirname(buf)) == NULL) {
495			snprintf(err, errlen, "dirname() failed");
496			return -1;
497		}
498		strlcpy(buf, cp, sizeof(buf));
499
500		if (stat(buf, &st) < 0 ||
501		    (st.st_uid != 0 && st.st_uid != uid) ||
502		    (st.st_mode & 022) != 0) {
503			snprintf(err, errlen,
504			    "bad ownership or modes for directory %s", buf);
505			return -1;
506		}
507
508		/* If are past the homedir then we can stop */
509		if (comparehome && strcmp(homedir, buf) == 0)
510			break;
511
512		/*
513		 * dirname should always complete with a "/" path,
514		 * but we can be paranoid and check for "." too
515		 */
516		if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
517			break;
518	}
519	return 0;
520}
521
522static FILE *
523auth_openfile(const char *file, struct passwd *pw, int strict_modes,
524    int log_missing, const char *file_type)
525{
526	char line[1024];
527	struct stat st;
528	int fd;
529	FILE *f;
530
531	if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
532		if (log_missing || errno != ENOENT)
533			debug("Could not open %s '%s': %s", file_type, file,
534			   strerror(errno));
535		return NULL;
536	}
537
538	if (fstat(fd, &st) < 0) {
539		close(fd);
540		return NULL;
541	}
542	if (!S_ISREG(st.st_mode)) {
543		logit("User %s %s %s is not a regular file",
544		    pw->pw_name, file_type, file);
545		close(fd);
546		return NULL;
547	}
548	unset_nonblock(fd);
549	if ((f = fdopen(fd, "r")) == NULL) {
550		close(fd);
551		return NULL;
552	}
553	if (strict_modes &&
554	    secure_filename(f, file, pw, line, sizeof(line)) != 0) {
555		fclose(f);
556		logit("Authentication refused: %s", line);
557		auth_debug_add("Ignored %s: %s", file_type, line);
558		return NULL;
559	}
560
561	return f;
562}
563
564
565FILE *
566auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
567{
568	return auth_openfile(file, pw, strict_modes, 1, "authorized keys");
569}
570
571FILE *
572auth_openprincipals(const char *file, struct passwd *pw, int strict_modes)
573{
574	return auth_openfile(file, pw, strict_modes, 0,
575	    "authorized principals");
576}
577
578struct passwd *
579getpwnamallow(const char *user)
580{
581#ifdef HAVE_LOGIN_CAP
582 	extern login_cap_t *lc;
583#ifdef BSD_AUTH
584 	auth_session_t *as;
585#endif
586#endif
587	struct passwd *pw;
588
589	parse_server_match_config(&options, user,
590	    get_canonical_hostname(options.use_dns), get_remote_ipaddr());
591
592	pw = getpwnam(user);
593	if (pw == NULL) {
594		logit("Invalid user %.100s from %.100s",
595		    user, get_remote_ipaddr());
596		return (NULL);
597	}
598	if (!allowed_user(pw))
599		return (NULL);
600#ifdef HAVE_LOGIN_CAP
601	if ((lc = login_getclass(pw->pw_class)) == NULL) {
602		debug("unable to get login class: %s", user);
603		return (NULL);
604	}
605#ifdef BSD_AUTH
606	if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 ||
607	    auth_approval(as, lc, pw->pw_name, "ssh") <= 0) {
608		debug("Approval failure for %s", user);
609		pw = NULL;
610	}
611	if (as != NULL)
612		auth_close(as);
613#endif
614#endif
615	if (pw != NULL)
616		return (pwcopy(pw));
617	return (NULL);
618}
619
620/* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */
621int
622auth_key_is_revoked(Key *key)
623{
624	char *key_fp;
625
626	if (options.revoked_keys_file == NULL)
627		return 0;
628
629	switch (key_in_file(key, options.revoked_keys_file, 0)) {
630	case 0:
631		/* key not revoked */
632		return 0;
633	case -1:
634		/* Error opening revoked_keys_file: refuse all keys */
635		error("Revoked keys file is unreadable: refusing public key "
636		    "authentication");
637		return 1;
638	case 1:
639		/* Key revoked */
640		key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
641		error("WARNING: authentication attempt with a revoked "
642		    "%s key %s ", key_type(key), key_fp);
643		xfree(key_fp);
644		return 1;
645	}
646	fatal("key_in_file returned junk");
647}
648
649void
650auth_debug_add(const char *fmt,...)
651{
652	char buf[1024];
653	va_list args;
654
655	if (!auth_debug_init)
656		return;
657
658	va_start(args, fmt);
659	vsnprintf(buf, sizeof(buf), fmt, args);
660	va_end(args);
661	buffer_put_cstring(&auth_debug, buf);
662}
663
664void
665auth_debug_send(void)
666{
667	char *msg;
668
669	if (!auth_debug_init)
670		return;
671	while (buffer_len(&auth_debug)) {
672		msg = buffer_get_string(&auth_debug, NULL);
673		packet_send_debug("%s", msg);
674		xfree(msg);
675	}
676}
677
678void
679auth_debug_reset(void)
680{
681	if (auth_debug_init)
682		buffer_clear(&auth_debug);
683	else {
684		buffer_init(&auth_debug);
685		auth_debug_init = 1;
686	}
687}
688
689struct passwd *
690fakepw(void)
691{
692	static struct passwd fake;
693	static char nouser[] = "NOUSER";
694	static char nonexist[] = "/nonexist";
695
696	memset(&fake, 0, sizeof(fake));
697	fake.pw_name = nouser;
698	fake.pw_passwd = __UNCONST(
699	    "$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK");
700	fake.pw_gecos = nouser;
701	fake.pw_uid = (uid_t)-1;
702	fake.pw_gid = (gid_t)-1;
703	fake.pw_class = __UNCONST("");
704	fake.pw_dir = nonexist;
705	fake.pw_shell = nonexist;
706
707	return (&fake);
708}
709