# infrastructure for reading user public key via helper application
# based on:
#   https://bugzilla.mindrot.org/show_bug.cgi?id=1663
#   https://bugzilla.mindrot.org/attachment.cgi?id=1920
#   by jchadima@redhat.com
diff -Nadpru8 openssh-5.1p1/auth2-pubkey.c openssh-5.1p1/auth2-pubkey.c
--- openssh-5.1p1/auth2-pubkey.c	2011-08-01 05:02:31.771388073 +0200
+++ openssh-5.1p1/auth2-pubkey.c	2011-08-01 06:30:01.505900591 +0200
@@ -22,21 +22,23 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "includes.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/wait.h>
 
 #include <fcntl.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdarg.h>
+#include <string.h>
 #include <unistd.h>
 
 #include "xmalloc.h"
 #include "ssh.h"
 #include "ssh2.h"
 #include "packet.h"
 #include "buffer.h"
 #include "log.h"
@@ -186,36 +187,24 @@ user_key_verify(const Key *key, const u_
 #ifdef SSH_AUDIT_EVENTS
 	audit_key(1, &rv, key);
 #endif
 	return rv;
 }
 
 /* return 1 if user allows given key */
 static int
-user_key_allowed2(struct passwd *pw, Key *key, char *file)
+user_search_key_in_file(FILE *f, char *file, Key* key, struct passwd *pw)
 {
 	char line[SSH_MAX_PUBKEY_BYTES];
 	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->type);
 
 	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
 		char *cp, *key_options = NULL;
 
 		/* Skip leading whitespace, empty and comment lines. */
 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
@@ -250,31 +239,207 @@ user_key_allowed2(struct passwd *pw, Key
 			    file, linenum);
 			fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
 			verbose("Found matching %s key: %s",
 			    key_type(found), fp);
 			xfree(fp);
 			break;
 		}
 	}
-	restore_uid();
-	fclose(f);
 	key_free(found);
 	if (!found_key)
 		debug2("key not found");
 	return found_key;
 }
 
