+/* $OpenBSD: auth.c,v 1.82 2010/01/13 00:19:04 dtucker Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
*/
#include "includes.h"
-RCSID("$OpenBSD: auth.c,v 1.45 2002/09/20 18:41:29 stevesk Exp $");
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+#include <pwd.h>
#ifdef HAVE_LOGIN_H
#include <login.h>
#endif
-#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW)
+#ifdef USE_SHADOW
#include <shadow.h>
-#endif /* defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) */
-
+#endif
#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
#include "xmalloc.h"
#include "match.h"
#include "groupaccess.h"
#include "log.h"
+#include "buffer.h"
#include "servconf.h"
+#include "key.h"
+#include "hostfile.h"
#include "auth.h"
#include "auth-options.h"
#include "canohost.h"
-#include "buffer.h"
-#include "bufaux.h"
#include "uidswap.h"
-#include "tildexpand.h"
#include "misc.h"
-#include "bufaux.h"
#include "packet.h"
+#include "loginrec.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+#include "monitor_wrap.h"
/* import */
extern ServerOptions options;
+extern int use_privsep;
+extern Buffer loginmsg;
+extern struct passwd *privsep_pw;
/* Debugging messages */
Buffer auth_debug;
allowed_user(struct passwd * pw)
{
struct stat st;
- const char *hostname = NULL, *ipaddr = NULL;
+ const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL;
char *shell;
- int i;
-#ifdef WITH_AIXAUTHENTICATE
- char *loginmsg;
-#endif /* WITH_AIXAUTHENTICATE */
-#if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \
- !defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
- struct spwd *spw;
+ u_int i;
+#ifdef USE_SHADOW
+ struct spwd *spw = NULL;
+#endif
/* Shouldn't be called if pw is NULL, but better safe than sorry... */
if (!pw || !pw->pw_name)
return 0;
-#define DAY (24L * 60 * 60) /* 1 day in seconds */
- spw = getspnam(pw->pw_name);
- if (spw != NULL) {
- time_t today = time(NULL) / DAY;
- debug3("allowed_user: today %d sp_expire %d sp_lstchg %d"
- " sp_max %d", (int)today, (int)spw->sp_expire,
- (int)spw->sp_lstchg, (int)spw->sp_max);
-
- /*
- * We assume account and password expiration occurs the
- * day after the day specified.
- */
- if (spw->sp_expire != -1 && today > spw->sp_expire) {
- log("Account %.100s has expired", pw->pw_name);
- return 0;
- }
+#ifdef USE_SHADOW
+ if (!options.use_pam)
+ spw = getspnam(pw->pw_name);
+#ifdef HAS_SHADOW_EXPIRE
+ if (!options.use_pam && spw != NULL && auth_shadow_acctexpired(spw))
+ return 0;
+#endif /* HAS_SHADOW_EXPIRE */
+#endif /* USE_SHADOW */
+
+ /* grab passwd field for locked account check */
+ passwd = pw->pw_passwd;
+#ifdef USE_SHADOW
+ if (spw != NULL)
+#ifdef USE_LIBIAF
+ passwd = get_iaf_password(pw);
+#else
+ passwd = spw->sp_pwdp;
+#endif /* USE_LIBIAF */
+#endif
- if (spw->sp_lstchg == 0) {
- log("User %.100s password has expired (root forced)",
- pw->pw_name);
- return 0;
- }
+ /* check for locked account */
+ if (!options.use_pam && passwd && *passwd) {
+ int locked = 0;
- if (spw->sp_max != -1 &&
- today > spw->sp_lstchg + spw->sp_max) {
- log("User %.100s password has expired (password aged)",
+#ifdef LOCKED_PASSWD_STRING
+ if (strcmp(passwd, LOCKED_PASSWD_STRING) == 0)
+ locked = 1;
+#endif
+#ifdef LOCKED_PASSWD_PREFIX
+ if (strncmp(passwd, LOCKED_PASSWD_PREFIX,
+ strlen(LOCKED_PASSWD_PREFIX)) == 0)
+ locked = 1;
+#endif
+#ifdef LOCKED_PASSWD_SUBSTR
+ if (strstr(passwd, LOCKED_PASSWD_SUBSTR))
+ locked = 1;
+#endif
+#ifdef USE_LIBIAF
+ free(passwd);
+#endif /* USE_LIBIAF */
+ if (locked) {
+ logit("User %.100s not allowed because account is locked",
pw->pw_name);
return 0;
}
}
-#else
- /* Shouldn't be called if pw is NULL, but better safe than sorry... */
- if (!pw || !pw->pw_name)
- return 0;
-#endif
/*
* Get the shell from the password data. An empty shell field is
/* deny if shell does not exists or is not executable */
if (stat(shell, &st) != 0) {
- log("User %.100s not allowed because shell %.100s does not exist",
+ logit("User %.100s not allowed because shell %.100s does not exist",
pw->pw_name, shell);
return 0;
}
if (S_ISREG(st.st_mode) == 0 ||
(st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
- log("User %.100s not allowed because shell %.100s is not executable",
+ logit("User %.100s not allowed because shell %.100s is not executable",
pw->pw_name, shell);
return 0;
}
- if (options.num_deny_users > 0 || options.num_allow_users > 0) {
- hostname = get_canonical_hostname(options.verify_reverse_mapping);
+ if (options.num_deny_users > 0 || options.num_allow_users > 0 ||
+ options.num_deny_groups > 0 || options.num_allow_groups > 0) {
+ hostname = get_canonical_hostname(options.use_dns);
ipaddr = get_remote_ipaddr();
}
for (i = 0; i < options.num_deny_users; i++)
if (match_user(pw->pw_name, hostname, ipaddr,
options.deny_users[i])) {
- log("User %.100s not allowed because listed in DenyUsers",
- pw->pw_name);
+ logit("User %.100s from %.100s not allowed "
+ "because listed in DenyUsers",
+ pw->pw_name, hostname);
return 0;
}
}
break;
/* i < options.num_allow_users iff we break for loop */
if (i >= options.num_allow_users) {
- log("User %.100s not allowed because not listed in AllowUsers",
- pw->pw_name);
+ logit("User %.100s from %.100s not allowed because "
+ "not listed in AllowUsers", pw->pw_name, hostname);
return 0;
}
}
if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
/* Get the user's group access list (primary and supplementary) */
if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
- log("User %.100s not allowed because not in any group",
- pw->pw_name);
+ logit("User %.100s from %.100s not allowed because "
+ "not in any group", pw->pw_name, hostname);
return 0;
}
if (ga_match(options.deny_groups,
options.num_deny_groups)) {
ga_free();
- log("User %.100s not allowed because a group is listed in DenyGroups",
- pw->pw_name);
+ logit("User %.100s from %.100s not allowed "
+ "because a group is listed in DenyGroups",
+ pw->pw_name, hostname);
return 0;
}
/*
if (!ga_match(options.allow_groups,
options.num_allow_groups)) {
ga_free();
- log("User %.100s not allowed because none of user's groups are listed in AllowGroups",
- pw->pw_name);
+ logit("User %.100s from %.100s not allowed "
+ "because none of user's groups are listed "
+ "in AllowGroups", pw->pw_name, hostname);
return 0;
}
ga_free();
}
-#ifdef WITH_AIXAUTHENTICATE
- /*
- * Don't check loginrestrictions() for root account (use
- * PermitRootLogin to control logins via ssh), or if running as
- * non-root user (since loginrestrictions will always fail).
- */
- if ( (pw->pw_uid != 0) && (geteuid() == 0) &&
- loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &loginmsg) != 0) {
- if (loginmsg && *loginmsg) {
- /* Remove embedded newlines (if any) */
- char *p;
- for (p = loginmsg; *p; p++) {
- if (*p == '\n')
- *p = ' ';
- }
- /* Remove trailing newline */
- *--p = '\0';
- log("Login restricted for %s: %.100s", pw->pw_name, loginmsg);
- }
+#ifdef CUSTOM_SYS_AUTH_ALLOWED_USER
+ if (!sys_auth_allowed_user(pw, &loginmsg))
return 0;
- }
-#endif /* WITH_AIXAUTHENTICATE */
+#endif
/* We found no reason not to let this user try to log on... */
return 1;
}
-Authctxt *
-authctxt_new(void)
-{
- Authctxt *authctxt = xmalloc(sizeof(*authctxt));
- memset(authctxt, 0, sizeof(*authctxt));
- return authctxt;
-}
-
void
auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
{
void (*authlog) (const char *fmt,...) = verbose;
char *authmsg;
+ if (use_privsep && !mm_is_monitor() && !authctxt->postponed)
+ return;
+
/* Raise logging level */
if (authenticated == 1 ||
!authctxt->valid ||
- authctxt->failures >= AUTH_FAIL_LOG ||
+ authctxt->failures >= options.max_authtries / 2 ||
strcmp(method, "password") == 0)
- authlog = log;
+ authlog = logit;
if (authctxt->postponed)
authmsg = "Postponed";
authlog("%s %s for %s%.100s from %.200s port %d%s",
authmsg,
method,
- authctxt->valid ? "" : "illegal user ",
+ authctxt->valid ? "" : "invalid user ",
authctxt->user,
get_remote_ipaddr(),
get_remote_port(),
info);
-#ifdef WITH_AIXAUTHENTICATE
- if (authenticated == 0 && strcmp(method, "password") == 0)
- loginfailed(authctxt->user,
- get_canonical_hostname(options.verify_reverse_mapping),
- "ssh");
-#endif /* WITH_AIXAUTHENTICATE */
-
+#ifdef CUSTOM_FAILED_LOGIN
+ if (authenticated == 0 && !authctxt->postponed &&
+ (strcmp(method, "password") == 0 ||
+ strncmp(method, "keyboard-interactive", 20) == 0 ||
+ strcmp(method, "challenge-response") == 0))
+ record_failed_login(authctxt->user,
+ get_canonical_hostname(options.use_dns), "ssh");
+# ifdef WITH_AIXAUTHENTICATE
+ if (authenticated)
+ sys_auth_record_login(authctxt->user,
+ get_canonical_hostname(options.use_dns), "ssh", &loginmsg);
+# endif
+#endif
+#ifdef SSH_AUDIT_EVENTS
+ if (authenticated == 0 && !authctxt->postponed)
+ audit_event(audit_classify_auth(method));
+#endif
}
/*
switch (options.permit_root_login) {
case PERMIT_YES:
return 1;
- break;
case PERMIT_NO_PASSWD:
if (strcmp(method, "password") != 0)
return 1;
break;
case PERMIT_FORCED_ONLY:
if (forced_command) {
- log("Root login accepted for forced command.");
+ logit("Root login accepted for forced command.");
return 1;
}
break;
}
- log("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr());
+ logit("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr());
return 0;
}
*
* This returns a buffer allocated by xmalloc.
*/
-char *
-expand_filename(const char *filename, struct passwd *pw)
+static char *
+expand_authorized_keys(const char *filename, struct passwd *pw)
{
- Buffer buffer;
- char *file;
- const char *cp;
+ char *file, ret[MAXPATHLEN];
+ int i;
- /*
- * Build the filename string in the buffer by making the appropriate
- * substitutions to the given file name.
- */
- buffer_init(&buffer);
- for (cp = filename; *cp; cp++) {
- if (cp[0] == '%' && cp[1] == '%') {
- buffer_append(&buffer, "%", 1);
- cp++;
- continue;
- }
- if (cp[0] == '%' && cp[1] == 'h') {
- buffer_append(&buffer, pw->pw_dir, strlen(pw->pw_dir));
- cp++;
- continue;
- }
- if (cp[0] == '%' && cp[1] == 'u') {
- buffer_append(&buffer, pw->pw_name,
- strlen(pw->pw_name));
- cp++;
- continue;
- }
- buffer_append(&buffer, cp, 1);
- }
- buffer_append(&buffer, "\0", 1);
+ file = percent_expand(filename, "h", pw->pw_dir,
+ "u", pw->pw_name, (char *)NULL);
/*
* Ensure that filename starts anchored. If not, be backward
* compatible and prepend the '%h/'
*/
- file = xmalloc(MAXPATHLEN);
- cp = buffer_ptr(&buffer);
- if (*cp != '/')
- snprintf(file, MAXPATHLEN, "%s/%s", pw->pw_dir, cp);
- else
- strlcpy(file, cp, MAXPATHLEN);
-
- buffer_free(&buffer);
- return file;
+ if (*file == '/')
+ return (file);
+
+ i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file);
+ if (i < 0 || (size_t)i >= sizeof(ret))
+ fatal("expand_authorized_keys: path too long");
+ xfree(file);
+ return (xstrdup(ret));
}
char *
authorized_keys_file(struct passwd *pw)
{
- return expand_filename(options.authorized_keys_file, pw);
+ return expand_authorized_keys(options.authorized_keys_file, pw);
}
char *
authorized_keys_file2(struct passwd *pw)
{
- return expand_filename(options.authorized_keys_file2, pw);
+ return expand_authorized_keys(options.authorized_keys_file2, pw);
}
/* return ok if key exists in sysfile or userfile */
(stat(user_hostfile, &st) == 0) &&
((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
(st.st_mode & 022) != 0)) {
- log("Authentication refused for %.100s: "
+ logit("Authentication refused for %.100s: "
"bad owner or modes for %.200s",
pw->pw_name, user_hostfile);
} else {
*
* Returns 0 on success and -1 on failure
*/
-int
+static int
secure_filename(FILE *f, const char *file, struct passwd *pw,
char *err, size_t errlen)
{
uid_t uid = pw->pw_uid;
char buf[MAXPATHLEN], homedir[MAXPATHLEN];
char *cp;
+ int comparehome = 0;
struct stat st;
if (realpath(file, buf) == NULL) {
strerror(errno));
return -1;
}
- if (realpath(pw->pw_dir, homedir) == NULL) {
- snprintf(err, errlen, "realpath %s failed: %s", pw->pw_dir,
- strerror(errno));
- return -1;
- }
+ if (realpath(pw->pw_dir, homedir) != NULL)
+ comparehome = 1;
/* check the open file to avoid races */
if (fstat(fileno(f), &st) < 0 ||
return -1;
}
- /* If are passed the homedir then we can stop */
- if (strcmp(homedir, buf) == 0) {
+ /* If are past the homedir then we can stop */
+ if (comparehome && strcmp(homedir, buf) == 0) {
debug3("secure_filename: terminating check at '%s'",
buf);
break;
return 0;
}
+FILE *
+auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
+{
+ char line[1024];
+ struct stat st;
+ int fd;
+ FILE *f;
+
+ /*
+ * Open the file containing the authorized keys
+ * Fail quietly if file does not exist
+ */
+ if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
+ if (errno != ENOENT)
+ debug("Could not open keyfile '%s': %s", file,
+ strerror(errno));
+ return NULL;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ close(fd);
+ return NULL;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ logit("User %s authorized keys %s is not a regular file",
+ pw->pw_name, file);
+ close(fd);
+ return NULL;
+ }
+ unset_nonblock(fd);
+ if ((f = fdopen(fd, "r")) == NULL) {
+ close(fd);
+ return NULL;
+ }
+ if (options.strict_modes &&
+ secure_filename(f, file, pw, line, sizeof(line)) != 0) {
+ fclose(f);
+ logit("Authentication refused: %s", line);
+ return NULL;
+ }
+
+ return f;
+}
+
struct passwd *
getpwnamallow(const char *user)
{
#endif
struct passwd *pw;
+ parse_server_match_config(&options, user,
+ get_canonical_hostname(options.use_dns), get_remote_ipaddr());
+
pw = getpwnam(user);
if (pw == NULL) {
- log("Illegal user %.100s from %.100s",
+ logit("Invalid user %.100s from %.100s",
user, get_remote_ipaddr());
+#ifdef CUSTOM_FAILED_LOGIN
+ record_failed_login(user,
+ get_canonical_hostname(options.use_dns), "ssh");
+#endif
+#ifdef SSH_AUDIT_EVENTS
+ audit_event(SSH_INVALID_USER);
+#endif /* SSH_AUDIT_EVENTS */
return (NULL);
}
if (!allowed_user(pw))
auth_debug_init = 1;
}
}
+
+struct passwd *
+fakepw(void)
+{
+ static struct passwd fake;
+
+ memset(&fake, 0, sizeof(fake));
+ fake.pw_name = "NOUSER";
+ fake.pw_passwd =
+ "$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK";
+ fake.pw_gecos = "NOUSER";
+ fake.pw_uid = privsep_pw == NULL ? (uid_t)-1 : privsep_pw->pw_uid;
+ fake.pw_gid = privsep_pw == NULL ? (gid_t)-1 : privsep_pw->pw_gid;
+#ifdef HAVE_PW_CLASS_IN_PASSWD
+ fake.pw_class = "";
+#endif
+ fake.pw_dir = "/nonexist";
+ fake.pw_shell = "/nonexist";
+
+ return (&fake);
+}