Imported Upstream version 6.2p2
[platform/upstream/openssh.git] / auth2-pubkey.c
index 5bccb5d..3ff6faa 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-pubkey.c,v 1.30 2011/09/25 05:44:47 djm Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.34 2013/02/14 21:35:59 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/wait.h>
 
+#include <errno.h>
 #include <fcntl.h>
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
 #include <pwd.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
@@ -240,7 +246,7 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert)
                        if (strcmp(cp, cert->principals[i]) == 0) {
                                debug3("matched principal \"%.100s\" "
                                    "from file \"%s\" on line %lu",
-                                   cert->principals[i], file, linenum);
+                                   cert->principals[i], file, linenum);
                                if (auth_parse_options(pw, line_opts,
                                    file, linenum) != 1)
                                        continue;
@@ -253,31 +259,22 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert)
        fclose(f);
        restore_uid();
        return 0;
-}      
+}
 
-/* return 1 if user allows given key */
+/*
+ * Checks whether key is allowed in authorized_keys-format file,
+ * returns 1 if the key is allowed or 0 otherwise.
+ */
 static int
-user_key_allowed2(struct passwd *pw, Key *key, char *file)
+check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw)
 {
        char line[SSH_MAX_PUBKEY_BYTES];
        const char *reason;
        int found_key = 0;
-       FILE *f;
        u_long linenum = 0;
        Key *found;
        char *fp;
 
-       /* Temporarily use the user's uid. */
-       temporarily_use_uid(pw);
-
-       debug("trying public key file %s", file);
-       f = auth_openkeyfile(file, pw, options.strict_modes);
-
-       if (!f) {
-               restore_uid();
-               return 0;
-       }
-
        found_key = 0;
        found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
 
@@ -370,8 +367,6 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file)
                        break;
                }
        }
-       restore_uid();
-       fclose(f);
        key_free(found);
        if (!found_key)
                debug2("key not found");
@@ -433,7 +428,180 @@ user_cert_trusted_ca(struct passwd *pw, Key *key)
        return ret;
 }
 
