9fe1afc
diff -up openssh-6.1p1/auth2-pubkey.c.akc openssh-6.1p1/auth2-pubkey.c
5c8cd31
--- openssh-6.1p1/auth2-pubkey.c.akc	2013-02-14 17:46:45.259546968 +0100
5c8cd31
+++ openssh-6.1p1/auth2-pubkey.c	2013-02-14 17:48:19.072137541 +0100
bach f7f8b48
@@ -27,9 +27,13 @@
Jan F. Chadima 69dd72f
 
Jan F. Chadima 69dd72f
 #include <sys/types.h>
Jan F. Chadima 69dd72f
 #include <sys/stat.h>
Jan F. Chadima 69dd72f
+#include <sys/wait.h>
Jan F. Chadima 69dd72f
 
bach f7f8b48
+#include <errno.h>
Jan F. Chadima 69dd72f
 #include <fcntl.h>
bach f7f8b48
+#include <paths.h>
Jan F. Chadima 69dd72f
 #include <pwd.h>
bach f7f8b48
+#include <signal.h>
bach f7f8b48
 #include <stdio.h>
bach f7f8b48
 #include <stdarg.h>
bach f7f8b48
 #include <string.h>
bach f7f8b48
@@ -260,7 +264,7 @@ match_principals_file(char *file, struct
bach f7f8b48
 			if (strcmp(cp, cert->principals[i]) == 0) {
bach f7f8b48
 				debug3("matched principal \"%.100s\" "
bach f7f8b48
 				    "from file \"%s\" on line %lu",
bach f7f8b48
-			    	    cert->principals[i], file, linenum);
bach f7f8b48
+				    cert->principals[i], file, linenum);
bach f7f8b48
 				if (auth_parse_options(pw, line_opts,
bach f7f8b48
 				    file, linenum) != 1)
bach f7f8b48
 					continue;
bach f7f8b48
@@ -273,31 +277,22 @@ match_principals_file(char *file, struct
bach f7f8b48
 	fclose(f);
bach f7f8b48
 	restore_uid();
bach f7f8b48
 	return 0;
bach f7f8b48
-}	
bach f7f8b48
+}
Jan F. Chadima 69dd72f
 
bach f7f8b48
-/* return 1 if user allows given key */
bach f7f8b48
+/*
bach f7f8b48
+ * Checks whether key is allowed in authorized_keys-format file,
bach f7f8b48
+ * returns 1 if the key is allowed or 0 otherwise.
bach f7f8b48
+ */
Jan F. Chadima 69dd72f
 static int
Jan F. Chadima 69dd72f
-user_key_allowed2(struct passwd *pw, Key *key, char *file)
bach f7f8b48
+check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw)
Jan F. Chadima 69dd72f
 {
Jan F. Chadima 69dd72f
 	char line[SSH_MAX_PUBKEY_BYTES];
Jan F. Chadima 69dd72f
 	const char *reason;
Jan F. Chadima 69dd72f
 	int found_key = 0;
Jan F. Chadima 69dd72f
-	FILE *f;
Jan F. Chadima 69dd72f
 	u_long linenum = 0;
Jan F. Chadima 69dd72f
 	Key *found;
Jan F. Chadima 69dd72f
 	char *fp;
Jan F. Chadima 69dd72f
 
Jan F. Chadima 69dd72f
-	/* Temporarily use the user's uid. */
Jan F. Chadima 69dd72f
-	temporarily_use_uid(pw);
Jan F. Chadima 69dd72f
-
Jan F. Chadima 69dd72f
-	debug("trying public key file %s", file);
Jan F. Chadima 69dd72f
-	f = auth_openkeyfile(file, pw, options.strict_modes);
Jan F. Chadima 69dd72f
-
Jan F. Chadima 69dd72f
-	if (!f) {
Jan F. Chadima 69dd72f
-		restore_uid();
Jan F. Chadima 69dd72f
-		return 0;
Jan F. Chadima 69dd72f
-	}
Jan F. Chadima 69dd72f
-
Jan F. Chadima 69dd72f
 	found_key = 0;
Jan F. Chadima 69dd72f
 	found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
Jan F. Chadima 69dd72f
 
bach f7f8b48
@@ -390,8 +385,6 @@ user_key_allowed2(struct passwd *pw, Key
Jan F. Chadima 69dd72f
 			break;
Jan F. Chadima 69dd72f
 		}
Jan F. Chadima 69dd72f
 	}
Jan F. Chadima 69dd72f
-	restore_uid();
Jan F. Chadima 69dd72f
-	fclose(f);
Jan F. Chadima 69dd72f
 	key_free(found);
