7818e56
diff -ruN openssh-5.5p1.orig/auth2-pubkey.c openssh-5.5p1/auth2-pubkey.c
7818e56
--- openssh-5.5p1.orig/auth2-pubkey.c	2010-03-21 14:51:21.000000000 -0400
7818e56
+++ openssh-5.5p1/auth2-pubkey.c	2010-07-03 20:23:43.000000000 -0400
7818e56
@@ -27,6 +27,7 @@
7818e56
 
7818e56
 #include <sys/types.h>
7818e56
 #include <sys/stat.h>
7818e56
+#include <sys/wait.h>
7818e56
 
7818e56
 #include <fcntl.h>
7818e56
 #include <pwd.h>
7818e56
@@ -178,27 +178,15 @@
7818e56
 
7818e56
 /* return 1 if user allows given key */
7818e56
 static int
7818e56
-user_key_allowed2(struct passwd *pw, Key *key, char *file)
7818e56
+user_search_key_in_file(FILE *f, char *file, Key* key, struct passwd *pw)
7818e56
 {
7818e56
 	char line[SSH_MAX_PUBKEY_BYTES];
7818e56
 	const char *reason;
7818e56
 	int found_key = 0;
7818e56
-	FILE *f;
7818e56
 	u_long linenum = 0;
7818e56
 	Key *found;
7818e56
 	char *fp;
7818e56
 
7818e56
-	/* Temporarily use the user's uid. */
7818e56
-	temporarily_use_uid(pw);
7818e56
-
7818e56
-	debug("trying public key file %s", file);
7818e56
-	f = auth_openkeyfile(file, pw, options.strict_modes);
7818e56
-
7818e56
-	if (!f) {
7818e56
-		restore_uid();
7818e56
-		return 0;
7818e56
-	}
7818e56
-
7818e56
 	found_key = 0;
7818e56
 	found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
7818e56
 
7818e56
@@ -273,8 +261,6 @@
7818e56
 			break;
7818e56
 		}
7818e56
 	}
7818e56
-	restore_uid();
7818e56
-	fclose(f);
7818e56
 	key_free(found);
7818e56
 	if (!found_key)
7818e56
 		debug2("key not found");
7818e56
@@ -321,13 +307,191 @@
7818e56
 	return ret;
7818e56
 }
7818e56
 