-/* check whether given key is in .ssh/authorized_keys* */
+/*
+ * Checks whether key is allowed in file.
+ * returns 1 if the key is allowed or 0 otherwise.
+ */
+static int
+user_key_allowed2(struct passwd *pw, Key *key, char *file)
+{
+       FILE *f;
+       int found_key = 0;
+
+       /* Temporarily use the user's uid. */
+       temporarily_use_uid(pw);
+
+       debug("trying public key file %s", file);
+       if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
+               found_key = check_authkeys_file(f, file, key, pw);
+               fclose(f);
+       }
+
+       restore_uid();
+       return found_key;
+}
+
+/*
+ * Checks whether key is allowed in output of command.
+ * returns 1 if the key is allowed or 0 otherwise.
+ */
+static int
+user_key_command_allowed2(struct passwd *user_pw, Key *key)
+{
+       FILE *f;
+       int ok, found_key = 0;
+       struct passwd *pw;
+       struct stat st;
+       int status, devnull, p[2], i;
+       pid_t pid;
+       char *username, errmsg[512];
+
+       if (options.authorized_keys_command == NULL ||
+           options.authorized_keys_command[0] != '/')
+               return 0;
+
+       if (options.authorized_keys_command_user == NULL) {
+               error("No user for AuthorizedKeysCommand specified, skipping");
+               return 0;
+       }
+
+       username = percent_expand(options.authorized_keys_command_user,
+           "u", user_pw->pw_name, (char *)NULL);
+       pw = getpwnam(username);
+       if (pw == NULL) {
+               error("AuthorizedKeysCommandUser \"%s\" not found: %s",
+                   username, strerror(errno));
+               free(username);
+               return 0;
+       }
+       free(username);
+
+       temporarily_use_uid(pw);
+
+       if (stat(options.authorized_keys_command, &st) < 0) {
+               error("Could not stat AuthorizedKeysCommand \"%s\": %s",
+                   options.authorized_keys_command, strerror(errno));
+               goto out;
+       }
+       if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0,
+           errmsg, sizeof(errmsg)) != 0) {
+               error("Unsafe AuthorizedKeysCommand: %s", errmsg);
+               goto out;
+       }
+
+       if (pipe(p) != 0) {
+               error("%s: pipe: %s", __func__, strerror(errno));
+               goto out;
+       }
+
+       debug3("Running AuthorizedKeysCommand: \"%s %s\" as \"%s\"",
+           options.authorized_keys_command, user_pw->pw_name, pw->pw_name);
+
+       /*
+        * Don't want to call this in the child, where it can fatal() and
+        * run cleanup_exit() code.
+        */
+       restore_uid();
+
+       switch ((pid = fork())) {
+       case -1: /* error */
+               error("%s: fork: %s", __func__, strerror(errno));
+               close(p[0]);
+               close(p[1]);
+               return 0;
+       case 0: /* child */
+               for (i = 0; i < NSIG; i++)
+                       signal(i, SIG_DFL);
+
+               if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
+                       error("%s: open %s: %s", __func__, _PATH_DEVNULL,
+                           strerror(errno));
+                       _exit(1);
+               }
+               /* Keep stderr around a while longer to catch errors */
+               if (dup2(devnull, STDIN_FILENO) == -1 ||
+                   dup2(p[1], STDOUT_FILENO) == -1) {
+                       error("%s: dup2: %s", __func__, strerror(errno));
+                       _exit(1);
+               }
+               closefrom(STDERR_FILENO + 1);
+
+               /* Don't use permanently_set_uid() here to avoid fatal() */
+               if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
+                       error("setresgid %u: %s", (u_int)pw->pw_gid,
+                           strerror(errno));
+                       _exit(1);
+               }
+               if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
+                       error("setresuid %u: %s", (u_int)pw->pw_uid,
+                           strerror(errno));
+                       _exit(1);
+               }
+               /* stdin is pointed to /dev/null at this point */
+               if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
+                       error("%s: dup2: %s", __func__, strerror(errno));
+                       _exit(1);
+               }
+
+               execl(options.authorized_keys_command,
+                   options.authorized_keys_command, user_pw->pw_name, NULL);
+
+               error("AuthorizedKeysCommand %s exec failed: %s",
+                   options.authorized_keys_command, strerror(errno));
+               _exit(127);
+       default: /* parent */
+               break;
+       }
+
+       temporarily_use_uid(pw);
+
+       close(p[1]);
+       if ((f = fdopen(p[0], "r")) == NULL) {
+               error("%s: fdopen: %s", __func__, strerror(errno));
+               close(p[0]);
+               /* Don't leave zombie child */
+               kill(pid, SIGTERM);
+               while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
+                       ;
+               goto out;
+       }
+       ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
+       fclose(f);
+
+       while (waitpid(pid, &status, 0) == -1) {
+               if (errno != EINTR) {
+                       error("%s: waitpid: %s", __func__, strerror(errno));
+                       goto out;
+               }
+       }
+       if (WIFSIGNALED(status)) {
+               error("AuthorizedKeysCommand %s exited on signal %d",
+                   options.authorized_keys_command, WTERMSIG(status));
+               goto out;
+       } else if (WEXITSTATUS(status) != 0) {
+               error("AuthorizedKeysCommand %s returned status %d",
+                   options.authorized_keys_command, WEXITSTATUS(status));
+               goto out;
+       }
+       found_key = ok;
+ out:
+       restore_uid();
+       return found_key;
+}
+
+/*
+ * Check whether key authenticates and authorises the user.
+ */
 int
 user_key_allowed(struct passwd *pw, Key *key)
 {
@@ -449,9 +617,17 @@ user_key_allowed(struct passwd *pw, Key *key)
        if (success)
                return success;
 
+       success = user_key_command_allowed2(pw, key);
+       if (success > 0)
+               return success;
+
        for (i = 0; !success && i < options.num_authkeys_files; i++) {
+
+               if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
+                       continue;
                file = expand_authorized_keys(
                    options.authorized_keys_files[i], pw);
+
                success = user_key_allowed2(pw, key, file);
                xfree(file);
        }