Jan F. Chadima 69dd72f
 	if (!found_key)
Jan F. Chadima 69dd72f
 		debug2("key not found");
5c8cd31
@@ -453,7 +446,180 @@ user_cert_trusted_ca(struct passwd *pw,
Jan F. Chadima 69dd72f
 	return ret;
Jan F. Chadima 69dd72f
 }
Jan F. Chadima 69dd72f
 
Jan F. Chadima 69dd72f
-/* check whether given key is in .ssh/authorized_keys* */
bach f7f8b48
+/*
bach f7f8b48
+ * Checks whether key is allowed in file.
bach f7f8b48
+ * returns 1 if the key is allowed or 0 otherwise.
bach f7f8b48
+ */
Jan F. Chadima 69dd72f
+static int
Jan F. Chadima 69dd72f
+user_key_allowed2(struct passwd *pw, Key *key, char *file)
Jan F. Chadima 69dd72f
+{
Jan F. Chadima 69dd72f
+	FILE *f;
Jan F. Chadima 69dd72f
+	int found_key = 0;
Jan F. Chadima 69dd72f
+
Jan F. Chadima 69dd72f
+	/* Temporarily use the user's uid. */
Jan F. Chadima 69dd72f
+	temporarily_use_uid(pw);
Jan F. Chadima 69dd72f
+
Jan F. Chadima 69dd72f
+	debug("trying public key file %s", file);
bach f7f8b48
+	if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
bach f7f8b48
+		found_key = check_authkeys_file(f, file, key, pw);
Jan F. Chadima 69dd72f
+		fclose(f);
Jan F. Chadima 69dd72f
+	}
Jan F. Chadima 69dd72f
+
Jan F. Chadima 69dd72f
+	restore_uid();
Jan F. Chadima 69dd72f
+	return found_key;
Jan F. Chadima 69dd72f
+}
Jan F. Chadima 69dd72f
+
bach f7f8b48
+/*
bach f7f8b48
+ * Checks whether key is allowed in output of command.
bach f7f8b48
+ * returns 1 if the key is allowed or 0 otherwise.
bach f7f8b48
+ */
Jan F. Chadima 69dd72f
+static int
bach f7f8b48
+user_key_command_allowed2(struct passwd *user_pw, Key *key)
Jan F. Chadima 69dd72f
+{
Jan F. Chadima 69dd72f
+	FILE *f;
bach f7f8b48
+	int ok, found_key = 0;
bach f7f8b48
+	struct passwd *pw;
Jan F. Chadima 69dd72f
+	struct stat st;
bach f7f8b48
+	int status, devnull, p[2], i;
bach f7f8b48
+	pid_t pid;
5c8cd31
+	char *username, errmsg[512];
Jan F. Chadima 69dd72f
+
bach f7f8b48
+	if (options.authorized_keys_command == NULL ||
bach f7f8b48
+	    options.authorized_keys_command[0] != '/')
Jan F. Chadima 1df0cf4
+		return 0;
Jan F. Chadima 69dd72f
+
5c8cd31
+	if (options.authorized_keys_command_user == NULL) {
fb00871
+		pw = user_pw;
5c8cd31
+	}
fb00871
+	else {
fb00871
+		username = percent_expand(options.authorized_keys_command_user,
fb00871
+		    "u", user_pw->pw_name, (char *)NULL);
fb00871
+		pw = getpwnam(username);
fb00871
+		if (pw == NULL) {
fb00871
+			error("AuthorizedKeyCommandUser \"%s\" not found: %s",
fb00871
+			    username, strerror(errno));
fb00871
+			free(username);
fb00871
+			return 0;
fb00871
+		}
5c8cd31
+		free(username);
Jan F. Chadima 69dd72f
+	}
Jan F. Chadima 69dd72f
+
bach f7f8b48
+	temporarily_use_uid(pw);
5c8cd31
+
bach f7f8b48
+	if (stat(options.authorized_keys_command, &st) < 0) {
bach f7f8b48
+		error("Could not stat AuthorizedKeysCommand \"%s\": %s",
bach f7f8b48
+		    options.authorized_keys_command, strerror(errno));
bach f7f8b48
+		goto out;
Jan F. Chadima 69dd72f
+	}
bach f7f8b48
+	if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0,
bach f7f8b48
+	    errmsg, sizeof(errmsg)) != 0) {
bach f7f8b48
+		error("Unsafe AuthorizedKeysCommand: %s", errmsg);
bach f7f8b48
+		goto out;
Jan F. Chadima 69dd72f
+	}
Jan F. Chadima 69dd72f
+
bach f7f8b48
+	if (pipe(p) != 0) {
bach f7f8b48
+		error("%s: pipe: %s", __func__, strerror(errno));
bach f7f8b48
+		goto out;
5c8cd31
+	}
bach f7f8b48
+
5c8cd31
+	debug3("Running AuthorizedKeysCommand: \"%s %s\" as \"%s\"",
5c8cd31
+	    options.authorized_keys_command, user_pw->pw_name, pw->pw_name);
Jan F. Chadima 69dd72f
+
Jan F. Chadima 69dd72f
+	/*
bach f7f8b48
+	 * Don't want to call this in the child, where it can fatal() and
bach f7f8b48
+	 * run cleanup_exit() code.
Jan F. Chadima 69dd72f
+	 */
bach f7f8b48
+	restore_uid();
Jan F. Chadima 69dd72f
+
bach f7f8b48
+	switch ((pid = fork())) {
bach f7f8b48
+	case -1: /* error */
bach f7f8b48
+		error("%s: fork: %s", __func__, strerror(errno));
bach f7f8b48
+		close(p[0]);
bach f7f8b48
+		close(p[1]);
bach f7f8b48
+		return 0;
bach f7f8b48
+	case 0: /* child */
bach f7f8b48
+		for (i = 0; i < NSIG; i++)
bach f7f8b48
+			signal(i, SIG_DFL);
bach f7f8b48
+
5c8cd31
+		if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
5c8cd31
+			error("%s: open %s: %s", __func__, _PATH_DEVNULL,
5c8cd31
+			    strerror(errno));
5c8cd31
+			_exit(1);
5c8cd31
+		}
5c8cd31
+		/* Keep stderr around a while longer to catch errors */
5c8cd31
+		if (dup2(devnull, STDIN_FILENO) == -1 ||
5c8cd31
+		    dup2(p[1], STDOUT_FILENO) == -1) {
5c8cd31
+			error("%s: dup2: %s", __func__, strerror(errno));
5c8cd31
+			_exit(1);
5c8cd31
+		}
5c8cd31
+		closefrom(STDERR_FILENO + 1);
5c8cd31
+
bach f7f8b48
+		/* Don't use permanently_set_uid() here to avoid fatal() */
bach f7f8b48
+		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
bach f7f8b48
+			error("setresgid %u: %s", (u_int)pw->pw_gid,
bach f7f8b48
+			    strerror(errno));
bach f7f8b48
+			_exit(1);
Jan F. Chadima 69dd72f
+		}
bach f7f8b48
+		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
bach f7f8b48
+			error("setresuid %u: %s", (u_int)pw->pw_uid,
bach f7f8b48
+			    strerror(errno));
bach f7f8b48
+			_exit(1);
Jan F. Chadima 69dd72f
+		}
5c8cd31
+		/* stdin is pointed to /dev/null at this point */
5c8cd31
+		if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
bach f7f8b48
+			error("%s: dup2: %s", __func__, strerror(errno));
bach f7f8b48
+			_exit(1);
Jan F. Chadima 69dd72f
+		}
Jan F. Chadima 69dd72f
+
bach f7f8b48
+		execl(options.authorized_keys_command,
5c8cd31
+		    options.authorized_keys_command, user_pw->pw_name, NULL);
Jan F. Chadima 69dd72f
+
bach f7f8b48
+		error("AuthorizedKeysCommand %s exec failed: %s",
bach f7f8b48
+		    options.authorized_keys_command, strerror(errno));
Jan F. Chadima 69dd72f
+		_exit(127);
bach f7f8b48
+	default: /* parent */
bach f7f8b48
+		break;
Jan F. Chadima 69dd72f
+	}
5c8cd31
+
bach f7f8b48
+	temporarily_use_uid(pw);
Jan F. Chadima 69dd72f
+
bach f7f8b48
+	close(p[1]);
bach f7f8b48
+	if ((f = fdopen(p[0], "r")) == NULL) {
bach f7f8b48
+		error("%s: fdopen: %s", __func__, strerror(errno));
bach f7f8b48
+		close(p[0]);
bach f7f8b48
+		/* Don't leave zombie child */
5c8cd31
+		kill(pid, SIGTERM);
bach f7f8b48
+		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
bach f7f8b48
+			;
bach f7f8b48
+		goto out;
bach f7f8b48
+	}
bach f7f8b48
+	ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
bach f7f8b48
+	fclose(f);
Jan F. Chadima 69dd72f
+
bach f7f8b48
+	while (waitpid(pid, &status, 0) == -1) {
bach f7f8b48
+		if (errno != EINTR) {
bach f7f8b48
+			error("%s: waitpid: %s", __func__, strerror(errno));
bach f7f8b48
+			goto out;
bach f7f8b48
+		}
bach f7f8b48
+	}
bach f7f8b48
+	if (WIFSIGNALED(status)) {
bach f7f8b48
+		error("AuthorizedKeysCommand %s exited on signal %d",
bach f7f8b48
+		    options.authorized_keys_command, WTERMSIG(status));
bach f7f8b48
+		goto out;
bach f7f8b48
+	} else if (WEXITSTATUS(status) != 0) {
bach f7f8b48
+		error("AuthorizedKeysCommand %s returned status %d",
bach f7f8b48
+		    options.authorized_keys_command, WEXITSTATUS(status));
bach f7f8b48
+		goto out;
bach f7f8b48
+	}
bach f7f8b48
+	found_key = ok;
bach f7f8b48
+ out:
bach f7f8b48
+	restore_uid();
Jan F. Chadima 69dd72f
+	return found_key;
Jan F. Chadima 69dd72f
+}
Jan F. Chadima 69dd72f
+
bach f7f8b48
+/*
bach f7f8b48
+ * Check whether key authenticates and authorises the user.
bach f7f8b48
+ */
Jan F. Chadima 69dd72f
 int