7818e56
-/* check whether given key is in .ssh/authorized_keys* */
7818e56
+/* return 1 if user allows given key */
7818e56
+static int
7818e56
+user_key_allowed2(struct passwd *pw, Key *key, char *file)
7818e56
+{
7818e56
+	FILE *f;
7818e56
+	int found_key = 0;
7818e56
+
7818e56
+	/* Temporarily use the user's uid. */
7818e56
+	temporarily_use_uid(pw);
7818e56
+
7818e56
+	debug("trying public key file %s", file);
7818e56
+	f = auth_openkeyfile(file, pw, options.strict_modes);
7818e56
+
7818e56
+ 	if (f) {
7818e56
+ 		found_key = user_search_key_in_file (f, file, key, pw);
7818e56
+		fclose(f);
7818e56
+	}
7818e56
+
7818e56
+	restore_uid();
7818e56
+	return found_key;
7818e56
+}
7818e56
+
7818e56
+#ifdef WITH_AUTHORIZED_KEYS_COMMAND
7818e56
+
7818e56
+#define WHITESPACE " \t\r\n"
7818e56
+
7818e56
+/* return 1 if user allows given key */
7818e56
+static int
7818e56
+user_key_via_command_allowed2(struct passwd *pw, Key *key)
7818e56
+{
7818e56
+	FILE *f;
7818e56
+	int found_key = 0;
7818e56
+	char *progname = NULL;
7818e56
+	char *cp;
7818e56
+	struct passwd *runas_pw;
7818e56
+	struct stat st;
7818e56
+	int childdescriptors[2], i;
7818e56
+	pid_t pstat, pid, child;
7818e56
+
7818e56
+	if (options.authorized_keys_command == NULL || options.authorized_keys_command[0] != '/')
7818e56
+		return -1;
7818e56
+
7818e56
+	/* get the run as identity from config */
7818e56
+	runas_pw = (options.authorized_keys_command_runas == NULL)? pw
7818e56
+	    : getpwnam (options.authorized_keys_command_runas);
7818e56
+	if (!runas_pw) {
7818e56
+		error("%s: getpwnam(\"%s\"): %s", __func__,
7818e56
+		    options.authorized_keys_command_runas, strerror(errno));
7818e56
+		return 0;
7818e56
+	}
7818e56
+
7818e56
+	/* Temporarily use the specified uid. */
7818e56
+	if (runas_pw->pw_uid != 0)
7818e56
+		temporarily_use_uid(runas_pw);
7818e56
+
7818e56
+	progname = xstrdup(options.authorized_keys_command);
7818e56
+
7818e56
+	debug3("%s: checking program '%s'", __func__, progname);
7818e56
+
7818e56
+	if (stat (progname, &st) < 0) {
7818e56
+		error("%s: stat(\"%s\"): %s", __func__,
7818e56
+		    progname, strerror(errno));
7818e56
+		goto go_away;
7818e56
+	}
7818e56
+
7818e56
+	if (st.st_uid != 0 || (st.st_mode & 022) != 0) {
7818e56
+		error("bad ownership or modes for AuthorizedKeysCommand \"%s\"",
7818e56
+		    progname);
7818e56
+		goto go_away;
7818e56
+	}
7818e56
+
7818e56
+	if (!S_ISREG(st.st_mode)) {
7818e56
+		error("AuthorizedKeysCommand \"%s\" is not a regular file",
7818e56
+		    progname);
7818e56
+		goto go_away;
7818e56
+	}
7818e56
+
7818e56
+	/*
7818e56
+	 * Descend the path, checking that each component is a
7818e56
+	 * root-owned directory with strict permissions.
7818e56
+	 */
7818e56
+	do {
7818e56
+		if ((cp = strrchr(progname, '/')) == NULL)
7818e56
+			break;
7818e56
+		else 
7818e56
+			*cp = '\0';
7818e56
+	
7818e56
+		debug3("%s: checking component '%s'", __func__, (*progname == '\0' ? "/" : progname));
7818e56
+
7818e56
+		if (stat((*progname == '\0' ? "/" : progname), &st) != 0) {
7818e56
+			error("%s: stat(\"%s\"): %s", __func__,
7818e56
+			    progname, strerror(errno));
7818e56
+			goto go_away;
7818e56
+		}
7818e56
+		if (st.st_uid != 0 || (st.st_mode & 022) != 0) {
7818e56
+			error("bad ownership or modes for AuthorizedKeysCommand path component \"%s\"",
7818e56
+			    progname);
7818e56
+			goto go_away;
7818e56
+		}
7818e56
+		if (!S_ISDIR(st.st_mode)) {
7818e56
+			error("AuthorizedKeysCommand path component \"%s\" is not a directory",
7818e56
+			    progname);
7818e56
+			goto go_away;
7818e56
+		}
7818e56
+	} while (1);
7818e56
+
7818e56
+	/* open the pipe and read the keys */
7818e56
+	if (pipe(childdescriptors)) {
7818e56
+		error("failed to pipe(2) for AuthorizedKeysCommand: %s",
7818e56
+		    strerror(errno));
7818e56
+		goto go_away;
7818e56
+	}
7818e56
+
7818e56
+	child = fork();
7818e56
+	if (child == -1) {
7818e56
+		error("failed to fork(2) for AuthorizedKeysCommand: %s",
7818e56
+		    strerror(errno));
7818e56
+		goto go_away;
7818e56
+	} else if (child == 0) {
7818e56
+		/* we're in the child process here -- we should never return from this block. */
7818e56
+		/* permanently drop privs in child process */
7818e56
+		if (runas_pw->pw_uid != 0) {
7818e56
+			restore_uid();
7818e56
+			permanently_set_uid(runas_pw);
7818e56
+	  	}
7818e56
+
7818e56
+		close(childdescriptors[0]);
7818e56
+		/* put the write end of the pipe on stdout (FD 1) */
7818e56
+		if (dup2(childdescriptors[1], 1) == -1) {
7818e56
+			error("failed to dup2(2) from AuthorizedKeysCommand: %s",
7818e56
+			    strerror(errno));
7818e56
+			_exit(127);
7818e56
+		}
7818e56
+
7818e56
+		debug3("about to execl() AuthorizedKeysCommand: \"%s\" \"%s\"", options.authorized_keys_command, pw->pw_name);
7818e56
+		/* see session.c:child_close_fds() */
7818e56
+		for (i = 3; i < 64; ++i) {
7818e56
+			close(i);
7818e56
+		}
7818e56
+
7818e56
+		execl(options.authorized_keys_command, options.authorized_keys_command, pw->pw_name, NULL);
7818e56
+
7818e56
+		/* if we got here, it didn't work */
7818e56
+		error("failed to execl AuthorizedKeysCommand: %s", strerror(errno)); /* this won't work because we closed the fds above */
7818e56
+		_exit(127);
7818e56
+	}
7818e56
+	
7818e56
+	close(childdescriptors[1]);
7818e56
+	f = fdopen(childdescriptors[0], "r");
7818e56
+	if (!f) {
7818e56
+		error("%s: could not buffer FDs from AuthorizedKeysCommand (\"%s\", \"r\"): %s", __func__,
7818e56
+		    options.authorized_keys_command, strerror (errno));
7818e56
+		goto go_away;
7818e56
+	}
7818e56
+
7818e56
+	found_key = user_search_key_in_file (f, options.authorized_keys_command, key, pw);
7818e56
+	fclose (f);
7818e56
+	do {
7818e56
+		pid = waitpid(child, &pstat, 0);
7818e56
+	} while (pid == -1 && errno == EINTR);
7818e56
+
7818e56
+	/* what about the return value from the child process? */
7818e56
+go_away:
7818e56
+	if (progname)
7818e56
+		xfree (progname);
7818e56
+
7818e56
+	if (runas_pw->pw_uid != 0)
7818e56
+		restore_uid();
7818e56
+	return found_key;
7818e56
+}
7818e56
+#endif
7818e56
+
7818e56
+/* check whether given key is in 
7818e56
 int
7818e56
 user_key_allowed(struct passwd *pw, Key *key)
7818e56
 {
7818e56
 	int success;
7818e56
 	char *file;
7818e56
 
7818e56
+#ifdef WITH_AUTHORIZED_KEYS_COMMAND
7818e56
+	success = user_key_via_command_allowed2(pw, key);
7818e56
+	if (success > 0)
7818e56
+		return success;
7818e56
+#endif
7818e56
+
7818e56
 	if (auth_key_is_revoked(key))
7818e56
 		return 0;
7818e56
 	if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
7818e56
diff -ruN openssh-5.5p1.orig/configure.ac openssh-5.5p1/configure.ac
7818e56
--- openssh-5.5p1.orig/configure.ac	2010-04-10 08:58:01.000000000 -0400
7818e56
+++ openssh-5.5p1/configure.ac	2010-07-03 19:57:42.000000000 -0400
7818e56
@@ -1346,6 +1346,18 @@
7818e56
 	esac ]
7818e56
 )
7818e56
 
7818e56
+# Check whether user wants AuthorizedKeysCommand support
7818e56
+AKC_MSG="no"
7818e56
+AC_ARG_WITH(authorized-keys-command,
7818e56
+	[  --with-authorized-keys-command      Enable AuthorizedKeysCommand support],
7818e56
+	[
7818e56
+		if test "x$withval" != "xno" ; then
7818e56
+			AC_DEFINE([WITH_AUTHORIZED_KEYS_COMMAND], 1, [Enable AuthorizedKeysCommand support])
7818e56
+			AKC_MSG="yes"
7818e56
+		fi
7818e56
+	]
7818e56
+)
7818e56
+
7818e56
 dnl    Checks for library functions. Please keep in alphabetical order
7818e56
 AC_CHECK_FUNCS( \
7818e56
 	arc4random \
7818e56
@@ -4181,6 +4193,7 @@
7818e56
 echo "                 Smartcard support: $SCARD_MSG"
7818e56
 echo "                     S/KEY support: $SKEY_MSG"
7818e56
 echo "              TCP Wrappers support: $TCPW_MSG"
7818e56
+echo "     AuthorizedKeysCommand support: $AKC_MSG"
7818e56
 echo "              MD5 password support: $MD5_MSG"
7818e56
 echo "                   libedit support: $LIBEDIT_MSG"
7818e56
 echo "  Solaris process contract support: $SPC_MSG"
7818e56
diff -ruN openssh-5.5p1.orig/servconf.c openssh-5.5p1/servconf.c
7818e56
--- openssh-5.5p1.orig/servconf.c	2010-03-25 19:40:04.000000000 -0400
7818e56
+++ openssh-5.5p1/servconf.c	2010-07-03 19:59:07.000000000 -0400
7818e56
@@ -128,6 +128,8 @@
7818e56
 	options->num_permitted_opens = -1;
7818e56
 	options->adm_forced_command = NULL;
7818e56
 	options->chroot_directory = NULL;
7818e56
+	options->authorized_keys_command = NULL;
7818e56
+	options->authorized_keys_command_runas = NULL;
7818e56
 	options->zero_knowledge_password_authentication = -1;
7818e56
 	options->revoked_keys_file = NULL;
7818e56
 	options->trusted_user_ca_keys = NULL;
7818e56
@@ -311,6 +313,7 @@
7818e56
 	sUsePrivilegeSeparation, sAllowAgentForwarding,
7818e56
 	sZeroKnowledgePasswordAuthentication, sHostCertificate,
7818e56
 	sRevokedKeys, sTrustedUserCAKeys,
7818e56
+	sAuthorizedKeysCommand, sAuthorizedKeysCommandRunAs,
7818e56
 	sDeprecated, sUnsupported
7818e56
 } ServerOpCodes;
7818e56
 
7818e56
@@ -432,6 +435,13 @@
7818e56
 	{ "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
7818e56
 	{ "revokedkeys", sRevokedKeys, SSHCFG_ALL },
7818e56
 	{ "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
7818e56
+#ifdef WITH_AUTHORIZED_KEYS_COMMAND
7818e56
+	{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
7818e56
+	{ "authorizedkeyscommandrunas", sAuthorizedKeysCommandRunAs, SSHCFG_ALL },
7818e56
+#else
7818e56
+	{ "authorizedkeyscommand", sUnsupported, SSHCFG_ALL },
7818e56
+	{ "authorizedkeyscommandrunas", sUnsupported, SSHCFG_ALL },
7818e56
+#endif
7818e56
 	{ NULL, sBadOption, 0 }
7818e56
 };
7818e56
 
7818e56
@@ -1345,6 +1355,20 @@
7818e56
 		charptr = &options->revoked_keys_file;
7818e56
 		goto parse_filename;
7818e56
 
7818e56
+	case sAuthorizedKeysCommand:
7818e56
+		len = strspn(cp, WHITESPACE);
7818e56
+		if (*activep && options->authorized_keys_command == NULL)
7818e56
+			options->authorized_keys_command = xstrdup(cp + len);
7818e56
+		return 0;
7818e56
+
7818e56
+	case sAuthorizedKeysCommandRunAs:
7818e56
+		charptr = &options->authorized_keys_command_runas;
7818e56
+
7818e56
+		arg = strdelim(&cp;;
7818e56
+		if (*activep && *charptr == NULL)
7818e56
+			*charptr = xstrdup(arg);
7818e56
+		break;
7818e56
+
7818e56
 	case sDeprecated:
7818e56
 		logit("%s line %d: Deprecated option %s",
7818e56
 		    filename, linenum, arg);
7818e56
@@ -1438,6 +1462,8 @@
7818e56
 	M_CP_INTOPT(gss_authentication);
7818e56
 	M_CP_INTOPT(rsa_authentication);
7818e56
 	M_CP_INTOPT(pubkey_authentication);
7818e56
+	M_CP_STROPT(authorized_keys_command);
7818e56
+	M_CP_STROPT(authorized_keys_command_runas);
7818e56
 	M_CP_INTOPT(kerberos_authentication);
7818e56
 	M_CP_INTOPT(hostbased_authentication);
7818e56
 	M_CP_INTOPT(kbd_interactive_authentication);
7818e56
@@ -1682,6 +1708,8 @@
7818e56
 	dump_cfg_string(sChrootDirectory, o->chroot_directory);
7818e56
 	dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys);
7818e56
 	dump_cfg_string(sRevokedKeys, o->revoked_keys_file);
7818e56
+	dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
7818e56
+	dump_cfg_string(sAuthorizedKeysCommandRunAs, o->authorized_keys_command_runas);
7818e56
 
7818e56
 	/* string arguments requiring a lookup */