-/* check whether given key is in .ssh/authorized_keys* */
+/* return 1 if user allows given key */
+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);
+	f = auth_openkeyfile(file, pw, options.strict_modes);
+
+ 	if (f) {
+ 		found_key = user_search_key_in_file (f, file, key, pw);
+		fclose(f);
+	}
+
+	restore_uid();
+	return found_key;
+}
+
+#ifdef WITH_AUTHORIZED_KEYS_COMMAND
+
+#define WHITESPACE " \t\r\n"
+
+/* return 1 if user allows given key */
+static int
+user_key_via_command_allowed2(struct passwd *pw, Key *key)
+{
+	FILE *f;
+	int found_key = 0;
+	char *progname = NULL;
+	char *cp;
+	struct passwd *runas_pw;
+	struct stat st;
+	int childdescriptors[2], i;
+	pid_t pstat, pid, child;
+
+	if (options.authorized_keys_command == NULL || options.authorized_keys_command[0] != '/')
+		return -1;
+
+	/* get the run as identity from config */
+	runas_pw = (options.authorized_keys_command_runas == NULL)? pw
+	    : getpwnam (options.authorized_keys_command_runas);
+	if (!runas_pw) {
+		error("%s: getpwnam(\"%s\"): %s", __func__,
+		    options.authorized_keys_command_runas, strerror(errno));
+		return 0;
+	}
+
+	/* Temporarily use the specified uid. */
+	if (runas_pw->pw_uid != 0)
+		temporarily_use_uid(runas_pw);
+
+	progname = xstrdup(options.authorized_keys_command);
+
+	debug3("%s: checking program '%s'", __func__, progname);
+
+	if (stat (progname, &st) < 0) {
+		error("%s: stat(\"%s\"): %s", __func__,
+		    progname, strerror(errno));
+		goto go_away;
+	}
+
+	if (st.st_uid != 0 || (st.st_mode & 022) != 0) {
+		error("bad ownership or modes for AuthorizedKeysCommand \"%s\"",
+		    progname);
+		goto go_away;
+	}
+
+	if (!S_ISREG(st.st_mode)) {
+		error("AuthorizedKeysCommand \"%s\" is not a regular file",
+		    progname);
+		goto go_away;
+	}
+
+	/*
+	 * Descend the path, checking that each component is a
+	 * root-owned directory with strict permissions.
+	 */
+	do {
+		if ((cp = strrchr(progname, '/')) == NULL)
+			break;
+		else 
+			*cp = '\0';
+	
+		debug3("%s: checking component '%s'", __func__, (*progname == '\0' ? "/" : progname));
+
+		if (stat((*progname == '\0' ? "/" : progname), &st) != 0) {
+			error("%s: stat(\"%s\"): %s", __func__,
+			    progname, strerror(errno));
+			goto go_away;
+		}
+		if (st.st_uid != 0 || (st.st_mode & 022) != 0) {
+			error("bad ownership or modes for AuthorizedKeysCommand path component \"%s\"",
+			    progname);
+			goto go_away;
+		}
+		if (!S_ISDIR(st.st_mode)) {
+			error("AuthorizedKeysCommand path component \"%s\" is not a directory",
+			    progname);
+			goto go_away;
+		}
+	} while (1);
+
+	/* open the pipe and read the keys */
+	if (pipe(childdescriptors)) {
+		error("failed to pipe(2) for AuthorizedKeysCommand: %s",
+		    strerror(errno));
+		goto go_away;
+	}
+
+	child = fork();
+	if (child == -1) {
+		error("failed to fork(2) for AuthorizedKeysCommand: %s",
+		    strerror(errno));
+		goto go_away;
+	} else if (child == 0) {
+		/* we're in the child process here -- we should never return from this block. */
+		/* permanently drop privs in child process */
+		if (runas_pw->pw_uid != 0) {
+			restore_uid();
+			permanently_set_uid(runas_pw);
+	  	}
+
+		close(childdescriptors[0]);
+		/* put the write end of the pipe on stdout (FD 1) */
+		if (dup2(childdescriptors[1], 1) == -1) {
+			error("failed to dup2(2) from AuthorizedKeysCommand: %s",
+			    strerror(errno));
+			_exit(127);
+		}
+
+		debug3("about to execl() AuthorizedKeysCommand: \"%s\" \"%s\"", options.authorized_keys_command, pw->pw_name);
+		/* see session.c:child_close_fds() */
+		for (i = 3; i < 64; ++i) {
+			close(i);
+		}
+
+		execl(options.authorized_keys_command, options.authorized_keys_command, pw->pw_name, NULL);
+
+		/* if we got here, it didn't work */
+		error("failed to execl AuthorizedKeysCommand: %s", strerror(errno)); /* this won't work because we closed the fds above */
+		_exit(127);
+	}
+	
+	close(childdescriptors[1]);
+	f = fdopen(childdescriptors[0], "r");
+	if (!f) {
+		error("%s: could not buffer FDs from AuthorizedKeysCommand (\"%s\", \"r\"): %s", __func__,
+		    options.authorized_keys_command, strerror (errno));
+		goto go_away;
+	}
+
+	found_key = user_search_key_in_file (f, options.authorized_keys_command, key, pw);
+	fclose (f);
+	do {
+		pid = waitpid(child, &pstat, 0);
+	} while (pid == -1 && errno == EINTR);
+
+	/* what about the return value from the child process? */
+go_away:
+	if (progname)
+		xfree (progname);
+
+	if (runas_pw->pw_uid != 0)
+		restore_uid();
+	return found_key;
+}
+#endif
+
+/* check whether given key is in <AuthorizedKeysCommand or .ssh/authorized_keys* */
 int
 user_key_allowed(struct passwd *pw, Key *key)
 {
 	int success;
 	char *file;
 
+#ifdef WITH_AUTHORIZED_KEYS_COMMAND
+	success = user_key_via_command_allowed2(pw, key);
+	if (success > 0)
+		return success;
+#endif
+
 	file = authorized_keys_file(pw);
 	success = user_key_allowed2(pw, key, file);
 	xfree(file);
 	if (success)
 		return success;
 
 	/* try suffix "2" for backward compat, too */
 	file = authorized_keys_file2(pw);
diff -Nadpru8 openssh-5.1p1/configure.ac openssh-5.1p1/configure.ac
--- openssh-5.1p1/configure.ac	2011-08-01 05:02:31.760388062 +0200
+++ openssh-5.1p1/configure.ac	2011-08-01 06:30:01.506900592 +0200
@@ -1302,16 +1302,28 @@ AC_ARG_WITH(audit,
 		AC_MSG_RESULT(no)
 		;;
 	  *)
 		AC_MSG_ERROR([Unknown audit module $withval])
 		;;
 	esac ]
 )
 
