-/* $OpenBSD: auth.c,v 1.94 2011/05/23 03:33:38 djm Exp $ */
+/* $OpenBSD: auth.c,v 1.113 2015/08/21 03:42:19 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/param.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <limits.h>
#include "xmalloc.h"
#include "match.h"
#include "groupaccess.h"
#include "log.h"
#include "buffer.h"
+#include "misc.h"
#include "servconf.h"
#include "key.h"
#include "hostfile.h"
#include "auth-options.h"
#include "canohost.h"
#include "uidswap.h"
-#include "misc.h"
#include "packet.h"
#include "loginrec.h"
#ifdef GSSAPI
#endif
#include "authfile.h"
#include "monitor_wrap.h"
+#include "authfile.h"
+#include "ssherr.h"
+#include "compat.h"
/* import */
extern ServerOptions options;
if (stat(shell, &st) != 0) {
logit("User %.100s not allowed because shell %.100s "
"does not exist", pw->pw_name, shell);
- xfree(shell);
+ free(shell);
return 0;
}
if (S_ISREG(st.st_mode) == 0 ||
(st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
logit("User %.100s not allowed because shell %.100s "
"is not executable", pw->pw_name, shell);
- xfree(shell);
+ free(shell);
return 0;
}
- xfree(shell);
+ free(shell);
}
if (options.num_deny_users > 0 || options.num_allow_users > 0 ||
}
void
-auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
+auth_info(Authctxt *authctxt, const char *fmt, ...)
+{
+ va_list ap;
+ int i;
+
+ free(authctxt->info);
+ authctxt->info = NULL;
+
+ va_start(ap, fmt);
+ i = vasprintf(&authctxt->info, fmt, ap);
+ va_end(ap);
+
+ if (i < 0 || authctxt->info == NULL)
+ fatal("vasprintf failed");
+}
+
+void
+auth_log(Authctxt *authctxt, int authenticated, int partial,
+ const char *method, const char *submethod)
{
void (*authlog) (const char *fmt,...) = verbose;
char *authmsg;
if (authctxt->postponed)
authmsg = "Postponed";
+ else if (partial)
+ authmsg = "Partial";
else
authmsg = authenticated ? "Accepted" : "Failed";
- authlog("%s %s for %s%.100s from %.200s port %d%s",
+ authlog("%s %s%s%s for %s%.100s from %.200s port %d %s%s%s",
authmsg,
method,
+ submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod,
authctxt->valid ? "" : "invalid user ",
authctxt->user,
get_remote_ipaddr(),
get_remote_port(),
- info);
+ compat20 ? "ssh2" : "ssh1",
+ authctxt->info != NULL ? ": " : "",
+ authctxt->info != NULL ? authctxt->info : "");
+ free(authctxt->info);
+ authctxt->info = NULL;
#ifdef CUSTOM_FAILED_LOGIN
if (authenticated == 0 && !authctxt->postponed &&
#endif
}
+
+void
+auth_maxtries_exceeded(Authctxt *authctxt)
+{
+ error("maximum authentication attempts exceeded for "
+ "%s%.100s from %.200s port %d %s",
+ authctxt->valid ? "" : "invalid user ",
+ authctxt->user,
+ get_remote_ipaddr(),
+ get_remote_port(),
+ compat20 ? "ssh2" : "ssh1");
+ packet_disconnect("Too many authentication failures");
+ /* NOTREACHED */
+}
+
/*
* Check whether root logins are disallowed.
*/
int
-auth_root_allowed(char *method)
+auth_root_allowed(const char *method)
{
switch (options.permit_root_login) {
case PERMIT_YES:
return 1;
case PERMIT_NO_PASSWD:
- if (strcmp(method, "password") != 0)
+ if (strcmp(method, "publickey") == 0 ||
+ strcmp(method, "hostbased") == 0 ||
+ strcmp(method, "gssapi-with-mic") == 0)
return 1;
break;
case PERMIT_FORCED_ONLY:
char *
expand_authorized_keys(const char *filename, struct passwd *pw)
{
- char *file, ret[MAXPATHLEN];
+ char *file, ret[PATH_MAX];
int i;
file = percent_expand(filename, "h", pw->pw_dir,
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);
+ free(file);
return (xstrdup(ret));
}
load_hostkeys(hostkeys, host, user_hostfile);
restore_uid();
}
- xfree(user_hostfile);
+ free(user_hostfile);
}
host_status = check_key_in_hostkeys(hostkeys, key, &found);
if (host_status == HOST_REVOKED)
return host_status;
}
-
/*
- * Check a given file for security. This is defined as all components
+ * Check a given path for security. This is defined as all components
* of the path to the file must be owned by either the owner of
* of the file or root and no directories must be group or world writable.
*
* XXX Should any specific check be done for sym links ?
*
- * Takes an open file descriptor, the file name, a uid and and
+ * Takes a file name, its stat information (preferably from fstat() to
+ * avoid races), the uid of the expected owner, their home directory and an
* error buffer plus max size as arguments.
*
* Returns 0 on success and -1 on failure
*/
-static int
-secure_filename(FILE *f, const char *file, struct passwd *pw,
- char *err, size_t errlen)
+int
+auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
+ uid_t uid, char *err, size_t errlen)
{
- uid_t uid = pw->pw_uid;
- char buf[MAXPATHLEN], homedir[MAXPATHLEN];
+ char buf[PATH_MAX], homedir[PATH_MAX];
char *cp;
int comparehome = 0;
struct stat st;
- if (realpath(file, buf) == NULL) {
- snprintf(err, errlen, "realpath %s failed: %s", file,
+ if (realpath(name, buf) == NULL) {
+ snprintf(err, errlen, "realpath %s failed: %s", name,
strerror(errno));
return -1;
}
- if (realpath(pw->pw_dir, homedir) != NULL)
+ if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL)
comparehome = 1;
- /* check the open file to avoid races */
- if (fstat(fileno(f), &st) < 0 ||
- (st.st_uid != 0 && st.st_uid != uid) ||
- (st.st_mode & 022) != 0) {
+ if (!S_ISREG(stp->st_mode)) {
+ snprintf(err, errlen, "%s is not a regular file", buf);
+ return -1;
+ }
+ if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) ||
+ (stp->st_mode & 022) != 0) {
+#if defined(ANDROID)
+ /* needed to allow root login on Android. */
+ if (getuid() != 0)
+#endif
+ {
snprintf(err, errlen, "bad ownership or modes for file %s",
buf);
return -1;
+ }
}
/* for each component of the canonical path, walking upwards */
}
strlcpy(buf, cp, sizeof(buf));
+#if !defined(ANDROID)
+ /* /data is owned by system user, which causes this check to fail */
if (stat(buf, &st) < 0 ||
- (st.st_uid != 0 && st.st_uid != uid) ||
+ (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) ||
(st.st_mode & 022) != 0) {
snprintf(err, errlen,
"bad ownership or modes for directory %s", buf);
return -1;
}
+#endif
/* If are past the homedir then we can stop */
if (comparehome && strcmp(homedir, buf) == 0)
return 0;
}
+/*
+ * Version of secure_path() that accepts an open file descriptor to
+ * avoid races.
+ *
+ * Returns 0 on success and -1 on failure
+ */
+static int
+secure_filename(FILE *f, const char *file, struct passwd *pw,
+ char *err, size_t errlen)
+{
+ struct stat st;
+
+ /* check the open file to avoid races */
+ if (fstat(fileno(f), &st) < 0) {
+ snprintf(err, errlen, "cannot stat file %s: %s",
+ file, strerror(errno));
+ return -1;
+ }
+ return auth_secure_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen);
+}
+
static FILE *
auth_openfile(const char *file, struct passwd *pw, int strict_modes,
int log_missing, char *file_type)
#endif
#endif
struct passwd *pw;
+ struct connection_info *ci = get_connection_info(1, options.use_dns);
- parse_server_match_config(&options, user,
- get_canonical_hostname(options.use_dns), get_remote_ipaddr());
+ ci->user = user;
+ parse_server_match_config(&options, ci);
#if defined(_AIX) && defined(HAVE_SETAUTHDB)
aix_setauthdb(user);
#endif
+#ifdef ANDROID_GCE
+ // Android has a fixed set of users. Any incoming user that we can't
+ // identify should be authenticated as the shell user.
+ if (strcmp(user, "root") && strcmp(user, "shell")) {
+ logit("Login name %.100s forced to shell", user);
+ user = "shell";
+ }
+#endif
pw = getpwnam(user);
#if defined(_AIX) && defined(HAVE_SETAUTHDB)
int
auth_key_is_revoked(Key *key)
{
- char *key_fp;
+ char *fp = NULL;
+ int r;
if (options.revoked_keys_file == NULL)
return 0;
+ if ((fp = sshkey_fingerprint(key, options.fingerprint_hash,
+ SSH_FP_DEFAULT)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ error("%s: fingerprint key: %s", __func__, ssh_err(r));
+ goto out;
+ }
- switch (key_in_file(key, options.revoked_keys_file, 0)) {
+ r = sshkey_check_revoked(key, options.revoked_keys_file);
+ switch (r) {
case 0:
- /* key not revoked */
- return 0;
- case -1:
- /* Error opening revoked_keys_file: refuse all keys */
- error("Revoked keys file is unreadable: refusing public key "
- "authentication");
- return 1;
- case 1:
- /* Key revoked */
- key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
- error("WARNING: authentication attempt with a revoked "
- "%s key %s ", key_type(key), key_fp);
- xfree(key_fp);
- return 1;
+ break; /* not revoked */
+ case SSH_ERR_KEY_REVOKED:
+ error("Authentication key %s %s revoked by file %s",
+ sshkey_type(key), fp, options.revoked_keys_file);
+ goto out;
+ default:
+ error("Error checking authentication key %s %s in "
+ "revoked keys file %s: %s", sshkey_type(key), fp,
+ options.revoked_keys_file, ssh_err(r));
+ goto out;
}
- fatal("key_in_file returned junk");
+
+ /* Success */
+ r = 0;
+
+ out:
+ free(fp);
+ return r == 0 ? 0 : 1;
}
void
while (buffer_len(&auth_debug)) {
msg = buffer_get_string(&auth_debug, NULL);
packet_send_debug("%s", msg);
- xfree(msg);
+ free(msg);
}
}
fake.pw_name = "NOUSER";
fake.pw_passwd =
"$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK";
+#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
fake.pw_gecos = "NOUSER";
+#endif
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
+#ifdef HAVE_STRUCT_PASSWD_PW_CLASS
fake.pw_class = "";
#endif
fake.pw_dir = "/nonexist";