7818e56
 	dump_cfg_string(sLogLevel, log_level_name(o->log_level));
7818e56
diff -ruN openssh-5.5p1.orig/servconf.h openssh-5.5p1/servconf.h
7818e56
--- openssh-5.5p1.orig/servconf.h	2010-03-04 05:53:35.000000000 -0500
7818e56
+++ openssh-5.5p1/servconf.h	2010-07-03 19:57:42.000000000 -0400
7818e56
@@ -156,6 +156,8 @@
7818e56
 	char   *chroot_directory;
7818e56
 	char   *revoked_keys_file;
7818e56
 	char   *trusted_user_ca_keys;
7818e56
+	char   *authorized_keys_command;
7818e56
+	char   *authorized_keys_command_runas;
7818e56
 }       ServerOptions;
7818e56
 
7818e56
 void	 initialize_server_options(ServerOptions *);
7818e56
diff -ruN openssh-5.5p1.orig/sshd_config openssh-5.5p1/sshd_config
7818e56
--- openssh-5.5p1.orig/sshd_config	2009-10-11 06:51:09.000000000 -0400
7818e56
+++ openssh-5.5p1/sshd_config	2010-07-03 19:57:42.000000000 -0400
7818e56
@@ -44,6 +44,8 @@
7818e56
 #RSAAuthentication yes
