--- /dev/null
+diff -up openssh-6.1p1/auth2-pubkey.c.akc openssh-6.1p1/auth2-pubkey.c
+--- openssh-6.1p1/auth2-pubkey.c.akc 2012-11-28 17:12:43.238524384 +0100
++++ openssh-6.1p1/auth2-pubkey.c 2012-11-28 17:12:43.263524297 +0100
+@@ -27,9 +27,13 @@
+
+ #include <sys/types.h>
+ #include <sys/stat.h>
++#include <sys/wait.h>
+
++#include <errno.h>
+ #include <fcntl.h>
++#include <paths.h>
+ #include <pwd.h>
++#include <signal.h>
+ #include <stdio.h>
+ #include <stdarg.h>
+ #include <string.h>
+@@ -260,7 +264,7 @@ match_principals_file(char *file, struct
+ 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;
+@@ -273,31 +277,22 @@ match_principals_file(char *file, struct
+ 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);
+
+@@ -390,8 +385,6 @@ user_key_allowed2(struct passwd *pw, Key
+ break;
+ }
+ }
+- restore_uid();
+- fclose(f);
+ key_free(found);
+ if (!found_key)
+ debug2("key not found");
+@@ -453,7 +446,173 @@ user_cert_trusted_ca(struct passwd *pw,
+ 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 errmsg[512];
++
++ if (options.authorized_keys_command == NULL ||
++ options.authorized_keys_command[0] != '/')
++ return 0;
++
++ /* If no user specified to run commands the default to target user */
++ if (options.authorized_keys_command_user == NULL)
++ pw = user_pw;
++ else {
++ pw = getpwnam(options.authorized_keys_command_user);
++ if (pw == NULL) {
++ error("AuthorizedKeyCommandUser \"%s\" not found: %s",
++ options.authorized_keys_command, strerror(errno));
++ return 0;
++ }
++ }
++
++ 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;
++ }
++
++ /* open the pipe and read the keys */
++ if (pipe(p) != 0) {
++ error("%s: pipe: %s", __func__, strerror(errno));
++ goto out;
++ }
++
++ debug3("Running AuthorizedKeysCommand: \"%s\" as \"%s\"",
++ options.authorized_keys_command, 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);
++
++ /* 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);
++ }
++
++ close(p[0]);
++ if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
++ error("%s: open %s: %s", __func__, _PATH_DEVNULL,
++ strerror(errno));
++ _exit(1);
++ }
++ if (dup2(devnull, STDIN_FILENO) == -1 ||
++ dup2(p[1], STDOUT_FILENO) == -1 ||
++ dup2(devnull, STDERR_FILENO) == -1) {
++ error("%s: dup2: %s", __func__, strerror(errno));
++ _exit(1);
++ }
++ closefrom(STDERR_FILENO + 1);
++
++ execl(options.authorized_keys_command,
++ options.authorized_keys_command, 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 */
++ 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)
+ {
+@@ -469,6 +628,10 @@ user_key_allowed(struct passwd *pw, 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++) {
+ file = expand_authorized_keys(
+ options.authorized_keys_files[i], pw);
+diff -up openssh-6.1p1/auth.c.akc openssh-6.1p1/auth.c
+--- openssh-6.1p1/auth.c.akc 2012-11-28 17:12:43.187524558 +0100
++++ openssh-6.1p1/auth.c 2012-11-28 17:12:43.263524297 +0100
+@@ -411,39 +411,41 @@ check_key_in_hostfiles(struct passwd *pw
+
+
+ /*
+- * 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 an the 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 *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 ((stp->st_uid != 0 && stp->st_uid != uid) ||
++ (stp->st_mode & 022) != 0) {
+ snprintf(err, errlen, "bad ownership or modes for file %s",
+ buf);
+ return -1;
+@@ -479,6 +481,31 @@ secure_filename(FILE *f, const char *fil
+ 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)
++{
++ uid_t uid = pw->pw_uid;
++ char buf[MAXPATHLEN], homedir[MAXPATHLEN];
++ char *cp;
++ int comparehome = 0;
++ struct stat st;
++
++ /* check the open file to avoid races */
++ if (fstat(fileno(f), &st) < 0) {
++ snprintf(err, errlen, "cannot stat file %s: %s",
++ buf, 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)
+diff -up openssh-6.1p1/auth.h.akc openssh-6.1p1/auth.h
+--- openssh-6.1p1/auth.h.akc 2012-11-28 17:12:43.239524381 +0100
++++ openssh-6.1p1/auth.h 2012-11-28 17:12:43.263524297 +0100
+@@ -125,6 +125,10 @@ int auth_rhosts_rsa_key_allowed(struct
+ int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
+ int user_key_allowed(struct passwd *, Key *);
+
++struct stat;
++int auth_secure_path(const char *, struct stat *, const char *, uid_t,
++ char *, size_t);
++
+ #ifdef KRB5
+ int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *);
+ int auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt);
+diff -up openssh-6.1p1/servconf.c.akc openssh-6.1p1/servconf.c
+--- openssh-6.1p1/servconf.c.akc 2012-11-28 17:12:43.198524521 +0100
++++ openssh-6.1p1/servconf.c 2012-11-28 17:14:50.314005026 +0100
+@@ -137,6 +137,8 @@ initialize_server_options(ServerOptions
+ options->num_permitted_opens = -1;
+ options->adm_forced_command = NULL;
+ options->chroot_directory = NULL;
++ options->authorized_keys_command = NULL;
++ options->authorized_keys_command_user = NULL;
+ options->zero_knowledge_password_authentication = -1;
+ options->revoked_keys_file = NULL;
+ options->trusted_user_ca_keys = NULL;
+@@ -331,6 +333,7 @@ typedef enum {
+ sZeroKnowledgePasswordAuthentication, sHostCertificate,
+ sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
+ sKexAlgorithms, sIPQoS, sVersionAddendum,
++ sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
+ sAuthenticationMethods,
+ sDeprecated, sUnsupported
+ } ServerOpCodes;
+@@ -457,6 +460,9 @@ static struct {
+ { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
+ { "ipqos", sIPQoS, SSHCFG_ALL },
+ { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
++ { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
++ { "authorizedkeyscommandrunas", sAuthorizedKeysCommandUser, SSHCFG_ALL },
++ { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
+ { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
+ { NULL, sBadOption, 0 }
+ };
+@@ -1520,6 +1526,26 @@ process_server_config_line(ServerOptions
+ }
+ return 0;
+
++ case sAuthorizedKeysCommand:
++ len = strspn(cp, WHITESPACE);
++ if (*activep && options->authorized_keys_command == NULL) {
++ options->authorized_keys_command = xstrdup(cp + len);
++ if (*options->authorized_keys_command != '/') {
++ fatal("%.200s line %d: AuthorizedKeysCommand "
++ "must be an absolute path",
++ filename, linenum);
++ }
++ }
++ return 0;
++
++ case sAuthorizedKeysCommandUser:
++ charptr = &options->authorized_keys_command_user;
++
++ arg = strdelim(&cp);
++ if (*activep && *charptr == NULL)
++ *charptr = xstrdup(arg);
++ break;
++
+ case sDeprecated:
+ logit("%s line %d: Deprecated option %s",
+ filename, linenum, arg);
+@@ -1670,6 +1696,8 @@ copy_set_server_options(ServerOptions *d
+ M_CP_INTOPT(hostbased_uses_name_from_packet_only);
+ M_CP_INTOPT(kbd_interactive_authentication);
+ M_CP_INTOPT(zero_knowledge_password_authentication);
++ M_CP_STROPT(authorized_keys_command);
++ M_CP_STROPT(authorized_keys_command_user);
+ M_CP_INTOPT(permit_root_login);
+ M_CP_INTOPT(permit_empty_passwd);
+
+@@ -1930,6 +1958,8 @@ dump_config(ServerOptions *o)
+ dump_cfg_string(sAuthorizedPrincipalsFile,
+ o->authorized_principals_file);
+ dump_cfg_string(sVersionAddendum, o->version_addendum);
++ dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
++ dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user);
+
+ /* string arguments requiring a lookup */
+ dump_cfg_string(sLogLevel, log_level_name(o->log_level));
+diff -up openssh-6.1p1/servconf.h.akc openssh-6.1p1/servconf.h
+--- openssh-6.1p1/servconf.h.akc 2012-11-28 17:12:43.000000000 +0100
++++ openssh-6.1p1/servconf.h 2012-11-28 17:18:41.217055157 +0100
+@@ -167,6 +167,8 @@ typedef struct {
+ char *revoked_keys_file;
+ char *trusted_user_ca_keys;
+ char *authorized_principals_file;
++ char *authorized_keys_command;
++ char *authorized_keys_command_user;
+
+ char *version_addendum; /* Appended to SSH banner */
+
+diff -up openssh-6.1p1/sshd.c.akc openssh-6.1p1/sshd.c
+--- openssh-6.1p1/sshd.c.akc 2012-11-28 17:12:43.245524360 +0100
++++ openssh-6.1p1/sshd.c 2012-11-28 17:12:43.265524291 +0100
+@@ -366,9 +366,20 @@ main_sigchld_handler(int sig)
+ static void
+ grace_alarm_handler(int sig)
+ {
++ pid_t pgid;
++
+ if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0)
+ kill(pmonitor->m_pid, SIGALRM);
+
++ /*
++ * Try to kill any processes that we have spawned, E.g. authorized
++ * keys command helpers.
++ */
++ if ((pgid = getpgid(0)) == getpid()) {
++ signal(SIGTERM, SIG_IGN);
++ killpg(pgid, SIGTERM);
++ }
++
+ /* Log error and exit. */
+ sigdie("Timeout before authentication for %s", get_remote_ipaddr());
+ }
+diff -up openssh-6.1p1/sshd_config.0.akc openssh-6.1p1/sshd_config.0
+--- openssh-6.1p1/sshd_config.0.akc 2012-08-29 02:53:04.000000000 +0200
++++ openssh-6.1p1/sshd_config.0 2012-11-28 17:12:43.265524291 +0100
+@@ -71,6 +71,23 @@ DESCRIPTION
+
+ See PATTERNS in ssh_config(5) for more information on patterns.
+
++ AuthorizedKeysCommand
++
++ Specifies a program to be used for lookup of the user's
++ public keys. The program will be invoked with its first
++ argument the name of the user being authorized, and should produce
++ on standard output AuthorizedKeys lines (see AUTHORIZED_KEYS
++ in sshd(8)). By default (or when set to the empty string) there is no
++ AuthorizedKeysCommand run. If the AuthorizedKeysCommand does not successfully
++ authorize the user, authorization falls through to the
++ AuthorizedKeysFile. Note that this option has an effect
++ only with PubkeyAuthentication turned on.
++
++ AuthorizedKeysCommandRunAs
++ Specifies the user under whose account the AuthorizedKeysCommand is run.
++ Empty string (the default value) means the user being authorized
++ is used.
++
+ AuthorizedKeysFile
+ Specifies the file that contains the public keys that can be used
+ for user authentication. The format is described in the
+@@ -402,7 +419,8 @@ DESCRIPTION
+ Only a subset of keywords may be used on the lines following a
+ Match keyword. Available keywords are AcceptEnv,
+ AllowAgentForwarding, AllowGroups, AllowTcpForwarding,
+- AllowUsers, AuthorizedKeysFile, AuthorizedPrincipalsFile, Banner,
++ AllowUsers, AuthorizedKeysFile, AuthorizedKeysCommand,
++ AuthorizedKeysCommandRunAs, AuthorizedPrincipalsFile, Banner,
+ ChrootDirectory, DenyGroups, DenyUsers, ForceCommand,
+ GatewayPorts, GSSAPIAuthentication, HostbasedAuthentication,
+ HostbasedUsesNameFromPacketOnly, KbdInteractiveAuthentication,
+diff -up openssh-6.1p1/sshd_config.5.akc openssh-6.1p1/sshd_config.5
+--- openssh-6.1p1/sshd_config.5.akc 2012-11-28 17:12:43.199524517 +0100
++++ openssh-6.1p1/sshd_config.5 2012-11-28 17:16:23.736624980 +0100
+@@ -173,6 +173,20 @@ Note that each authentication method lis
+ in the configuration.
+ The default is not to require multiple authentication; successful completion
+ of a single authentication method is sufficient.
++.It Cm AuthorizedKeysCommand
++Specifies a program to be used for lookup of the user's public keys.
++The program will be invoked with a single argument of the username
++being authenticated, and should produce on standard output zero or
++more lines of authorized_keys output (see AUTHORIZED_KEYS in
++.Xr sshd 8 )
++If a key supplied by AuthorizedKeysCommand does not successfully authenticate
++and authorize the user then public key authentication continues using the usual
++.Cm AuthorizedKeysFile
++files.
++By default, no AuthorizedKeysCommand is run.
++.It Cm AuthorizedKeysCommandUser
++Specifies the user under whose account the AuthorizedKeysCommand is run.
++The default is the user being authenticated.
+ .It Cm AuthorizedKeysFile
+ Specifies the file that contains the public keys that can be used
+ for user authentication.
+@@ -734,6 +748,8 @@ Available keywords are
+ .Cm AllowTcpForwarding ,
+ .Cm AllowUsers ,
+ .Cm AuthenticationMethods ,
++.Cm AuthorizedKeysCommand ,
++.Cm AuthorizedKeysCommandUser ,
+ .Cm AuthorizedKeysFile ,
+ .Cm AuthorizedPrincipalsFile ,
+ .Cm Banner ,
+@@ -749,6 +765,7 @@ Available keywords are
+ .Cm KerberosAuthentication ,
+ .Cm MaxAuthTries ,
+ .Cm MaxSessions ,
++.Cm PubkeyAuthentication ,
+ .Cm PasswordAuthentication ,
+ .Cm PermitEmptyPasswords ,
+ .Cm PermitOpen ,
+diff -up openssh-6.1p1/sshd_config.akc openssh-6.1p1/sshd_config
+--- openssh-6.1p1/sshd_config.akc 2012-07-31 04:21:34.000000000 +0200
++++ openssh-6.1p1/sshd_config 2012-11-28 17:12:43.265524291 +0100
+@@ -49,6 +49,9 @@
+ # but this is overridden so installations will only check .ssh/authorized_keys
+ AuthorizedKeysFile .ssh/authorized_keys
+
++#AuthorizedKeysCommand none
++#AuthorizedKeysCommandUser nobody
++
+ #AuthorizedPrincipalsFile none
+
+ # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts