auth.c revision 248619
1184610Salfred/* $OpenBSD: auth.c,v 1.101 2013/02/06 00:22:21 dtucker Exp $ */
2184610Salfred/*
3184610Salfred * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4184610Salfred *
5184610Salfred * Redistribution and use in source and binary forms, with or without
6184610Salfred * modification, are permitted provided that the following conditions
7184610Salfred * are met:
8184610Salfred * 1. Redistributions of source code must retain the above copyright
9184610Salfred *    notice, this list of conditions and the following disclaimer.
10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
11184610Salfred *    notice, this list of conditions and the following disclaimer in the
12184610Salfred *    documentation and/or other materials provided with the distribution.
13184610Salfred *
14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15184610Salfred * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16184610Salfred * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17184610Salfred * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18184610Salfred * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19184610Salfred * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20184610Salfred * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21184610Salfred * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22184610Salfred * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23184610Salfred * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24184610Salfred */
25184610Salfred
26184610Salfred#include "includes.h"
27184610Salfred__RCSID("$FreeBSD: head/crypto/openssh/auth.c 248619 2013-03-22 17:55:38Z des $");
28184610Salfred
29184610Salfred#include <sys/types.h>
30184610Salfred#include <sys/stat.h>
31184610Salfred#include <sys/param.h>
32184610Salfred
33184610Salfred#include <netinet/in.h>
34184610Salfred
35184610Salfred#include <errno.h>
36184610Salfred#include <fcntl.h>
37184610Salfred#ifdef HAVE_PATHS_H
38194677Sthompsa# include <paths.h>
39194677Sthompsa#endif
40194677Sthompsa#include <pwd.h>
41194677Sthompsa#ifdef HAVE_LOGIN_H
42194677Sthompsa#include <login.h>
43194677Sthompsa#endif
44194677Sthompsa#ifdef USE_SHADOW
45194677Sthompsa#include <shadow.h>
46194677Sthompsa#endif
47194677Sthompsa#ifdef HAVE_LIBGEN_H
48194677Sthompsa#include <libgen.h>
49194677Sthompsa#endif
50194677Sthompsa#include <stdarg.h>
51194677Sthompsa#include <stdio.h>
52194677Sthompsa#include <string.h>
53194677Sthompsa#include <unistd.h>
54194677Sthompsa
55194677Sthompsa#include "xmalloc.h"
56194677Sthompsa#include "match.h"
57194677Sthompsa#include "groupaccess.h"
58198373Sthompsa#include "log.h"
59194677Sthompsa#include "buffer.h"
60188942Sthompsa#include "servconf.h"
61194677Sthompsa#include "key.h"
62194677Sthompsa#include "hostfile.h"
63188942Sthompsa#include "auth.h"
64194677Sthompsa#include "auth-options.h"
65184610Salfred#include "canohost.h"
66184610Salfred#include "uidswap.h"
67188942Sthompsa#include "misc.h"
68184610Salfred#include "packet.h"
69188942Sthompsa#include "loginrec.h"
70184610Salfred#ifdef GSSAPI
71184610Salfred#include "ssh-gss.h"
72184610Salfred#endif
73184610Salfred#include "authfile.h"
74184610Salfred#include "monitor_wrap.h"
75184610Salfred#include "krl.h"
76207077Sthompsa
77184610Salfred/* import */
78184610Salfredextern ServerOptions options;
79227309Sedextern int use_privsep;
80192502Sthompsaextern Buffer loginmsg;
81184610Salfredextern struct passwd *privsep_pw;
82184610Salfred
83184610Salfred/* Debugging messages */
84184610SalfredBuffer auth_debug;
85184610Salfredint auth_debug_init;
86184610Salfred
87184610Salfred/*
88184610Salfred * Check if the user is allowed to log in via ssh. If user is listed
89184610Salfred * in DenyUsers or one of user's groups is listed in DenyGroups, false
90184610Salfred * will be returned. If AllowUsers isn't empty and user isn't listed
91190741Sthompsa * there, or if AllowGroups isn't empty and one of user's groups isn't
92184610Salfred * listed there, false will be returned.
93187259Sthompsa * If the user's shell is not executable, false will be returned.
94187259Sthompsa * Otherwise true is returned.
95188981Sthompsa */
96187259Sthompsaint
97187259Sthompsaallowed_user(struct passwd * pw)
98190741Sthompsa{
99184610Salfred	struct stat st;
100184610Salfred	const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL;
101184610Salfred	u_int i;
102184610Salfred#ifdef USE_SHADOW
103184610Salfred	struct spwd *spw = NULL;
104184610Salfred#endif
105184610Salfred
106184610Salfred	/* Shouldn't be called if pw is NULL, but better safe than sorry... */
107184610Salfred	if (!pw || !pw->pw_name)
108184610Salfred		return 0;
109184610Salfred
110184610Salfred#ifdef USE_SHADOW
111184610Salfred	if (!options.use_pam)
112188981Sthompsa		spw = getspnam(pw->pw_name);
113188981Sthompsa#ifdef HAS_SHADOW_EXPIRE
114184610Salfred	if (!options.use_pam && spw != NULL && auth_shadow_acctexpired(spw))
115189583Sthompsa		return 0;
116189583Sthompsa#endif /* HAS_SHADOW_EXPIRE */
117189583Sthompsa#endif /* USE_SHADOW */
118189583Sthompsa
119189583Sthompsa	/* grab passwd field for locked account check */
120189583Sthompsa	passwd = pw->pw_passwd;
121190741Sthompsa#ifdef USE_SHADOW
122190741Sthompsa	if (spw != NULL)
123190741Sthompsa#ifdef USE_LIBIAF
124190741Sthompsa		passwd = get_iaf_password(pw);
125192984Sthompsa#else
126190741Sthompsa		passwd = spw->sp_pwdp;
127192984Sthompsa#endif /* USE_LIBIAF */
128190741Sthompsa#endif
129190741Sthompsa
130190741Sthompsa	/* check for locked account */
131190741Sthompsa	if (!options.use_pam && passwd && *passwd) {
132190741Sthompsa		int locked = 0;
133190741Sthompsa
134192984Sthompsa#ifdef LOCKED_PASSWD_STRING
135190741Sthompsa		if (strcmp(passwd, LOCKED_PASSWD_STRING) == 0)
136195959Salfred			 locked = 1;
137235451Shselasky#endif
138195959Salfred#ifdef LOCKED_PASSWD_PREFIX
139190741Sthompsa		if (strncmp(passwd, LOCKED_PASSWD_PREFIX,
140190741Sthompsa		    strlen(LOCKED_PASSWD_PREFIX)) == 0)
141184610Salfred			 locked = 1;
142184610Salfred#endif
143184610Salfred#ifdef LOCKED_PASSWD_SUBSTR
144184610Salfred		if (strstr(passwd, LOCKED_PASSWD_SUBSTR))
145184610Salfred			locked = 1;
146193045Sthompsa#endif
147184610Salfred#ifdef USE_LIBIAF
148184610Salfred		free((void *) passwd);
149184610Salfred#endif /* USE_LIBIAF */
150184610Salfred		if (locked) {
151184610Salfred			logit("User %.100s not allowed because account is locked",
152193045Sthompsa			    pw->pw_name);
153193045Sthompsa			return 0;
154193045Sthompsa		}
155193045Sthompsa	}
156193045Sthompsa
157184610Salfred	/*
158198373Sthompsa	 * Deny if shell does not exist or is not executable unless we
159198373Sthompsa	 * are chrooting.
160198373Sthompsa	 */
161184610Salfred	if (options.chroot_directory == NULL ||
162192984Sthompsa	    strcasecmp(options.chroot_directory, "none") == 0) {
163184610Salfred		char *shell = xstrdup((pw->pw_shell[0] == '\0') ?
164184610Salfred		    _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */
165184610Salfred
166184610Salfred		if (stat(shell, &st) != 0) {
167184610Salfred			logit("User %.100s not allowed because shell %.100s "
168184610Salfred			    "does not exist", pw->pw_name, shell);
169184610Salfred			xfree(shell);
170184610Salfred			return 0;
171184610Salfred		}
172184610Salfred		if (S_ISREG(st.st_mode) == 0 ||
173184610Salfred		    (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
174184610Salfred			logit("User %.100s not allowed because shell %.100s "
175184610Salfred			    "is not executable", pw->pw_name, shell);
176184610Salfred			xfree(shell);
177184610Salfred			return 0;
178184610Salfred		}
179184610Salfred		xfree(shell);
180184610Salfred	}
181184610Salfred
182194677Sthompsa	if (options.num_deny_users > 0 || options.num_allow_users > 0 ||
183184610Salfred	    options.num_deny_groups > 0 || options.num_allow_groups > 0) {
184194677Sthompsa		hostname = get_canonical_hostname(options.use_dns);
185190741Sthompsa		ipaddr = get_remote_ipaddr();
186194677Sthompsa	}
187184610Salfred
188184610Salfred	/* Return false if user is listed in DenyUsers */
189195959Salfred	if (options.num_deny_users > 0) {
190190741Sthompsa		for (i = 0; i < options.num_deny_users; i++)
191190741Sthompsa			if (match_user(pw->pw_name, hostname, ipaddr,
192190741Sthompsa			    options.deny_users[i])) {
193190741Sthompsa				logit("User %.100s from %.100s not allowed "
194190741Sthompsa				    "because listed in DenyUsers",
195184610Salfred				    pw->pw_name, hostname);
196189583Sthompsa				return 0;
197194677Sthompsa			}
198184610Salfred	}
199194677Sthompsa	/* Return false if AllowUsers isn't empty and user isn't listed there */
200194677Sthompsa	if (options.num_allow_users > 0) {
201184610Salfred		for (i = 0; i < options.num_allow_users; i++)
202184610Salfred			if (match_user(pw->pw_name, hostname, ipaddr,
203184610Salfred			    options.allow_users[i]))
204184610Salfred				break;
205233774Shselasky		/* i < options.num_allow_users iff we break for loop */
206184610Salfred		if (i >= options.num_allow_users) {
207184610Salfred			logit("User %.100s from %.100s not allowed because "
208184610Salfred			    "not listed in AllowUsers", pw->pw_name, hostname);
209184610Salfred			return 0;
210188981Sthompsa		}
211184610Salfred	}
212188981Sthompsa	if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
213194677Sthompsa		/* Get the user's group access list (primary and supplementary) */
214194677Sthompsa		if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
215184610Salfred			logit("User %.100s from %.100s not allowed because "
216184610Salfred			    "not in any group", pw->pw_name, hostname);
217184610Salfred			return 0;
218184610Salfred		}
219184610Salfred
220184610Salfred		/* Return false if one of user's groups is listed in DenyGroups */
221184610Salfred		if (options.num_deny_groups > 0)
222184610Salfred			if (ga_match(options.deny_groups,
223184610Salfred			    options.num_deny_groups)) {
224189583Sthompsa				ga_free();
225184610Salfred				logit("User %.100s from %.100s not allowed "
226184610Salfred				    "because a group is listed in DenyGroups",
227184610Salfred				    pw->pw_name, hostname);
228184610Salfred				return 0;
229184610Salfred			}
230189583Sthompsa		/*
231190741Sthompsa		 * Return false if AllowGroups isn't empty and one of user's groups
232184610Salfred		 * isn't listed there
233184610Salfred		 */
234184610Salfred		if (options.num_allow_groups > 0)
235184610Salfred			if (!ga_match(options.allow_groups,
236184610Salfred			    options.num_allow_groups)) {
237184610Salfred				ga_free();
238190741Sthompsa				logit("User %.100s from %.100s not allowed "
239190741Sthompsa				    "because none of user's groups are listed "
240190741Sthompsa				    "in AllowGroups", pw->pw_name, hostname);
241190741Sthompsa				return 0;
242184610Salfred			}
243190741Sthompsa		ga_free();
244190741Sthompsa	}
245190741Sthompsa
246184610Salfred#ifdef CUSTOM_SYS_AUTH_ALLOWED_USER
247190741Sthompsa	if (!sys_auth_allowed_user(pw, &loginmsg))
248190741Sthompsa		return 0;
249190741Sthompsa#endif
250184610Salfred
251190741Sthompsa	/* We found no reason not to let this user try to log on... */
252190741Sthompsa	return 1;
253190741Sthompsa}
254190741Sthompsa
255190741Sthompsavoid
256190741Sthompsaauth_log(Authctxt *authctxt, int authenticated, int partial,
257190741Sthompsa    const char *method, const char *submethod, const char *info)
258190741Sthompsa{
259184610Salfred	void (*authlog) (const char *fmt,...) = verbose;
260190741Sthompsa	char *authmsg;
261190741Sthompsa
262190741Sthompsa	if (use_privsep && !mm_is_monitor() && !authctxt->postponed)
263184610Salfred		return;
264190741Sthompsa
265195959Salfred	/* Raise logging level */
266195959Salfred	if (authenticated == 1 ||
267195959Salfred	    !authctxt->valid ||
268190741Sthompsa	    authctxt->failures >= options.max_authtries / 2 ||
269189583Sthompsa	    strcmp(method, "password") == 0)
270195959Salfred		authlog = logit;
271195959Salfred
272195959Salfred	if (authctxt->postponed)
273195959Salfred		authmsg = "Postponed";
274195959Salfred	else if (partial)
275184610Salfred		authmsg = "Partial";
276184610Salfred	else
277190741Sthompsa		authmsg = authenticated ? "Accepted" : "Failed";
278190741Sthompsa
279190741Sthompsa	authlog("%s %s%s%s for %s%.100s from %.200s port %d%s",
280195959Salfred	    authmsg,
281195959Salfred	    method,
282195959Salfred	    submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod,
283184610Salfred	    authctxt->valid ? "" : "invalid user ",
284184610Salfred	    authctxt->user,
285184610Salfred	    get_remote_ipaddr(),
286184610Salfred	    get_remote_port(),
287184610Salfred	    info);
288184610Salfred
289208009Sthompsa#ifdef CUSTOM_FAILED_LOGIN
290208009Sthompsa	if (authenticated == 0 && !authctxt->postponed &&
291208009Sthompsa	    (strcmp(method, "password") == 0 ||
292208009Sthompsa	    strncmp(method, "keyboard-interactive", 20) == 0 ||
293208009Sthompsa	    strcmp(method, "challenge-response") == 0))
294208009Sthompsa		record_failed_login(authctxt->user,
295184610Salfred		    get_canonical_hostname(options.use_dns), "ssh");
296184610Salfred# ifdef WITH_AIXAUTHENTICATE
297184610Salfred	if (authenticated)
298184610Salfred		sys_auth_record_login(authctxt->user,
299184610Salfred		    get_canonical_hostname(options.use_dns), "ssh", &loginmsg);
300184610Salfred# endif
301184610Salfred#endif
302184610Salfred#ifdef SSH_AUDIT_EVENTS
303184610Salfred	if (authenticated == 0 && !authctxt->postponed)
304184610Salfred		audit_event(audit_classify_auth(method));
305188981Sthompsa#endif
306188981Sthompsa}
307188981Sthompsa
308188981Sthompsa/*
309188981Sthompsa * Check whether root logins are disallowed.
310188981Sthompsa */
311188981Sthompsaint
312188981Sthompsaauth_root_allowed(const char *method)
313184610Salfred{
314190741Sthompsa	switch (options.permit_root_login) {
315184610Salfred	case PERMIT_YES:
316184610Salfred		return 1;
317184610Salfred	case PERMIT_NO_PASSWD:
318194228Sthompsa		if (strcmp(method, "password") != 0)
319184610Salfred			return 1;
320184610Salfred		break;
321184610Salfred	case PERMIT_FORCED_ONLY:
322194228Sthompsa		if (forced_command) {
323184610Salfred			logit("Root login accepted for forced command.");
324184610Salfred			return 1;
325184610Salfred		}
326184610Salfred		break;
327184610Salfred	}
328184610Salfred	logit("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr());
329188981Sthompsa	return 0;
330194228Sthompsa}
331188981Sthompsa
332194677Sthompsa
333194228Sthompsa/*
334184610Salfred * Given a template and a passwd structure, build a filename
335188981Sthompsa * by substituting % tokenised options. Currently, %% becomes '%',
336184610Salfred * %h becomes the home directory and %u the username.
337184610Salfred *
338194677Sthompsa * This returns a buffer allocated by xmalloc.
339188981Sthompsa */
340194677Sthompsachar *
341188981Sthompsaexpand_authorized_keys(const char *filename, struct passwd *pw)
342184610Salfred{
343188981Sthompsa	char *file, ret[MAXPATHLEN];
344184610Salfred	int i;
345184610Salfred
346184610Salfred	file = percent_expand(filename, "h", pw->pw_dir,
347192984Sthompsa	    "u", pw->pw_name, (char *)NULL);
348184610Salfred
349187259Sthompsa	/*
350184610Salfred	 * Ensure that filename starts anchored. If not, be backward
351184610Salfred	 * compatible and prepend the '%h/'
352184610Salfred	 */
353190734Sthompsa	if (*file == '/')
354190734Sthompsa		return (file);
355190734Sthompsa
356184610Salfred	i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file);
357184610Salfred	if (i < 0 || (size_t)i >= sizeof(ret))
358184610Salfred		fatal("expand_authorized_keys: path too long");
359223521Shselasky	xfree(file);
360223521Shselasky	return (xstrdup(ret));
361223521Shselasky}
362223521Shselasky
363223521Shselaskychar *
364223521Shselaskyauthorized_principals_file(struct passwd *pw)
365223521Shselasky{
366184610Salfred	if (options.authorized_principals_file == NULL ||
367184610Salfred	    strcasecmp(options.authorized_principals_file, "none") == 0)
368184610Salfred		return NULL;
369192984Sthompsa	return expand_authorized_keys(options.authorized_principals_file, pw);
370184610Salfred}
371245248Shselasky
372184610Salfred/* return ok if key exists in sysfile or userfile */
373184610SalfredHostStatus
374184610Salfredcheck_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
375184610Salfred    const char *sysfile, const char *userfile)
376192499Sthompsa{
377184610Salfred	char *user_hostfile;
378188981Sthompsa	struct stat st;
379194068Sthompsa	HostStatus host_status;
380184610Salfred	struct hostkeys *hostkeys;
381188981Sthompsa	const struct hostkey_entry *found;
382240615Shselasky
383240615Shselasky	hostkeys = init_hostkeys();
384240615Shselasky	load_hostkeys(hostkeys, host, sysfile);
385194068Sthompsa	if (userfile != NULL) {
386194068Sthompsa		user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
387222051Savg		if (options.strict_modes &&
388194068Sthompsa		    (stat(user_hostfile, &st) == 0) &&
389194228Sthompsa		    ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
390184610Salfred		    (st.st_mode & 022) != 0)) {
391184610Salfred			logit("Authentication refused for %.100s: "
392188981Sthompsa			    "bad owner or modes for %.200s",
393184610Salfred			    pw->pw_name, user_hostfile);
394188981Sthompsa			auth_debug_add("Ignored %.200s: bad ownership or modes",
395245248Shselasky			    user_hostfile);
396245248Shselasky		} else {
397245248Shselasky			temporarily_use_uid(pw);
398245248Shselasky			load_hostkeys(hostkeys, host, user_hostfile);
399245248Shselasky			restore_uid();
400184610Salfred		}
401245248Shselasky		xfree(user_hostfile);
402184610Salfred	}
403184610Salfred	host_status = check_key_in_hostkeys(hostkeys, key, &found);
404190741Sthompsa	if (host_status == HOST_REVOKED)
405190741Sthompsa		error("WARNING: revoked key for %s attempted authentication",
406190741Sthompsa		    found->host);
407184610Salfred	else if (host_status == HOST_OK)
408190741Sthompsa		debug("%s: key for %s found at %s:%ld", __func__,
409184610Salfred		    found->host, found->file, found->line);
410184610Salfred	else
411212129Sthompsa		debug("%s: key for host %s not found", __func__, host);
412184610Salfred
413190741Sthompsa	free_hostkeys(hostkeys);
414190741Sthompsa
415184610Salfred	return host_status;
416184610Salfred}
417190741Sthompsa
418184610Salfred/*
419184610Salfred * Check a given path for security. This is defined as all components
420190741Sthompsa * of the path to the file must be owned by either the owner of
421190741Sthompsa * of the file or root and no directories must be group or world writable.
422184610Salfred *
423184610Salfred * XXX Should any specific check be done for sym links ?
424190741Sthompsa *
425184610Salfred * Takes a file name, its stat information (preferably from fstat() to
426184610Salfred * avoid races), the uid of the expected owner, their home directory and an
427184610Salfred * error buffer plus max size as arguments.
428190741Sthompsa *
429190741Sthompsa * Returns 0 on success and -1 on failure
430190741Sthompsa */
431190741Sthompsaint
432190741Sthompsaauth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
433190741Sthompsa    uid_t uid, char *err, size_t errlen)
434184610Salfred{
435190741Sthompsa	char buf[MAXPATHLEN], homedir[MAXPATHLEN];
436184610Salfred	char *cp;
437184610Salfred	int comparehome = 0;
438184610Salfred	struct stat st;
439184610Salfred
440184610Salfred	if (realpath(name, buf) == NULL) {
441190741Sthompsa		snprintf(err, errlen, "realpath %s failed: %s", name,
442190741Sthompsa		    strerror(errno));
443190741Sthompsa		return -1;
444184610Salfred	}
445184610Salfred	if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL)
446190741Sthompsa		comparehome = 1;
447184610Salfred
448184610Salfred	if (!S_ISREG(stp->st_mode)) {
449190741Sthompsa		snprintf(err, errlen, "%s is not a regular file", buf);
450190741Sthompsa		return -1;
451190741Sthompsa	}
452184610Salfred	if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) ||
453184610Salfred	    (stp->st_mode & 022) != 0) {
454190741Sthompsa		snprintf(err, errlen, "bad ownership or modes for file %s",
455184610Salfred		    buf);
456184610Salfred		return -1;
457184610Salfred	}
458184610Salfred
459184610Salfred	/* for each component of the canonical path, walking upwards */
460184610Salfred	for (;;) {
461184610Salfred		if ((cp = dirname(buf)) == NULL) {
462184610Salfred			snprintf(err, errlen, "dirname() failed");
463184610Salfred			return -1;
464190741Sthompsa		}
465190741Sthompsa		strlcpy(buf, cp, sizeof(buf));
466190741Sthompsa
467184610Salfred		if (stat(buf, &st) < 0 ||
468190741Sthompsa		    (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) ||
469184610Salfred		    (st.st_mode & 022) != 0) {
470184610Salfred			snprintf(err, errlen,
471190741Sthompsa			    "bad ownership or modes for directory %s", buf);
472184610Salfred			return -1;
473208009Sthompsa		}
474208009Sthompsa
475208009Sthompsa		/* If are past the homedir then we can stop */
476208009Sthompsa		if (comparehome && strcmp(homedir, buf) == 0)
477208009Sthompsa			break;
478208009Sthompsa
479184610Salfred		/*
480184610Salfred		 * dirname should always complete with a "/" path,
481184610Salfred		 * but we can be paranoid and check for "." too
482184610Salfred		 */
483190741Sthompsa		if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
484190741Sthompsa			break;
485190741Sthompsa	}
486184610Salfred	return 0;
487184610Salfred}
488184610Salfred
489212129Sthompsa/*
490212129Sthompsa * Version of secure_path() that accepts an open file descriptor to
491212129Sthompsa * avoid races.
492212132Sthompsa *
493212129Sthompsa * Returns 0 on success and -1 on failure
494212129Sthompsa */
495212129Sthompsastatic int
496212129Sthompsasecure_filename(FILE *f, const char *file, struct passwd *pw,
497212129Sthompsa    char *err, size_t errlen)
498212129Sthompsa{
499212129Sthompsa	struct stat st;
500190741Sthompsa
501184610Salfred	/* check the open file to avoid races */
502190741Sthompsa	if (fstat(fileno(f), &st) < 0) {
503190741Sthompsa		snprintf(err, errlen, "cannot stat file %s: %s",
504184610Salfred		    file, strerror(errno));
505190741Sthompsa		return -1;
506190741Sthompsa	}
507190741Sthompsa	return auth_secure_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen);
508190741Sthompsa}
509190741Sthompsa
510190741Sthompsastatic FILE *
511190741Sthompsaauth_openfile(const char *file, struct passwd *pw, int strict_modes,
512190741Sthompsa    int log_missing, char *file_type)
513190741Sthompsa{
514190741Sthompsa	char line[1024];
515190741Sthompsa	struct stat st;
516190741Sthompsa	int fd;
517190741Sthompsa	FILE *f;
518190741Sthompsa
519190741Sthompsa	if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
520190741Sthompsa		if (log_missing || errno != ENOENT)
521190741Sthompsa			debug("Could not open %s '%s': %s", file_type, file,
522192984Sthompsa			   strerror(errno));
523190741Sthompsa		return NULL;
524190741Sthompsa	}
525190741Sthompsa
526190741Sthompsa	if (fstat(fd, &st) < 0) {
527190741Sthompsa		close(fd);
528190741Sthompsa		return NULL;
529190741Sthompsa	}
530207077Sthompsa	if (!S_ISREG(st.st_mode)) {
531190741Sthompsa		logit("User %s %s %s is not a regular file",
532207077Sthompsa		    pw->pw_name, file_type, file);
533190741Sthompsa		close(fd);
534190741Sthompsa		return NULL;
535190741Sthompsa	}
536194228Sthompsa	unset_nonblock(fd);
537190741Sthompsa	if ((f = fdopen(fd, "r")) == NULL) {
538190741Sthompsa		close(fd);
539190741Sthompsa		return NULL;
540194228Sthompsa	}
541190741Sthompsa	if (strict_modes &&
542190741Sthompsa	    secure_filename(f, file, pw, line, sizeof(line)) != 0) {
543190741Sthompsa		fclose(f);
544190741Sthompsa		logit("Authentication refused: %s", line);
545190741Sthompsa		auth_debug_add("Ignored %s: %s", file_type, line);
546190741Sthompsa		return NULL;
547190741Sthompsa	}
548194228Sthompsa
549190741Sthompsa	return f;
550190741Sthompsa}
551194228Sthompsa
552190741Sthompsa
553190741SthompsaFILE *
554190741Sthompsaauth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
555190741Sthompsa{
556194228Sthompsa	return auth_openfile(file, pw, strict_modes, 1, "authorized keys");
557190741Sthompsa}
558190741Sthompsa
559195959SalfredFILE *
560195959Salfredauth_openprincipals(const char *file, struct passwd *pw, int strict_modes)
561194228Sthompsa{
562190741Sthompsa	return auth_openfile(file, pw, strict_modes, 0,
563190741Sthompsa	    "authorized principals");
564190741Sthompsa}
565190741Sthompsa
566190741Sthompsastruct passwd *
567190741Sthompsagetpwnamallow(const char *user)
568190741Sthompsa{
569189583Sthompsa#ifdef HAVE_LOGIN_CAP
570184610Salfred	extern login_cap_t *lc;
571184610Salfred#ifdef BSD_AUTH
572184610Salfred	auth_session_t *as;
573184610Salfred#endif
574184610Salfred#endif
575184610Salfred	struct passwd *pw;
576184610Salfred	struct connection_info *ci = get_connection_info(1, options.use_dns);
577194228Sthompsa
578195959Salfred	ci->user = user;
579195959Salfred	parse_server_match_config(&options, ci);
580195959Salfred
581190741Sthompsa#if defined(_AIX) && defined(HAVE_SETAUTHDB)
582190741Sthompsa	aix_setauthdb(user);
583184610Salfred#endif
584184610Salfred
585184610Salfred	pw = getpwnam(user);
586190741Sthompsa
587184610Salfred#if defined(_AIX) && defined(HAVE_SETAUTHDB)
588184610Salfred	aix_restoreauthdb();
589190741Sthompsa#endif
590195959Salfred#ifdef HAVE_CYGWIN
591190741Sthompsa	/*
592195959Salfred	 * Windows usernames are case-insensitive.  To avoid later problems
593190741Sthompsa	 * when trying to match the username, the user is only allowed to
594195959Salfred	 * login if the username is given in the same case as stored in the
595190741Sthompsa	 * user database.
596195959Salfred	 */
597190741Sthompsa	if (pw != NULL && strcmp(user, pw->pw_name) != 0) {
598195959Salfred		logit("Login name %.100s does not match stored username %.100s",
599190741Sthompsa		    user, pw->pw_name);
600195959Salfred		pw = NULL;
601188981Sthompsa	}
602190741Sthompsa#endif
603190741Sthompsa	if (pw == NULL) {
604190741Sthompsa		logit("Invalid user %.100s from %.100s",
605190741Sthompsa		    user, get_remote_ipaddr());
606190741Sthompsa#ifdef CUSTOM_FAILED_LOGIN
607190741Sthompsa		record_failed_login(user,
608190741Sthompsa		    get_canonical_hostname(options.use_dns), "ssh");
609190741Sthompsa#endif
610190741Sthompsa#ifdef SSH_AUDIT_EVENTS
611184610Salfred		audit_event(SSH_INVALID_USER);
612188981Sthompsa#endif /* SSH_AUDIT_EVENTS */
613194228Sthompsa		return (NULL);
614190741Sthompsa	}
615184610Salfred	if (!allowed_user(pw))
616190741Sthompsa		return (NULL);
617184610Salfred#ifdef HAVE_LOGIN_CAP
618233774Shselasky	if ((lc = login_getpwclass(pw)) == NULL) {
619184610Salfred		debug("unable to get login class: %s", user);
620194677Sthompsa		return (NULL);
621194677Sthompsa	}
622184610Salfred#ifdef BSD_AUTH
623184610Salfred	if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 ||
624184610Salfred	    auth_approval(as, lc, pw->pw_name, "ssh") <= 0) {
625184610Salfred		debug("Approval failure for %s", user);
626207077Sthompsa		pw = NULL;
627190741Sthompsa	}
628190741Sthompsa	if (as != NULL)
629184610Salfred		auth_close(as);
630190741Sthompsa#endif
631190741Sthompsa#endif
632190741Sthompsa	if (pw != NULL)
633190741Sthompsa		return (pwcopy(pw));
634190741Sthompsa	return (NULL);
635190741Sthompsa}
636190741Sthompsa
637190741Sthompsa/* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */
638190741Sthompsaint
639190741Sthompsaauth_key_is_revoked(Key *key)
640190741Sthompsa{
641190741Sthompsa	char *key_fp;
642190741Sthompsa
643190741Sthompsa	if (options.revoked_keys_file == NULL)
644190741Sthompsa		return 0;
645190741Sthompsa	switch (ssh_krl_file_contains_key(options.revoked_keys_file, key)) {
646190741Sthompsa	case 0:
647184610Salfred		return 0;	/* Not revoked */
648184610Salfred	case -2:
649184610Salfred		break;		/* Not a KRL */
650184610Salfred	default:
651194228Sthompsa		goto revoked;
652184610Salfred	}
653233774Shselasky	debug3("%s: treating %s as a key list", __func__,
654189110Sthompsa	    options.revoked_keys_file);
655235451Shselasky	switch (key_in_file(key, options.revoked_keys_file, 0)) {
656184610Salfred	case 0:
657235451Shselasky		/* key not revoked */
658198373Sthompsa		return 0;
659198373Sthompsa	case -1:
660198373Sthompsa		/* Error opening revoked_keys_file: refuse all keys */
661198373Sthompsa		error("Revoked keys file is unreadable: refusing public key "
662219848Shselasky		    "authentication");
663198373Sthompsa		return 1;
664184610Salfred	case 1:
665184610Salfred revoked:
666184610Salfred		/* Key revoked */
667184610Salfred		key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
668184610Salfred		error("WARNING: authentication attempt with a revoked "
669184610Salfred		    "%s key %s ", key_type(key), key_fp);
670184610Salfred		xfree(key_fp);
671184610Salfred		return 1;
672184610Salfred	}
673184610Salfred	fatal("key_in_file returned junk");
674184610Salfred}
675184610Salfred
676184610Salfredvoid
677184610Salfredauth_debug_add(const char *fmt,...)
678184610Salfred{
679184610Salfred	char buf[1024];
680184610Salfred	va_list args;
681194228Sthompsa
682184610Salfred	if (!auth_debug_init)
683194228Sthompsa		return;
684184610Salfred
685194228Sthompsa	va_start(args, fmt);
686184610Salfred	vsnprintf(buf, sizeof(buf), fmt, args);
687184610Salfred	va_end(args);
688184610Salfred	buffer_put_cstring(&auth_debug, buf);
689184610Salfred}
690184610Salfred
691184610Salfredvoid
692184610Salfredauth_debug_send(void)
693192984Sthompsa{
694184610Salfred	char *msg;
695194677Sthompsa
696195959Salfred	if (!auth_debug_init)
697184610Salfred		return;
698195959Salfred	while (buffer_len(&auth_debug)) {
699195959Salfred		msg = buffer_get_string(&auth_debug, NULL);
700195959Salfred		packet_send_debug("%s", msg);
701195959Salfred		xfree(msg);
702195959Salfred	}
703195959Salfred}
704195959Salfred
705195959Salfredvoid
706195959Salfredauth_debug_reset(void)
707195959Salfred{
708195959Salfred	if (auth_debug_init)
709195959Salfred		buffer_clear(&auth_debug);
710195959Salfred	else {
711195959Salfred		buffer_init(&auth_debug);
712195959Salfred		auth_debug_init = 1;
713195959Salfred	}
714194228Sthompsa}
715184610Salfred
716184610Salfredstruct passwd *
717184610Salfredfakepw(void)
718192984Sthompsa{
719184610Salfred	static struct passwd fake;
720194677Sthompsa
721184610Salfred	memset(&fake, 0, sizeof(fake));
722194228Sthompsa	fake.pw_name = "NOUSER";
723194228Sthompsa	fake.pw_passwd =
724184610Salfred	    "$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK";
725184610Salfred	fake.pw_gecos = "NOUSER";
726184610Salfred	fake.pw_uid = privsep_pw == NULL ? (uid_t)-1 : privsep_pw->pw_uid;
727184610Salfred	fake.pw_gid = privsep_pw == NULL ? (gid_t)-1 : privsep_pw->pw_gid;
728184610Salfred#ifdef HAVE_PW_CLASS_IN_PASSWD
729184610Salfred	fake.pw_class = "";
730184610Salfred#endif
731184610Salfred	fake.pw_dir = "/nonexist";
732184610Salfred	fake.pw_shell = "/nonexist";
733184610Salfred
734184610Salfred	return (&fake);
735184610Salfred}
736184610Salfred