7818e56
 #PubkeyAuthentication yes
7818e56
 #AuthorizedKeysFile	.ssh/authorized_keys
7818e56
+#AuthorizedKeysCommand none
7818e56
+#AuthorizedKeysCommandRunAs nobody
7818e56
 
7818e56
 # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
7818e56
 #RhostsRSAAuthentication no
7818e56
diff -ruN openssh-5.5p1.orig/sshd_config.0 openssh-5.5p1/sshd_config.0
7818e56
--- openssh-5.5p1.orig/sshd_config.0	2010-04-15 20:17:12.000000000 -0400
7818e56
+++ openssh-5.5p1/sshd_config.0	2010-07-03 19:57:42.000000000 -0400
7818e56
@@ -352,7 +352,8 @@
7818e56
              KbdInteractiveAuthentication, KerberosAuthentication,
7818e56
              MaxAuthTries, MaxSessions, PasswordAuthentication,
7818e56
              PermitEmptyPasswords, PermitOpen, PermitRootLogin,
7818e56
-             PubkeyAuthentication, RhostsRSAAuthentication, RSAAuthentication,
7818e56
+             PubkeyAuthentication, AuthorizedKeysCommand, AuthorizedKeysCommandRunAs,
7818e56
+             RhostsRSAAuthentication, RSAAuthentication,
7818e56
              X11DisplayOffset, X11Forwarding and X11UseLocalHost.