+# Check whether user wants AuthorizedKeysCommand support
+AKC_MSG="no"
+AC_ARG_WITH(authorized-keys-command,
+	[  --with-authorized-keys-command      Enable AuthorizedKeysCommand support],
+	[
+		if test "x$withval" != "xno" ; then
+			AC_DEFINE([WITH_AUTHORIZED_KEYS_COMMAND], 1, [Enable AuthorizedKeysCommand support])
+			AKC_MSG="yes"
+		fi
+	]
+)
+
 dnl    Checks for library functions. Please keep in alphabetical order
 AC_CHECK_FUNCS( \
 	arc4random \
 	arc4random_buf \
 	arc4random_uniform \
 	asprintf \
 	b64_ntop \
 	__b64_ntop \
@@ -4156,16 +4168,17 @@ echo "                    Manpage format
 echo "                       PAM support: $PAM_MSG"
 echo "                   OSF SIA support: $SIA_MSG"
 echo "                 KerberosV support: $KRB5_MSG"
 echo "                   SELinux support: $SELINUX_MSG"
 echo "               Linux audit support: $LINUX_AUDIT_MSG"
 echo "                 Smartcard support: $SCARD_MSG"
 echo "                     S/KEY support: $SKEY_MSG"
 echo "              TCP Wrappers support: $TCPW_MSG"
+echo "     AuthorizedKeysCommand support: $AKC_MSG"
 echo "              MD5 password support: $MD5_MSG"
 echo "                   libedit support: $LIBEDIT_MSG"
 echo "  Solaris process contract support: $SPC_MSG"
 echo "       IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG"
 echo "           Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG"
 echo "                  BSD Auth support: $BSD_AUTH_MSG"
 echo "              Random number source: $RAND_MSG"
 if test ! -z "$USE_RAND_HELPER" ; then
diff -Nadpru8 openssh-5.1p1/servconf.c openssh-5.1p1/servconf.c
--- openssh-5.1p1/servconf.c	2011-08-01 05:02:31.713388015 +0200
+++ openssh-5.1p1/servconf.c	2011-08-01 06:30:01.507900593 +0200
@@ -123,16 +123,18 @@ initialize_server_options(ServerOptions
 	options->client_alive_count_max = -1;
 	options->authorized_keys_file = NULL;
 	options->authorized_keys_file2 = NULL;
 	options->num_accept_env = 0;
 	options->permit_tun = -1;
 	options->num_permitted_opens = -1;
 	options->adm_forced_command = NULL;
 	options->chroot_directory = NULL;
+	options->authorized_keys_command = NULL;
+	options->authorized_keys_command_runas = NULL;
 }
 
 void
 fill_default_server_options(ServerOptions *options)
 {
 	/* Portable-specific options */
 	if (options->use_pam == -1)
 		options->use_pam = 0;
@@ -300,16 +302,17 @@ typedef enum {
 	sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem,
 	sMaxStartups, sMaxAuthTries, sMaxSessions,
 	sBanner, sUseDNS, sHostbasedAuthentication,
 	sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
 	sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
 	sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, sGssEnableMITM,
 	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
 	sUsePrivilegeSeparation, sAllowAgentForwarding,
+	sAuthorizedKeysCommand, sAuthorizedKeysCommandRunAs,
 	sDeprecated, sUnsupported
 } ServerOpCodes;
 
 #define SSHCFG_GLOBAL	0x01	/* allowed in main section of sshd_config */
 #define SSHCFG_MATCH	0x02	/* allowed inside a Match section */
 #define SSHCFG_ALL	(SSHCFG_GLOBAL|SSHCFG_MATCH)
 
 /* Textual representation of the tokens. */
@@ -415,16 +418,23 @@ static struct {
 	{ "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL },
 	{ "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL },
 	{ "acceptenv", sAcceptEnv, SSHCFG_GLOBAL },
 	{ "permittunnel", sPermitTunnel, SSHCFG_GLOBAL },
  	{ "match", sMatch, SSHCFG_ALL },
 	{ "permitopen", sPermitOpen, SSHCFG_ALL },
 	{ "forcecommand", sForceCommand, SSHCFG_ALL },
 	{ "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
+#ifdef WITH_AUTHORIZED_KEYS_COMMAND
+	{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
+	{ "authorizedkeyscommandrunas", sAuthorizedKeysCommandRunAs, SSHCFG_ALL },
+#else
+	{ "authorizedkeyscommand", sUnsupported, SSHCFG_ALL },
+	{ "authorizedkeyscommandrunas", sUnsupported, SSHCFG_ALL },
+#endif
 	{ NULL, sBadOption, 0 }
 };
 
 static struct {
 	int val;
 	char *text;
 } tunmode_desc[] = {
 	{ SSH_TUNMODE_NO, "no" },
@@ -1285,16 +1295,30 @@ process_server_config_line(ServerOptions
 		arg = strdelim(&cp);
 		if (!arg || *arg == '\0')
 			fatal("%s line %d: missing file name.",
 			    filename, linenum);
 		if (*activep && *charptr == NULL)
 			*charptr = xstrdup(arg);
 		break;
 
+	case sAuthorizedKeysCommand:
+		len = strspn(cp, WHITESPACE);
+		if (*activep && options->authorized_keys_command == NULL)
+			options->authorized_keys_command = xstrdup(cp + len);
+		return 0;
+
+	case sAuthorizedKeysCommandRunAs:
+		charptr = &options->authorized_keys_command_runas;
+
+		arg = strdelim(&cp);
+		if (*activep && *charptr == NULL)
+			*charptr = xstrdup(arg);
+		break;
+
 	case sDeprecated:
 		logit("%s line %d: Deprecated option %s",
 		    filename, linenum, arg);
 		while (arg)
 		    arg = strdelim(&cp);
 		break;
 
 	case sUnsupported:
@@ -1378,16 +1402,18 @@ parse_server_match_config(ServerOptions
  */
 void
 copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
 {
 	M_CP_INTOPT(password_authentication);
 	M_CP_INTOPT(gss_authentication);
 	M_CP_INTOPT(rsa_authentication);
 	M_CP_INTOPT(pubkey_authentication);
+	M_CP_STROPT(authorized_keys_command);
+	M_CP_STROPT(authorized_keys_command_runas);
 	M_CP_INTOPT(kerberos_authentication);
 	M_CP_INTOPT(hostbased_authentication);
 	M_CP_INTOPT(kbd_interactive_authentication);
 	M_CP_INTOPT(permit_root_login);
 
 	M_CP_INTOPT(allow_tcp_forwarding);
 	M_CP_INTOPT(allow_agent_forwarding);
 	M_CP_INTOPT(gateway_ports);
@@ -1601,16 +1627,18 @@ dump_config(ServerOptions *o)
 	dump_cfg_string(sPidFile, o->pid_file);
 	dump_cfg_string(sXAuthLocation, o->xauth_location);
 	dump_cfg_string(sCiphers, o->ciphers);
 	dump_cfg_string(sMacs, o->macs);
 	dump_cfg_string(sBanner, o->banner);
 	dump_cfg_string(sAuthorizedKeysFile, o->authorized_keys_file);
 	dump_cfg_string(sAuthorizedKeysFile2, o->authorized_keys_file2);
 	dump_cfg_string(sForceCommand, o->adm_forced_command);
+	dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
+	dump_cfg_string(sAuthorizedKeysCommandRunAs, o->authorized_keys_command_runas);
 
 	/* string arguments requiring a lookup */
 	dump_cfg_string(sLogLevel, log_level_name(o->log_level));
 	dump_cfg_string(sLogFacility, log_facility_name(o->log_facility));
 
 	/* string array arguments */
 	dump_cfg_strarray(sHostKeyFile, o->num_host_key_files,
 	     o->host_key_files);
diff -Nadpru8 openssh-5.1p1/servconf.h openssh-5.1p1/servconf.h
--- openssh-5.1p1/servconf.h	2011-08-01 05:02:31.713388015 +0200
+++ openssh-5.1p1/servconf.h	2011-08-01 06:30:01.507900593 +0200
@@ -145,16 +145,18 @@ typedef struct {
 
 	int	use_pam;		/* Enable auth via PAM */
 
 	int	permit_tun;
 
 	int	num_permitted_opens;
 
 	char   *chroot_directory;
+	char   *authorized_keys_command;
+	char   *authorized_keys_command_runas;
 }       ServerOptions;
 
 void	 initialize_server_options(ServerOptions *);
 void	 fill_default_server_options(ServerOptions *);
 int	 process_server_config_line(ServerOptions *, char *, const char *, int,
 	     int *, const char *, const char *, const char *);
 void	 load_server_config(const char *, Buffer *);
 void	 parse_server_config(ServerOptions *, const char *, Buffer *,
diff -Nadpru8 openssh-5.1p1/sshd_config openssh-5.1p1/sshd_config
--- openssh-5.1p1/sshd_config	2011-08-01 05:02:31.723388025 +0200
+++ openssh-5.1p1/sshd_config	2011-08-01 06:30:01.508900594 +0200
@@ -41,16 +41,18 @@ Protocol 2
 #PermitRootLogin yes
 #StrictModes yes
 #MaxAuthTries 6
 #MaxSessions 10
 
 #RSAAuthentication yes
 #PubkeyAuthentication yes
 #AuthorizedKeysFile	.ssh/authorized_keys
+#AuthorizedKeysCommand none
+#AuthorizedKeysCommandRunAs nobody
 
 # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
 #RhostsRSAAuthentication no
 # similar for protocol version 2
 #HostbasedAuthentication no
 # Change to yes if you don't trust ~/.ssh/known_hosts for
 # RhostsRSAAuthentication and HostbasedAuthentication
 #IgnoreUserKnownHosts no
diff -Nadpru8 openssh-5.1p1/sshd_config.0 openssh-5.1p1/sshd_config.0
--- openssh-5.1p1/sshd_config.0	2008-07-21 10:30:51.000000000 +0200
+++ openssh-5.1p1/sshd_config.0	2011-08-01 06:30:01.508900594 +0200
@@ -66,16 +66,33 @@ DESCRIPTION
              is allowed for all users.  If the pattern takes the form US-
              ER@HOST then USER and HOST are separately checked, restricting
              logins to particular users from particular hosts.  The allow/deny
              directives are processed in the following order: DenyUsers,
              AllowUsers, DenyGroups, and finally AllowGroups.
 
              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.  AuthorizedKeysFile may contain tokens
              of the form %T which are substituted during connection setup.
              The following tokens are defined: %% is replaced by a literal
              '%', %h is replaced by the home directory of the user being au-
              thenticated, and %u is replaced by the username of that user.
              After expansion, AuthorizedKeysFile is taken to be an absolute
@@ -333,16 +350,17 @@ DESCRIPTION
              ``192.0.2.0/24'' or ``3ffe:ffff::/32''.  Note that the mask
              length provided must be consistent with the address - it is an
              error to specify a mask length that is too long for the address
              or one with bits set in this host portion of the address.  For
              example, ``192.0.2.0/33'' and ``192.0.2.0/8'' respectively.
 
              Only a subset of keywords may be used on the lines following a
              Match keyword.  Available keywords are AllowTcpForwarding,
+             AuthorizedKeysCommand, AuthorizedKeysCommandRunAs,
              Banner, ChrootDirectory, ForceCommand, GatewayPorts,
              GSSAPIAuthentication, HostbasedAuthentication,
              KbdInteractiveAuthentication, KerberosAuthentication,
              MaxAuthTries, MaxSessions, PasswordAuthentication, PermitOpen,
              PermitRootLogin, RhostsRSAAuthentication, RSAAuthentication,
              X11DisplayOffset, X11Forwarding, and X11UseLocalHost.
 
      MaxAuthTries
diff -Nadpru8 openssh-5.1p1/sshd_config.5 openssh-5.1p1/sshd_config.5
--- openssh-5.1p1/sshd_config.5	2011-08-01 05:02:31.716388018 +0200
+++ openssh-5.1p1/sshd_config.5	2011-08-01 06:30:01.508900594 +0200
@@ -586,26 +586,29 @@ and
 .Dq 192.0.2.0/8
 respectively.
 .Pp
 Only a subset of keywords may be used on the lines following a
 .Cm Match
 keyword.
 Available keywords are
 .Cm AllowTcpForwarding ,
+.Cm AuthorizedKeysCommand ,
+.Cm AuthorizedKeysCommandRunAs ,
 .Cm Banner ,
 .Cm ChrootDirectory ,
 .Cm ForceCommand ,
 .Cm GatewayPorts ,
 .Cm GSSAPIAuthentication ,
 .Cm HostbasedAuthentication ,
 .Cm KbdInteractiveAuthentication ,
 .Cm KerberosAuthentication ,
 .Cm MaxAuthTries ,
 .Cm MaxSessions ,
+.Cm PubkeyAuthentication ,
 .Cm PasswordAuthentication ,
 .Cm PermitOpen ,
 .Cm PermitRootLogin ,
 .Cm RhostsRSAAuthentication ,
 .Cm RSAAuthentication ,
 .Cm X11DisplayOffset ,
 .Cm X11Forwarding ,
 and
@@ -790,16 +793,30 @@ Specifying
 .Dq 2,1
 is identical to
 .Dq 1,2 .
 .It Cm PubkeyAuthentication
 Specifies whether public key authentication is allowed.
 The default is
 .Dq yes .
 Note that this option applies to protocol version 2 only.
+.It Cm 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.
+.It Cm AuthorizedKeysCommandRunAs
+Specifies the user under whose account the AuthorizedKeysCommand is run. Empty
+string (the default value) means the user being authorized is used.
+.Dq 
 .It Cm RhostsRSAAuthentication
 Specifies whether rhosts or /etc/hosts.equiv authentication together
 with successful RSA host authentication is allowed.
 The default is
 .Dq no .
 This option applies to protocol version 1 only.
 .It Cm RSAAuthentication
 Specifies whether pure RSA authentication is allowed.