Jan F. Chadima 69dd72f
 user_key_allowed(struct passwd *pw, Key *key)
Jan F. Chadima 69dd72f
 {
5c8cd31
@@ -469,9 +635,17 @@ user_key_allowed(struct passwd *pw, Key
bach f7f8b48
 	if (success)
bach f7f8b48
 		return success;
fc87f2d
 
bach f7f8b48
+	success = user_key_command_allowed2(pw, key);
fc87f2d
+	if (success > 0)
fc87f2d
+		return success;
fc87f2d
+
bach f7f8b48
 	for (i = 0; !success && i < options.num_authkeys_files; i++) {
5c8cd31
+
5c8cd31
+		if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
5c8cd31
+			continue;
bach f7f8b48
 		file = expand_authorized_keys(
bach f7f8b48
 		    options.authorized_keys_files[i], pw);
5c8cd31
+
5c8cd31
 		success = user_key_allowed2(pw, key, file);
5c8cd31
 		xfree(file);
5c8cd31
 	}
5039c7c
diff -up openssh-6.1p1/auth.c.akc openssh-6.1p1/auth.c
5c8cd31
--- openssh-6.1p1/auth.c.akc	2013-02-14 17:46:45.189547274 +0100
5c8cd31
+++ openssh-6.1p1/auth.c	2013-02-14 17:46:45.273546907 +0100
5c8cd31
@@ -415,39 +415,41 @@ check_key_in_hostfiles(struct passwd *pw
5039c7c
 
5039c7c
 
5039c7c
 /*
5039c7c
- * Check a given file for security. This is defined as all components
5039c7c
+ * Check a given path for security. This is defined as all components
5039c7c
  * of the path to the file must be owned by either the owner of
5039c7c
  * of the file or root and no directories must be group or world writable.
5039c7c
  *
5039c7c
  * XXX Should any specific check be done for sym links ?
5039c7c
  *
5039c7c
- * Takes an open file descriptor, the file name, a uid and and
5039c7c
+ * Takes an the file name, its stat information (preferably from fstat() to
5039c7c
+ * avoid races), the uid of the expected owner, their home directory and an
5039c7c
  * error buffer plus max size as arguments.
5039c7c
  *
5039c7c
  * Returns 0 on success and -1 on failure
5039c7c
  */
5039c7c
-static int
5039c7c
-secure_filename(FILE *f, const char *file, struct passwd *pw,
5039c7c
-    char *err, size_t errlen)
5039c7c
+int
5039c7c
+auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
5039c7c
+    uid_t uid, char *err, size_t errlen)
5039c7c
 {
5039c7c
-	uid_t uid = pw->pw_uid;
5039c7c
 	char buf[MAXPATHLEN], homedir[MAXPATHLEN];
5039c7c
 	char *cp;
5039c7c
 	int comparehome = 0;
5039c7c
 	struct stat st;
5039c7c
 
5039c7c
-	if (realpath(file, buf) == NULL) {
5039c7c
-		snprintf(err, errlen, "realpath %s failed: %s", file,
5039c7c
+	if (realpath(name, buf) == NULL) {
5039c7c
+		snprintf(err, errlen, "realpath %s failed: %s", name,
5039c7c
 		    strerror(errno));
5039c7c
 		return -1;
5039c7c
 	}
5039c7c
-	if (realpath(pw->pw_dir, homedir) != NULL)
5039c7c
+	if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL)
5039c7c
 		comparehome = 1;
5039c7c
 
5039c7c
-	/* check the open file to avoid races */
5039c7c
-	if (fstat(fileno(f), &st) < 0 ||
5039c7c
-	    (st.st_uid != 0 && st.st_uid != uid) ||
5039c7c
-	    (st.st_mode & 022) != 0) {
5039c7c
+	if (!S_ISREG(stp->st_mode)) {
5039c7c
+		snprintf(err, errlen, "%s is not a regular file", buf);
5039c7c
+		return -1;
5039c7c
+	}
5039c7c
+	if ((stp->st_uid != 0 && stp->st_uid != uid) ||
5039c7c
+	    (stp->st_mode & 022) != 0) {
5039c7c
 		snprintf(err, errlen, "bad ownership or modes for file %s",
5039c7c
 		    buf);
5039c7c
 		return -1;
5c8cd31
@@ -483,6 +485,31 @@ secure_filename(FILE *f, const char *fil
5039c7c
 	return 0;
5039c7c
 }
5039c7c
 
5039c7c
+/*
5039c7c
+ * Version of secure_path() that accepts an open file descriptor to
5039c7c
+ * avoid races.
5039c7c
+ *
5039c7c
+ * Returns 0 on success and -1 on failure
5039c7c
+ */
5039c7c
+static int
5039c7c
+secure_filename(FILE *f, const char *file, struct passwd *pw,
5039c7c
+    char *err, size_t errlen)
5039c7c
+{
5039c7c
+	uid_t uid = pw->pw_uid;
5039c7c
+	char buf[MAXPATHLEN], homedir[MAXPATHLEN];
5039c7c
+	char *cp;
5039c7c
+	int comparehome = 0;
5039c7c
+	struct stat st;
5039c7c
+
5039c7c
+	/* check the open file to avoid races */
5039c7c
+	if (fstat(fileno(f), &st) < 0) {
5039c7c
+		snprintf(err, errlen, "cannot stat file %s: %s",
5039c7c
+		    buf, strerror(errno));
5039c7c
+		return -1;
5039c7c
+	}
5039c7c
+	return auth_secure_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen);
5039c7c
+}
5039c7c
+
5039c7c
 static FILE *
5039c7c
 auth_openfile(const char *file, struct passwd *pw, int strict_modes,
5039c7c
     int log_missing, char *file_type)
5039c7c
diff -up openssh-6.1p1/auth.h.akc openssh-6.1p1/auth.h
5c8cd31
--- openssh-6.1p1/auth.h.akc	2013-02-14 17:46:45.259546968 +0100
5c8cd31
+++ openssh-6.1p1/auth.h	2013-02-14 17:46:45.274546903 +0100
5039c7c
@@ -125,6 +125,10 @@ int	 auth_rhosts_rsa_key_allowed(struct
5039c7c
 int	 hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
5039c7c
 int	 user_key_allowed(struct passwd *, Key *);
5039c7c
 
5039c7c
+struct stat;
5039c7c
+int	 auth_secure_path(const char *, struct stat *, const char *, uid_t,
5039c7c
+    char *, size_t);
5039c7c
+
5039c7c
 #ifdef KRB5
5039c7c
 int	auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *);
5039c7c
 int	auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt);
9fe1afc
diff -up openssh-6.1p1/servconf.c.akc openssh-6.1p1/servconf.c
5c8cd31
--- openssh-6.1p1/servconf.c.akc	2013-02-14 17:46:45.193547257 +0100
5c8cd31
+++ openssh-6.1p1/servconf.c	2013-02-14 17:46:45.274546903 +0100
5039c7c
@@ -137,6 +137,8 @@ initialize_server_options(ServerOptions
Jan F. Chadima 69dd72f
 	options->num_permitted_opens = -1;
Jan F. Chadima 69dd72f
 	options->adm_forced_command = NULL;
Jan F. Chadima 69dd72f
 	options->chroot_directory = NULL;
Jan F. Chadima 69dd72f
+	options->authorized_keys_command = NULL;
bach f7f8b48
+	options->authorized_keys_command_user = NULL;
Jan F. Chadima 69dd72f
 	options->zero_knowledge_password_authentication = -1;
Jan F. Chadima 69dd72f
 	options->revoked_keys_file = NULL;
Jan F. Chadima 69dd72f
 	options->trusted_user_ca_keys = NULL;
5039c7c
@@ -331,6 +333,7 @@ typedef enum {
Jan F. Chadima 69dd72f
 	sZeroKnowledgePasswordAuthentication, sHostCertificate,
Jan F. Chadima 69dd72f
 	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
9fe1afc
 	sKexAlgorithms, sIPQoS, sVersionAddendum,
bach f7f8b48
+	sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
5039c7c
 	sAuthenticationMethods,
Jan F. Chadima 69dd72f
 	sDeprecated, sUnsupported
Jan F. Chadima 69dd72f
 } ServerOpCodes;
5039c7c
@@ -457,6 +460,9 @@ static struct {
5039c7c
 	{ "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
Jan F. Chadima 69dd72f
 	{ "ipqos", sIPQoS, SSHCFG_ALL },
5039c7c
 	{ "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
Jan F. Chadima 69dd72f
+	{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
bach f7f8b48
+	{ "authorizedkeyscommandrunas", sAuthorizedKeysCommandUser, SSHCFG_ALL },
bach f7f8b48
+	{ "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
5039c7c
 	{ "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
Jan F. Chadima 69dd72f
 	{ NULL, sBadOption, 0 }
Jan F. Chadima 69dd72f
 };
5039c7c
@@ -1520,6 +1526,26 @@ process_server_config_line(ServerOptions
Jan F. Chadima 69dd72f
 		}
9fe1afc
 		return 0;
Jan F. Chadima 69dd72f
 
Jan F. Chadima 69dd72f
+	case sAuthorizedKeysCommand:
Jan F. Chadima 69dd72f
+		len = strspn(cp, WHITESPACE);
bach f7f8b48
+		if (*activep && options->authorized_keys_command == NULL) {
Jan F. Chadima 69dd72f
+			options->authorized_keys_command = xstrdup(cp + len);
bach f7f8b48
+			if (*options->authorized_keys_command != '/') {
bach f7f8b48
+				fatal("%.200s line %d: AuthorizedKeysCommand "
bach f7f8b48
+				    "must be an absolute path",
bach f7f8b48
+				    filename, linenum);
bach f7f8b48
+			}
bach f7f8b48
+		}
Jan F. Chadima 69dd72f
+		return 0;
Jan F. Chadima 69dd72f
+
bach f7f8b48
+	case sAuthorizedKeysCommandUser:
bach f7f8b48
+		charptr = &options->authorized_keys_command_user;
Jan F. Chadima 69dd72f
+
Jan F. Chadima 69dd72f
+		arg = strdelim(&cp;;
Jan F. Chadima 69dd72f
+		if (*activep && *charptr == NULL)
Jan F. Chadima 69dd72f
+			*charptr = xstrdup(arg);
Jan F. Chadima 69dd72f
+		break;
Jan F. Chadima 69dd72f
+
Jan F. Chadima 69dd72f
 	case sDeprecated:
Jan F. Chadima 69dd72f
 		logit("%s line %d: Deprecated option %s",
Jan F. Chadima 69dd72f
 		    filename, linenum, arg);
5039c7c
@@ -1670,6 +1696,8 @@ copy_set_server_options(ServerOptions *d
d9e6186
 	M_CP_INTOPT(hostbased_uses_name_from_packet_only);
d9e6186
 	M_CP_INTOPT(kbd_interactive_authentication);
Jan F. Chadima 69dd72f
 	M_CP_INTOPT(zero_knowledge_password_authentication);
Jan F. Chadima 69dd72f
+	M_CP_STROPT(authorized_keys_command);
bach f7f8b48
+	M_CP_STROPT(authorized_keys_command_user);
Jan F. Chadima 69dd72f
 	M_CP_INTOPT(permit_root_login);
Jan F. Chadima 69dd72f
 	M_CP_INTOPT(permit_empty_passwd);
Jan F. Chadima 69dd72f
 
5039c7c
@@ -1930,6 +1958,8 @@ dump_config(ServerOptions *o)
Jan F. Chadima 69dd72f
 	dump_cfg_string(sAuthorizedPrincipalsFile,
Jan F. Chadima 69dd72f
 	    o->authorized_principals_file);
9fe1afc
 	dump_cfg_string(sVersionAddendum, o->version_addendum);
Jan F. Chadima 69dd72f
+	dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
bach f7f8b48
+	dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user);
Jan F. Chadima 69dd72f
 
Jan F. Chadima 69dd72f
 	/* string arguments requiring a lookup */
Jan F. Chadima 69dd72f
 	dump_cfg_string(sLogLevel, log_level_name(o->log_level));
9fe1afc
diff -up openssh-6.1p1/servconf.h.akc openssh-6.1p1/servconf.h
5c8cd31
--- openssh-6.1p1/servconf.h.akc	2013-02-14 17:46:45.194547252 +0100
5c8cd31
+++ openssh-6.1p1/servconf.h	2013-02-14 17:46:45.275546898 +0100
5039c7c
@@ -167,6 +167,8 @@ typedef struct {
Jan F. Chadima 69dd72f
 	char   *revoked_keys_file;
Jan F. Chadima 69dd72f
 	char   *trusted_user_ca_keys;
Jan F. Chadima 69dd72f
 	char   *authorized_principals_file;
Jan F. Chadima 69dd72f
+	char   *authorized_keys_command;
bach f7f8b48
+	char   *authorized_keys_command_user;
Jan F. Chadima 69dd72f
 
9fe1afc
 	char   *version_addendum;	/* Appended to SSH banner */
5039c7c
 
bach f7f8b48
diff -up openssh-6.1p1/sshd.c.akc openssh-6.1p1/sshd.c
5c8cd31
--- openssh-6.1p1/sshd.c.akc	2013-02-14 17:46:45.270546920 +0100
5c8cd31
+++ openssh-6.1p1/sshd.c	2013-02-14 17:46:45.276546894 +0100
bach f7f8b48
@@ -366,9 +366,20 @@ main_sigchld_handler(int sig)
bach f7f8b48
 static void
bach f7f8b48
 grace_alarm_handler(int sig)
bach f7f8b48
 {
bach f7f8b48
+	pid_t pgid;
bach f7f8b48
+
bach f7f8b48
 	if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0)
bach f7f8b48
 		kill(pmonitor->m_pid, SIGALRM);
bach f7f8b48
 
bach f7f8b48
+	/*
bach f7f8b48
+	 * Try to kill any processes that we have spawned, E.g. authorized
bach f7f8b48
+	 * keys command helpers.
bach f7f8b48
+	 */
bach f7f8b48
+	if ((pgid = getpgid(0)) == getpid()) {
bach f7f8b48
+		signal(SIGTERM, SIG_IGN);
bach f7f8b48
+		killpg(pgid, SIGTERM);
bach f7f8b48
+	}
bach f7f8b48
+
bach f7f8b48
 	/* Log error and exit. */
bach f7f8b48
 	sigdie("Timeout before authentication for %s", get_remote_ipaddr());
bach f7f8b48
 }
9fe1afc
diff -up openssh-6.1p1/sshd_config.0.akc openssh-6.1p1/sshd_config.0
9fe1afc
--- openssh-6.1p1/sshd_config.0.akc	2012-08-29 02:53:04.000000000 +0200
5c8cd31
+++ openssh-6.1p1/sshd_config.0	2013-02-14 17:46:45.276546894 +0100
Jan F. Chadima 69dd72f
@@ -71,6 +71,23 @@ DESCRIPTION
Jan F. Chadima 69dd72f
 
Jan F. Chadima 69dd72f
              See PATTERNS in ssh_config(5) for more information on patterns.
Jan F. Chadima 69dd72f
 
Jan F. Chadima 69dd72f
+     AuthorizedKeysCommand
Jan F. Chadima 69dd72f
+
Jan F. Chadima 69dd72f
+             Specifies a program to be used for lookup of the user's
Jan F. Chadima 69dd72f
+	     public keys.  The program will be invoked with its first
d9e6186
+	     argument the name of the user being authorized, and should produce
d9e6186
+	     on standard output AuthorizedKeys lines (see AUTHORIZED_KEYS
Jan F. Chadima 69dd72f
+	     in sshd(8)).  By default (or when set to the empty string) there is no
Jan F. Chadima 69dd72f
+	     AuthorizedKeysCommand run.  If the AuthorizedKeysCommand does not successfully
Jan F. Chadima 69dd72f
+	     authorize the user, authorization falls through to the
Jan F. Chadima 69dd72f
+	     AuthorizedKeysFile.  Note that this option has an effect
Jan F. Chadima 69dd72f
+	     only with PubkeyAuthentication turned on.
Jan F. Chadima 69dd72f
+
Jan F. Chadima 69dd72f
+     AuthorizedKeysCommandRunAs
Jan F. Chadima 69dd72f
+             Specifies the user under whose account the AuthorizedKeysCommand is run.
Jan F. Chadima 69dd72f
+             Empty string (the default value) means the user being authorized
Jan F. Chadima 69dd72f
+             is used.
Jan F. Chadima 69dd72f
+
Jan F. Chadima 69dd72f
      AuthorizedKeysFile
Jan F. Chadima 69dd72f
              Specifies the file that contains the public keys that can be used
Jan F. Chadima 69dd72f
              for user authentication.  The format is described in the
9fe1afc
@@ -402,7 +419,8 @@ DESCRIPTION
Jan F. Chadima 69dd72f
              Only a subset of keywords may be used on the lines following a
9fe1afc
              Match keyword.  Available keywords are AcceptEnv,
9fe1afc
              AllowAgentForwarding, AllowGroups, AllowTcpForwarding,
9fe1afc
-             AllowUsers, AuthorizedKeysFile, AuthorizedPrincipalsFile, Banner,
9fe1afc
+             AllowUsers, AuthorizedKeysFile, AuthorizedKeysCommand,
9fe1afc
+             AuthorizedKeysCommandRunAs, AuthorizedPrincipalsFile, Banner,
9fe1afc
              ChrootDirectory, DenyGroups, DenyUsers, ForceCommand,
9fe1afc
              GatewayPorts, GSSAPIAuthentication, HostbasedAuthentication,
Jan F. Chadima 69dd72f
              HostbasedUsesNameFromPacketOnly, KbdInteractiveAuthentication,
9fe1afc
diff -up openssh-6.1p1/sshd_config.5.akc openssh-6.1p1/sshd_config.5
5c8cd31
--- openssh-6.1p1/sshd_config.5.akc	2013-02-14 17:46:45.195547248 +0100
5c8cd31
+++ openssh-6.1p1/sshd_config.5	2013-02-14 17:46:45.277546890 +0100
5039c7c
@@ -173,6 +173,20 @@ Note that each authentication method lis
5039c7c
 in the configuration.
5039c7c
 The default is not to require multiple authentication; successful completion
5039c7c
 of a single authentication method is sufficient.
d9e6186
+.It Cm AuthorizedKeysCommand
bach f7f8b48
+Specifies a program to be used for lookup of the user's public keys.
bach f7f8b48
+The program will be invoked with a single argument of the username
bach f7f8b48
+being authenticated, and should produce on standard output zero or
bach f7f8b48
+more lines of authorized_keys output (see AUTHORIZED_KEYS in
bach f7f8b48
+.Xr sshd 8 )
bach f7f8b48
+If a key supplied by AuthorizedKeysCommand does not successfully authenticate
bach f7f8b48
+and authorize the user then public key authentication continues using the usual
bach f7f8b48
+.Cm AuthorizedKeysFile
bach f7f8b48
+files.
bach f7f8b48
+By default, no AuthorizedKeysCommand is run.
bach f7f8b48
+.It Cm AuthorizedKeysCommandUser
bach f7f8b48
+Specifies the user under whose account the AuthorizedKeysCommand is run.
bach f7f8b48
+The default is the user being authenticated.
d9e6186
 .It Cm AuthorizedKeysFile
d9e6186
 Specifies the file that contains the public keys that can be used
d9e6186
 for user authentication.
5039c7c
@@ -734,6 +748,8 @@ Available keywords are
Jan F. Chadima 69dd72f
 .Cm AllowTcpForwarding ,
9fe1afc
 .Cm AllowUsers ,
5039c7c
 .Cm AuthenticationMethods ,
Jan F. Chadima 69dd72f
+.Cm AuthorizedKeysCommand ,
bach f7f8b48
+.Cm AuthorizedKeysCommandUser ,
5039c7c
 .Cm AuthorizedKeysFile ,
Jan F. Chadima 69dd72f
 .Cm AuthorizedPrincipalsFile ,
Jan F. Chadima 69dd72f
 .Cm Banner ,
5039c7c
@@ -749,6 +765,7 @@ Available keywords are
Jan F. Chadima 69dd72f
 .Cm KerberosAuthentication ,
Jan F. Chadima 69dd72f
 .Cm MaxAuthTries ,
Jan F. Chadima 69dd72f
 .Cm MaxSessions ,
Jan F. Chadima 69dd72f
+.Cm PubkeyAuthentication ,
Jan F. Chadima 69dd72f
 .Cm PasswordAuthentication ,
Jan F. Chadima 69dd72f
 .Cm PermitEmptyPasswords ,
Jan F. Chadima 69dd72f
 .Cm PermitOpen ,
5039c7c
diff -up openssh-6.1p1/sshd_config.akc openssh-6.1p1/sshd_config
5039c7c
--- openssh-6.1p1/sshd_config.akc	2012-07-31 04:21:34.000000000 +0200
5c8cd31
+++ openssh-6.1p1/sshd_config	2013-02-14 17:46:45.277546890 +0100
5039c7c
@@ -49,6 +49,9 @@
5039c7c
 # but this is overridden so installations will only check .ssh/authorized_keys
5039c7c
 AuthorizedKeysFile	.ssh/authorized_keys
5039c7c
 
5039c7c
+#AuthorizedKeysCommand none
5039c7c
+#AuthorizedKeysCommandUser nobody
5039c7c
+
5039c7c
 #AuthorizedPrincipalsFile none
5039c7c
 
5039c7c
 # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts