OSDN Git Service

openssh-7.1p2
[android-x86/external-openssh.git] / auth.c
diff --git a/auth.c b/auth.c
index cd95da9..65f3d0d 100644 (file)
--- a/auth.c
+++ b/auth.c
@@ -1,4 +1,4 @@
-/* $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.
  *
@@ -27,7 +27,6 @@
 
 #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"
@@ -63,7 +64,6 @@
 #include "auth-options.h"
 #include "canohost.h"
 #include "uidswap.h"
-#include "misc.h"
 #include "packet.h"
 #include "loginrec.h"
 #ifdef GSSAPI
@@ -71,6 +71,9 @@
 #endif
 #include "authfile.h"
 #include "monitor_wrap.h"
+#include "authfile.h"
+#include "ssherr.h"
+#include "compat.h"
 
 /* import */
 extern ServerOptions options;
@@ -164,17 +167,17 @@ allowed_user(struct passwd * pw)
                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 ||
@@ -251,7 +254,25 @@ allowed_user(struct passwd * pw)
 }
 
 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;
@@ -268,17 +289,24 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
 
        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 &&
@@ -299,17 +327,34 @@ auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
 #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:
@@ -334,7 +379,7 @@ auth_root_allowed(char *method)
 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,
@@ -350,7 +395,7 @@ expand_authorized_keys(const char *filename, struct passwd *pw)
        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));
 }
 
@@ -391,7 +436,7 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
                        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)
@@ -408,44 +453,51 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
        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 */
@@ -456,13 +508,16 @@ secure_filename(FILE *f, const char *file, struct passwd *pw,
                }
                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)
@@ -478,6 +533,27 @@ secure_filename(FILE *f, const char *file, struct passwd *pw,
        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)
@@ -544,14 +620,23 @@ getpwnamallow(const char *user)
 #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)
@@ -608,29 +693,39 @@ getpwnamallow(const char *user)
 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
@@ -658,7 +753,7 @@ auth_debug_send(void)
        while (buffer_len(&auth_debug)) {
                msg = buffer_get_string(&auth_debug, NULL);
                packet_send_debug("%s", msg);
-               xfree(msg);
+               free(msg);
        }
 }
 
@@ -682,10 +777,12 @@ fakepw(void)
        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";