auth.c revision 204917
1221828Sgrehan/* $OpenBSD: auth.c,v 1.86 2010/03/05 02:58:11 djm Exp $ */
2221828Sgrehan/*
3221828Sgrehan * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4221828Sgrehan *
5221828Sgrehan * Redistribution and use in source and binary forms, with or without
6221828Sgrehan * modification, are permitted provided that the following conditions
7221828Sgrehan * are met:
8221828Sgrehan * 1. Redistributions of source code must retain the above copyright
9221828Sgrehan *    notice, this list of conditions and the following disclaimer.
10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11221828Sgrehan *    notice, this list of conditions and the following disclaimer in the
12221828Sgrehan *    documentation and/or other materials provided with the distribution.
13221828Sgrehan *
14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15221828Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16221828Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17221828Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18221828Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19221828Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20221828Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21221828Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22221828Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23221828Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24221828Sgrehan */
25221828Sgrehan
26221828Sgrehan#include "includes.h"
27221828Sgrehan__RCSID("$FreeBSD: head/crypto/openssh/auth.c 204917 2010-03-09 19:16:43Z des $");
28221828Sgrehan
29221828Sgrehan#include <sys/types.h>
30221828Sgrehan#include <sys/stat.h>
31221828Sgrehan#include <sys/param.h>
32221828Sgrehan
33234695Sgrehan#include <netinet/in.h>
34221828Sgrehan
35221828Sgrehan#include <errno.h>
36221828Sgrehan#include <fcntl.h>
37221828Sgrehan#ifdef HAVE_PATHS_H
38221828Sgrehan# include <paths.h>
39221828Sgrehan#endif
40221828Sgrehan#include <pwd.h>
41221828Sgrehan#ifdef HAVE_LOGIN_H
42221828Sgrehan#include <login.h>
43221828Sgrehan#endif
44221828Sgrehan#ifdef USE_SHADOW
45221828Sgrehan#include <shadow.h>
46221828Sgrehan#endif
47221828Sgrehan#ifdef HAVE_LIBGEN_H
48221828Sgrehan#include <libgen.h>
49221828Sgrehan#endif
50241489Sneel#include <stdarg.h>
51221914Sjhb#include <stdio.h>
52221828Sgrehan#include <string.h>
53221828Sgrehan#include <unistd.h>
54221828Sgrehan
55221828Sgrehan#include "xmalloc.h"
56221828Sgrehan#include "match.h"
57221828Sgrehan#include "groupaccess.h"
58221828Sgrehan#include "log.h"
59221828Sgrehan#include "buffer.h"
60221828Sgrehan#include "servconf.h"
61242065Sneel#include "key.h"
62221828Sgrehan#include "hostfile.h"
63221828Sgrehan#include "auth.h"
64221828Sgrehan#include "auth-options.h"
65221828Sgrehan#include "canohost.h"
66221828Sgrehan#include "uidswap.h"
67221828Sgrehan#include "misc.h"
68221828Sgrehan#include "packet.h"
69221828Sgrehan#include "loginrec.h"
70241489Sneel#ifdef GSSAPI
71241489Sneel#include "ssh-gss.h"
72221828Sgrehan#endif
73221828Sgrehan#include "authfile.h"
74221828Sgrehan#include "monitor_wrap.h"
75221828Sgrehan
76221828Sgrehan/* import */
77234695Sgrehanextern ServerOptions options;
78221828Sgrehanextern int use_privsep;
79240894Sneelextern Buffer loginmsg;
80240922Sneelextern struct passwd *privsep_pw;
81241982Sneel
82221828Sgrehan/* Debugging messages */
83221828SgrehanBuffer auth_debug;
84221828Sgrehanint auth_debug_init;
85221828Sgrehan
86221828Sgrehan/*
87221828Sgrehan * Check if the user is allowed to log in via ssh. If user is listed
88221828Sgrehan * in DenyUsers or one of user's groups is listed in DenyGroups, false
89221828Sgrehan * will be returned. If AllowUsers isn't empty and user isn't listed
90221828Sgrehan * there, or if AllowGroups isn't empty and one of user's groups isn't
91221828Sgrehan * listed there, false will be returned.
92221828Sgrehan * If the user's shell is not executable, false will be returned.
93221828Sgrehan * Otherwise true is returned.
94221828Sgrehan */
95221828Sgrehanint
96242065Sneelallowed_user(struct passwd * pw)
97242065Sneel{
98242065Sneel	struct stat st;
99241489Sneel	const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL;
100221828Sgrehan	u_int i;
101221828Sgrehan#ifdef USE_SHADOW
102221828Sgrehan	struct spwd *spw = NULL;
103221828Sgrehan#endif
104221828Sgrehan
105221828Sgrehan	/* Shouldn't be called if pw is NULL, but better safe than sorry... */
106221828Sgrehan	if (!pw || !pw->pw_name)
107221828Sgrehan		return 0;
108221828Sgrehan
109221828Sgrehan#ifdef USE_SHADOW
110221828Sgrehan	if (!options.use_pam)
111223621Sgrehan		spw = getspnam(pw->pw_name);
112221828Sgrehan#ifdef HAS_SHADOW_EXPIRE
113221828Sgrehan	if (!options.use_pam && spw != NULL && auth_shadow_acctexpired(spw))
114221828Sgrehan		return 0;
115223621Sgrehan#endif /* HAS_SHADOW_EXPIRE */
116221828Sgrehan#endif /* USE_SHADOW */
117221828Sgrehan
118221828Sgrehan	/* grab passwd field for locked account check */
119221828Sgrehan	passwd = pw->pw_passwd;
120221828Sgrehan#ifdef USE_SHADOW
121221828Sgrehan	if (spw != NULL)
122221828Sgrehan#ifdef USE_LIBIAF
123240894Sneel		passwd = get_iaf_password(pw);
124240894Sneel#else
125221828Sgrehan		passwd = spw->sp_pwdp;
126241147Sneel#endif /* USE_LIBIAF */
127241147Sneel#endif
128241147Sneel
129241147Sneel	/* check for locked account */
130241147Sneel	if (!options.use_pam && passwd && *passwd) {
131241147Sneel		int locked = 0;
132221828Sgrehan
133221828Sgrehan#ifdef LOCKED_PASSWD_STRING
134221828Sgrehan		if (strcmp(passwd, LOCKED_PASSWD_STRING) == 0)
135221828Sgrehan			 locked = 1;
136221828Sgrehan#endif
137221828Sgrehan#ifdef LOCKED_PASSWD_PREFIX
138221828Sgrehan		if (strncmp(passwd, LOCKED_PASSWD_PREFIX,
139221828Sgrehan		    strlen(LOCKED_PASSWD_PREFIX)) == 0)
140221828Sgrehan			 locked = 1;
141221828Sgrehan#endif
142221828Sgrehan#ifdef LOCKED_PASSWD_SUBSTR
143221828Sgrehan		if (strstr(passwd, LOCKED_PASSWD_SUBSTR))
144221828Sgrehan			locked = 1;
145221828Sgrehan#endif
146221828Sgrehan#ifdef USE_LIBIAF
147234695Sgrehan		free(passwd);
148234695Sgrehan#endif /* USE_LIBIAF */
149221828Sgrehan		if (locked) {
150221828Sgrehan			logit("User %.100s not allowed because account is locked",
151221828Sgrehan			    pw->pw_name);
152221828Sgrehan			return 0;
153221828Sgrehan		}
154221828Sgrehan	}
155221828Sgrehan
156221828Sgrehan	/*
157221828Sgrehan	 * Deny if shell does not exist or is not executable unless we
158221828Sgrehan	 * are chrooting.
159221828Sgrehan	 */
160234695Sgrehan	if (options.chroot_directory == NULL ||
161234695Sgrehan	    strcasecmp(options.chroot_directory, "none") == 0) {
162221828Sgrehan		char *shell = xstrdup((pw->pw_shell[0] == '\0') ?
163221828Sgrehan		    _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */
164221828Sgrehan
165221828Sgrehan		if (stat(shell, &st) != 0) {
166221828Sgrehan			logit("User %.100s not allowed because shell %.100s "
167221828Sgrehan			    "does not exist", pw->pw_name, shell);
168221828Sgrehan			xfree(shell);
169221828Sgrehan			return 0;
170221828Sgrehan		}
171241489Sneel		if (S_ISREG(st.st_mode) == 0 ||
172241489Sneel		    (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
173221828Sgrehan			logit("User %.100s not allowed because shell %.100s "
174221828Sgrehan			    "is not executable", pw->pw_name, shell);
175240943Sneel			xfree(shell);
176234695Sgrehan			return 0;
177234695Sgrehan		}
178221828Sgrehan		xfree(shell);
179221828Sgrehan	}
180221828Sgrehan
181240894Sneel	if (options.num_deny_users > 0 || options.num_allow_users > 0 ||
182240894Sneel	    options.num_deny_groups > 0 || options.num_allow_groups > 0) {
183240894Sneel		hostname = get_canonical_hostname(options.use_dns);
184240894Sneel		ipaddr = get_remote_ipaddr();
185240894Sneel	}
186240894Sneel
187240894Sneel	/* Return false if user is listed in DenyUsers */
188240894Sneel	if (options.num_deny_users > 0) {
189240894Sneel		for (i = 0; i < options.num_deny_users; i++)
190240894Sneel			if (match_user(pw->pw_name, hostname, ipaddr,
191240894Sneel			    options.deny_users[i])) {
192240894Sneel				logit("User %.100s from %.100s not allowed "
193240894Sneel				    "because listed in DenyUsers",
194221828Sgrehan				    pw->pw_name, hostname);
195221828Sgrehan				return 0;
196221828Sgrehan			}
197221828Sgrehan	}
198221828Sgrehan	/* Return false if AllowUsers isn't empty and user isn't listed there */
199221828Sgrehan	if (options.num_allow_users > 0) {
200221828Sgrehan		for (i = 0; i < options.num_allow_users; i++)
201221828Sgrehan			if (match_user(pw->pw_name, hostname, ipaddr,
202221828Sgrehan			    options.allow_users[i]))
203221828Sgrehan				break;
204221828Sgrehan		/* i < options.num_allow_users iff we break for loop */
205221828Sgrehan		if (i >= options.num_allow_users) {
206221828Sgrehan			logit("User %.100s from %.100s not allowed because "
207221828Sgrehan			    "not listed in AllowUsers", pw->pw_name, hostname);
208221828Sgrehan			return 0;
209221828Sgrehan		}
210221828Sgrehan	}
211221828Sgrehan	if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
212221828Sgrehan		/* Get the user's group access list (primary and supplementary) */
213221828Sgrehan		if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
214221828Sgrehan			logit("User %.100s from %.100s not allowed because "
215221828Sgrehan			    "not in any group", pw->pw_name, hostname);
216221828Sgrehan			return 0;
217221828Sgrehan		}
218221828Sgrehan
219221828Sgrehan		/* Return false if one of user's groups is listed in DenyGroups */
220221828Sgrehan		if (options.num_deny_groups > 0)
221221828Sgrehan			if (ga_match(options.deny_groups,
222221828Sgrehan			    options.num_deny_groups)) {
223221828Sgrehan				ga_free();
224221828Sgrehan				logit("User %.100s from %.100s not allowed "
225221828Sgrehan				    "because a group is listed in DenyGroups",
226221828Sgrehan				    pw->pw_name, hostname);
227221828Sgrehan				return 0;
228221828Sgrehan			}
229241454Sneel		/*
230241454Sneel		 * Return false if AllowGroups isn't empty and one of user's groups
231241454Sneel		 * isn't listed there
232241454Sneel		 */
233241454Sneel		if (options.num_allow_groups > 0)
234241454Sneel			if (!ga_match(options.allow_groups,
235221828Sgrehan			    options.num_allow_groups)) {
236221828Sgrehan				ga_free();
237221828Sgrehan				logit("User %.100s from %.100s not allowed "
238221828Sgrehan				    "because none of user's groups are listed "
239221828Sgrehan				    "in AllowGroups", pw->pw_name, hostname);
240221828Sgrehan				return 0;
241221828Sgrehan			}
242221828Sgrehan		ga_free();
243221828Sgrehan	}
244221828Sgrehan
245221828Sgrehan#ifdef CUSTOM_SYS_AUTH_ALLOWED_USER
246221828Sgrehan	if (!sys_auth_allowed_user(pw, &loginmsg))
247221828Sgrehan		return 0;
248221828Sgrehan#endif
249221828Sgrehan
250221828Sgrehan	/* We found no reason not to let this user try to log on... */
251221828Sgrehan	return 1;
252221828Sgrehan}
253221828Sgrehan
254221828Sgrehanvoid
255221828Sgrehanauth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
256221828Sgrehan{
257221828Sgrehan	void (*authlog) (const char *fmt,...) = verbose;
258221828Sgrehan	char *authmsg;
259221828Sgrehan
260221828Sgrehan	if (use_privsep && !mm_is_monitor() && !authctxt->postponed)
261221828Sgrehan		return;
262221828Sgrehan
263221828Sgrehan	/* Raise logging level */
264221828Sgrehan	if (authenticated == 1 ||
265221828Sgrehan	    !authctxt->valid ||
266221828Sgrehan	    authctxt->failures >= options.max_authtries / 2 ||
267221828Sgrehan	    strcmp(method, "password") == 0)
268221828Sgrehan		authlog = logit;
269221828Sgrehan
270221828Sgrehan	if (authctxt->postponed)
271221828Sgrehan		authmsg = "Postponed";
272221828Sgrehan	else
273221828Sgrehan		authmsg = authenticated ? "Accepted" : "Failed";
274221828Sgrehan
275221828Sgrehan	authlog("%s %s for %s%.100s from %.200s port %d%s",
276221828Sgrehan	    authmsg,
277221828Sgrehan	    method,
278221828Sgrehan	    authctxt->valid ? "" : "invalid user ",
279221828Sgrehan	    authctxt->user,
280221828Sgrehan	    get_remote_ipaddr(),
281221828Sgrehan	    get_remote_port(),
282221828Sgrehan	    info);
283221828Sgrehan
284221828Sgrehan#ifdef CUSTOM_FAILED_LOGIN
285221828Sgrehan	if (authenticated == 0 && !authctxt->postponed &&
286221828Sgrehan	    (strcmp(method, "password") == 0 ||
287241178Sneel	    strncmp(method, "keyboard-interactive", 20) == 0 ||
288241178Sneel	    strcmp(method, "challenge-response") == 0))
289241178Sneel		record_failed_login(authctxt->user,
290241178Sneel		    get_canonical_hostname(options.use_dns), "ssh");
291241178Sneel# ifdef WITH_AIXAUTHENTICATE
292241362Sneel	if (authenticated)
293241178Sneel		sys_auth_record_login(authctxt->user,
294241362Sneel		    get_canonical_hostname(options.use_dns), "ssh", &loginmsg);
295241362Sneel# endif
296241178Sneel#endif
297241178Sneel#ifdef SSH_AUDIT_EVENTS
298241178Sneel	if (authenticated == 0 && !authctxt->postponed)
299241178Sneel		audit_event(audit_classify_auth(method));
300241178Sneel#endif
301241178Sneel}
302241178Sneel
303241178Sneel/*
304241362Sneel * Check whether root logins are disallowed.
305241362Sneel */
306241362Sneelint
307241362Sneelauth_root_allowed(char *method)
308241362Sneel{
309241362Sneel	switch (options.permit_root_login) {
310241362Sneel	case PERMIT_YES:
311241178Sneel		return 1;
312241178Sneel	case PERMIT_NO_PASSWD:
313241178Sneel		if (strcmp(method, "password") != 0)
314241178Sneel			return 1;
315241178Sneel		break;
316241362Sneel	case PERMIT_FORCED_ONLY:
317241362Sneel		if (forced_command) {
318241362Sneel			logit("Root login accepted for forced command.");
319241362Sneel			return 1;
320241362Sneel		}
321241362Sneel		break;
322241178Sneel	}
323241178Sneel	logit("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr());
324241178Sneel	return 0;
325221828Sgrehan}
326221828Sgrehan
327221828Sgrehan
328221828Sgrehan/*
329221828Sgrehan * Given a template and a passwd structure, build a filename
330221828Sgrehan * by substituting % tokenised options. Currently, %% becomes '%',
331221828Sgrehan * %h becomes the home directory and %u the username.
332221828Sgrehan *
333241178Sneel * This returns a buffer allocated by xmalloc.
334221828Sgrehan */
335241178Sneelstatic char *
336241178Sneelexpand_authorized_keys(const char *filename, struct passwd *pw)
337221828Sgrehan{
338221828Sgrehan	char *file, ret[MAXPATHLEN];
339221828Sgrehan	int i;
340221828Sgrehan
341221828Sgrehan	file = percent_expand(filename, "h", pw->pw_dir,
342221828Sgrehan	    "u", pw->pw_name, (char *)NULL);
343221828Sgrehan
344221828Sgrehan	/*
345221828Sgrehan	 * Ensure that filename starts anchored. If not, be backward
346221828Sgrehan	 * compatible and prepend the '%h/'
347221828Sgrehan	 */
348221828Sgrehan	if (*file == '/')
349221828Sgrehan		return (file);
350221828Sgrehan
351221828Sgrehan	i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file);
352221828Sgrehan	if (i < 0 || (size_t)i >= sizeof(ret))
353221828Sgrehan		fatal("expand_authorized_keys: path too long");
354221828Sgrehan	xfree(file);
355221828Sgrehan	return (xstrdup(ret));
356221828Sgrehan}
357221828Sgrehan
358241147Sneelchar *
359241147Sneelauthorized_keys_file(struct passwd *pw)
360221828Sgrehan{
361221828Sgrehan	return expand_authorized_keys(options.authorized_keys_file, pw);
362221828Sgrehan}
363221828Sgrehan
364221828Sgrehanchar *
365221828Sgrehanauthorized_keys_file2(struct passwd *pw)
366221828Sgrehan{
367241147Sneel	return expand_authorized_keys(options.authorized_keys_file2, pw);
368241147Sneel}
369221828Sgrehan
370221828Sgrehan/* return ok if key exists in sysfile or userfile */
371241041SneelHostStatus
372241041Sneelcheck_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
373241041Sneel    const char *sysfile, const char *userfile)
374241041Sneel{
375241041Sneel	Key *found;
376241041Sneel	char *user_hostfile;
377241041Sneel	struct stat st;
378241041Sneel	HostStatus host_status;
379241041Sneel
380241041Sneel	/* Check if we know the host and its host key. */
381241041Sneel	found = key_new(key->type);
382241041Sneel	host_status = check_host_in_hostfile(sysfile, host, key, found, NULL);
383241041Sneel
384241041Sneel	if (host_status != HOST_OK && userfile != NULL) {
385241041Sneel		user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
386241041Sneel		if (options.strict_modes &&
387241041Sneel		    (stat(user_hostfile, &st) == 0) &&
388241041Sneel		    ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
389241041Sneel		    (st.st_mode & 022) != 0)) {
390241041Sneel			logit("Authentication refused for %.100s: "
391241041Sneel			    "bad owner or modes for %.200s",
392241041Sneel			    pw->pw_name, user_hostfile);
393221828Sgrehan		} else {
394241041Sneel			temporarily_use_uid(pw);
395221828Sgrehan			host_status = check_host_in_hostfile(user_hostfile,
396241041Sneel			    host, key, found, NULL);
397241178Sneel			restore_uid();
398241041Sneel		}
399241362Sneel		xfree(user_hostfile);
400221828Sgrehan	}
401221828Sgrehan	key_free(found);
402241041Sneel
403241041Sneel	debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ?
404241041Sneel	    "ok" : "not found", host);
405221828Sgrehan	return host_status;
406241041Sneel}
407241041Sneel
408241041Sneel
409241041Sneel/*
410241041Sneel * Check a given file for security. This is defined as all components
411241041Sneel * of the path to the file must be owned by either the owner of
412241041Sneel * of the file or root and no directories must be group or world writable.
413241041Sneel *
414241041Sneel * XXX Should any specific check be done for sym links ?
415241041Sneel *
416241041Sneel * Takes an open file descriptor, the file name, a uid and and
417221828Sgrehan * error buffer plus max size as arguments.
418241041Sneel *
419241041Sneel * Returns 0 on success and -1 on failure
420221828Sgrehan */
421241041Sneelstatic int
422241041Sneelsecure_filename(FILE *f, const char *file, struct passwd *pw,
423221828Sgrehan    char *err, size_t errlen)
424241041Sneel{
425241041Sneel	uid_t uid = pw->pw_uid;
426241041Sneel	char buf[MAXPATHLEN], homedir[MAXPATHLEN];
427241041Sneel	char *cp;
428241041Sneel	int comparehome = 0;
429241041Sneel	struct stat st;
430241041Sneel
431221828Sgrehan	if (realpath(file, buf) == NULL) {
432221828Sgrehan		snprintf(err, errlen, "realpath %s failed: %s", file,
433221828Sgrehan		    strerror(errno));
434241362Sneel		return -1;
435241362Sneel	}
436241178Sneel	if (realpath(pw->pw_dir, homedir) != NULL)
437221828Sgrehan		comparehome = 1;
438241362Sneel
439241178Sneel	/* check the open file to avoid races */
440241178Sneel	if (fstat(fileno(f), &st) < 0 ||
441241178Sneel	    (st.st_uid != 0 && st.st_uid != uid) ||
442241178Sneel	    (st.st_mode & 022) != 0) {
443241178Sneel		snprintf(err, errlen, "bad ownership or modes for file %s",
444241178Sneel		    buf);
445241178Sneel		return -1;
446241178Sneel	}
447241178Sneel
448241178Sneel	/* for each component of the canonical path, walking upwards */
449241178Sneel	for (;;) {
450241178Sneel		if ((cp = dirname(buf)) == NULL) {
451241178Sneel			snprintf(err, errlen, "dirname() failed");
452241178Sneel			return -1;
453241362Sneel		}
454241362Sneel		strlcpy(buf, cp, sizeof(buf));
455241362Sneel
456241362Sneel		debug3("secure_filename: checking '%s'", buf);
457241362Sneel		if (stat(buf, &st) < 0 ||
458241178Sneel		    (st.st_uid != 0 && st.st_uid != uid) ||
459241178Sneel		    (st.st_mode & 022) != 0) {
460241178Sneel			snprintf(err, errlen,
461241178Sneel			    "bad ownership or modes for directory %s", buf);
462241178Sneel			return -1;
463241362Sneel		}
464241178Sneel
465221828Sgrehan		/* If are past the homedir then we can stop */
466221828Sgrehan		if (comparehome && strcmp(homedir, buf) == 0) {
467221828Sgrehan			debug3("secure_filename: terminating check at '%s'",
468241362Sneel			    buf);
469241362Sneel			break;
470241362Sneel		}
471241362Sneel		/*
472241362Sneel		 * dirname should always complete with a "/" path,
473241362Sneel		 * but we can be paranoid and check for "." too
474221828Sgrehan		 */
475241041Sneel		if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
476221828Sgrehan			break;
477221828Sgrehan	}
478221828Sgrehan	return 0;
479221828Sgrehan}
480221828Sgrehan
481221828SgrehanFILE *
482241148Sneelauth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
483221828Sgrehan{
484241148Sneel	char line[1024];
485241148Sneel	struct stat st;
486241148Sneel	int fd;
487241148Sneel	FILE *f;
488241147Sneel
489221828Sgrehan	/*
490221828Sgrehan	 * Open the file containing the authorized keys
491221828Sgrehan	 * Fail quietly if file does not exist
492221828Sgrehan	 */
493221828Sgrehan	if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
494221828Sgrehan		if (errno != ENOENT)
495221828Sgrehan			debug("Could not open keyfile '%s': %s", file,
496221828Sgrehan			   strerror(errno));
497221828Sgrehan		return NULL;
498221828Sgrehan	}
499221828Sgrehan
500221828Sgrehan	if (fstat(fd, &st) < 0) {
501221828Sgrehan		close(fd);
502221828Sgrehan		return NULL;
503221828Sgrehan	}
504221828Sgrehan	if (!S_ISREG(st.st_mode)) {
505221828Sgrehan		logit("User %s authorized keys %s is not a regular file",
506221828Sgrehan		    pw->pw_name, file);
507221828Sgrehan		close(fd);
508221828Sgrehan		return NULL;
509221828Sgrehan	}
510221828Sgrehan	unset_nonblock(fd);
511221828Sgrehan	if ((f = fdopen(fd, "r")) == NULL) {
512221828Sgrehan		close(fd);
513221828Sgrehan		return NULL;
514221828Sgrehan	}
515221828Sgrehan	if (options.strict_modes &&
516221828Sgrehan	    secure_filename(f, file, pw, line, sizeof(line)) != 0) {
517221828Sgrehan		fclose(f);
518221828Sgrehan		logit("Authentication refused: %s", line);
519221828Sgrehan		return NULL;
520221828Sgrehan	}
521221828Sgrehan
522221828Sgrehan	return f;
523221828Sgrehan}
524221828Sgrehan
525221828Sgrehanstruct passwd *
526221828Sgrehangetpwnamallow(const char *user)
527221828Sgrehan{
528221828Sgrehan#ifdef HAVE_LOGIN_CAP
529221828Sgrehan	extern login_cap_t *lc;
530221828Sgrehan#ifdef BSD_AUTH
531221828Sgrehan	auth_session_t *as;
532221828Sgrehan#endif
533221828Sgrehan#endif
534221828Sgrehan	struct passwd *pw;
535221828Sgrehan
536221828Sgrehan	parse_server_match_config(&options, user,
537221828Sgrehan	    get_canonical_hostname(options.use_dns), get_remote_ipaddr());
538221828Sgrehan
539221828Sgrehan#if defined(_AIX) && defined(HAVE_SETAUTHDB)
540221828Sgrehan	aix_setauthdb(user);
541221828Sgrehan#endif
542221828Sgrehan
543221828Sgrehan	pw = getpwnam(user);
544221828Sgrehan
545221828Sgrehan#if defined(_AIX) && defined(HAVE_SETAUTHDB)
546221828Sgrehan	aix_restoreauthdb();
547221828Sgrehan#endif
548221828Sgrehan#ifdef HAVE_CYGWIN
549221828Sgrehan	/*
550221828Sgrehan	 * Windows usernames are case-insensitive.  To avoid later problems
551221828Sgrehan	 * when trying to match the username, the user is only allowed to
552221828Sgrehan	 * login if the username is given in the same case as stored in the
553221828Sgrehan	 * user database.
554221828Sgrehan	 */
555221828Sgrehan	if (pw != NULL && strcmp(user, pw->pw_name) != 0) {
556221828Sgrehan		logit("Login name %.100s does not match stored username %.100s",
557221828Sgrehan		    user, pw->pw_name);
558221828Sgrehan		pw = NULL;
559221828Sgrehan	}
560221828Sgrehan#endif
561221828Sgrehan	if (pw == NULL) {
562221828Sgrehan		logit("Invalid user %.100s from %.100s",
563221828Sgrehan		    user, get_remote_ipaddr());
564221828Sgrehan#ifdef CUSTOM_FAILED_LOGIN
565221828Sgrehan		record_failed_login(user,
566221828Sgrehan		    get_canonical_hostname(options.use_dns), "ssh");
567221828Sgrehan#endif
568221828Sgrehan#ifdef SSH_AUDIT_EVENTS
569221828Sgrehan		audit_event(SSH_INVALID_USER);
570221828Sgrehan#endif /* SSH_AUDIT_EVENTS */
571221828Sgrehan		return (NULL);
572221828Sgrehan	}
573221828Sgrehan	if (!allowed_user(pw))
574221828Sgrehan		return (NULL);
575221828Sgrehan#ifdef HAVE_LOGIN_CAP
576221828Sgrehan	if ((lc = login_getpwclass(pw)) == NULL) {
577221828Sgrehan		debug("unable to get login class: %s", user);
578221828Sgrehan		return (NULL);
579221828Sgrehan	}
580221828Sgrehan#ifdef BSD_AUTH
581221828Sgrehan	if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 ||
582221828Sgrehan	    auth_approval(as, lc, pw->pw_name, "ssh") <= 0) {
583221828Sgrehan		debug("Approval failure for %s", user);
584221828Sgrehan		pw = NULL;
585221828Sgrehan	}
586221828Sgrehan	if (as != NULL)
587221828Sgrehan		auth_close(as);
588221828Sgrehan#endif
589221828Sgrehan#endif
590221828Sgrehan	if (pw != NULL)
591221828Sgrehan		return (pwcopy(pw));
592221828Sgrehan	return (NULL);
593221828Sgrehan}
594221828Sgrehan
595221828Sgrehan/* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */
596221828Sgrehanint
597221828Sgrehanauth_key_is_revoked(Key *key)
598221828Sgrehan{
599221828Sgrehan	char *key_fp;
600221828Sgrehan
601221828Sgrehan	if (options.revoked_keys_file == NULL)
602221828Sgrehan		return 0;
603221828Sgrehan
604221828Sgrehan	switch (key_in_file(key, options.revoked_keys_file, 0)) {
605221828Sgrehan	case 0:
606221828Sgrehan		/* key not revoked */
607221828Sgrehan		return 0;
608221828Sgrehan	case -1:
609221828Sgrehan		/* Error opening revoked_keys_file: refuse all keys */
610221828Sgrehan		error("Revoked keys file is unreadable: refusing public key "
611221828Sgrehan		    "authentication");
612221828Sgrehan		return 1;
613221828Sgrehan	case 1:
614221828Sgrehan		/* Key revoked */
615221828Sgrehan		key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
616221828Sgrehan		error("WARNING: authentication attempt with a revoked "
617221828Sgrehan		    "%s key %s ", key_type(key), key_fp);
618221828Sgrehan		xfree(key_fp);
619221828Sgrehan		return 1;
620221828Sgrehan	}
621221828Sgrehan	fatal("key_in_file returned junk");
622221828Sgrehan}
623221828Sgrehan
624221828Sgrehanvoid
625221828Sgrehanauth_debug_add(const char *fmt,...)
626221828Sgrehan{
627221828Sgrehan	char buf[1024];
628221828Sgrehan	va_list args;
629221828Sgrehan
630221828Sgrehan	if (!auth_debug_init)
631221828Sgrehan		return;
632221828Sgrehan
633221828Sgrehan	va_start(args, fmt);
634221828Sgrehan	vsnprintf(buf, sizeof(buf), fmt, args);
635221828Sgrehan	va_end(args);
636221828Sgrehan	buffer_put_cstring(&auth_debug, buf);
637221828Sgrehan}
638221828Sgrehan
639221828Sgrehanvoid
640221828Sgrehanauth_debug_send(void)
641234695Sgrehan{
642234695Sgrehan	char *msg;
643221828Sgrehan
644234695Sgrehan	if (!auth_debug_init)
645221828Sgrehan		return;
646221828Sgrehan	while (buffer_len(&auth_debug)) {
647221828Sgrehan		msg = buffer_get_string(&auth_debug, NULL);
648221828Sgrehan		packet_send_debug("%s", msg);
649221828Sgrehan		xfree(msg);
650221828Sgrehan	}
651234695Sgrehan}
652221828Sgrehan
653221828Sgrehanvoid
654221828Sgrehanauth_debug_reset(void)
655242065Sneel{
656242065Sneel	if (auth_debug_init)
657221828Sgrehan		buffer_clear(&auth_debug);
658221828Sgrehan	else {
659221828Sgrehan		buffer_init(&auth_debug);
660242065Sneel		auth_debug_init = 1;
661221828Sgrehan	}
662221828Sgrehan}
663242065Sneel
664242065Sneelstruct passwd *
665221828Sgrehanfakepw(void)
666221828Sgrehan{
667221828Sgrehan	static struct passwd fake;
668221828Sgrehan
669221828Sgrehan	memset(&fake, 0, sizeof(fake));
670221828Sgrehan	fake.pw_name = "NOUSER";
671221828Sgrehan	fake.pw_passwd =
672242065Sneel	    "$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK";
673242065Sneel	fake.pw_gecos = "NOUSER";
674242065Sneel	fake.pw_uid = privsep_pw == NULL ? (uid_t)-1 : privsep_pw->pw_uid;
675221828Sgrehan	fake.pw_gid = privsep_pw == NULL ? (gid_t)-1 : privsep_pw->pw_gid;
676221828Sgrehan#ifdef HAVE_PW_CLASS_IN_PASSWD
677221828Sgrehan	fake.pw_class = "";
678221828Sgrehan#endif
679221828Sgrehan	fake.pw_dir = "/nonexist";
680221914Sjhb	fake.pw_shell = "/nonexist";
681221828Sgrehan
682234695Sgrehan	return (&fake);
683221828Sgrehan}
684241489Sneel