7818e56
 
7818e56
      MaxAuthTries
7818e56
@@ -467,6 +468,23 @@
7818e56
              this file is not readable, then public key authentication will be
7818e56
              refused for all users.
7818e56
 
7818e56
+     AuthorizedKeysCommand
7818e56
+
7818e56
+             Specifies a program to be used for lookup of the user's
7818e56
+	      public keys.  The program will be invoked with its first
7818e56
+	      argument the name of the user being authorized, and should produce 
7818e56
+	      on standard output AuthorizedKeys lines (see AUTHORIZED_KEYS 
7818e56
+	      in sshd(8)).  By default (or when set to the empty string) there is no
7818e56
+	      AuthorizedKeysCommand run.  If the AuthorizedKeysCommand does not successfully
7818e56
+	      authorize the user, authorization falls through to the
7818e56
+	      AuthorizedKeysFile.  Note that this option has an effect
7818e56
+	      only with PubkeyAuthentication turned on.
7818e56
+
7818e56
+     AuthorizedKeysCommandRunAs
7818e56
+             Specifies the user under whose account the AuthorizedKeysCommand is run.
7818e56
+             Empty string (the default value) means the user being authorized
7818e56
+             is used.
7818e56
+
7818e56
      RhostsRSAAuthentication
7818e56
              Specifies whether rhosts or /etc/hosts.equiv authentication to-
7818e56
              gether with successful RSA host authentication is allowed.  The
7818e56
diff -ruN openssh-5.5p1.orig/sshd_config.5 openssh-5.5p1/sshd_config.5
7818e56
--- openssh-5.5p1.orig/sshd_config.5	2010-03-04 18:41:45.000000000 -0500
7818e56
+++ openssh-5.5p1/sshd_config.5	2010-07-03 19:57:42.000000000 -0400
7818e56
@@ -618,6 +618,9 @@
7818e56
 .Cm KerberosAuthentication ,
7818e56
 .Cm MaxAuthTries ,
7818e56
 .Cm MaxSessions ,
7818e56
+.Cm PubkeyAuthentication ,
7818e56
+.Cm AuthorizedKeysCommand ,
7818e56
+.Cm AuthorizedKeysCommandRunAs ,
7818e56
 .Cm PasswordAuthentication ,
7818e56
 .Cm PermitEmptyPasswords ,
7818e56
 .Cm PermitOpen ,
7818e56
@@ -819,6 +822,20 @@
7818e56
 Keys listed in this file will be refused for public key authentication.
7818e56
 Note that if this file is not readable, then public key authentication will
7818e56
 be refused for all users.
7818e56
+.It Cm AuthorizedKeysCommand
7818e56
+Specifies a program to be used for lookup of the user's
7818e56
+public keys.  The program will be invoked with its first
7818e56
+argument the name of the user being authorized, and should produce 
7818e56
+on standard output AuthorizedKeys lines (see AUTHORIZED_KEYS 
7818e56
+in sshd(8)).  By default (or when set to the empty string) there is no
7818e56
+AuthorizedKeysCommand run.  If the AuthorizedKeysCommand does not successfully
7818e56
+authorize the user, authorization falls through to the
7818e56
+AuthorizedKeysFile.  Note that this option has an effect
7818e56
+only with PubkeyAuthentication turned on.
7818e56
+.It Cm AuthorizedKeysCommandRunAs
7818e56
+Specifies the user under whose account the AuthorizedKeysCommand is run. Empty
7818e56
+string (the default value) means the user being authorized is used.
7818e56
+.Dq 
7818e56
 .It Cm RhostsRSAAuthentication
7818e56
 Specifies whether rhosts or /etc/hosts.equiv authentication together
7818e56
 with successful RSA host authentication is allowed.