diff -up openssh-5.9p1/auth2-pubkey.c.akc openssh-5.9p1/auth2-pubkey.c
--- openssh-5.9p1/auth2-pubkey.c.akc 2012-02-06 20:47:36.641814218 +0100
+++ openssh-5.9p1/auth2-pubkey.c 2012-02-06 20:47:36.665095838 +0100
@@ -27,6 +27,7 @@
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <fcntl.h>
#include <pwd.h>
@@ -276,27 +277,15 @@ match_principals_file(char *file, struct
/* 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];
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);
@@ -389,8 +378,6 @@ user_key_allowed2(struct passwd *pw, Key
break;
}
}
- restore_uid();
- fclose(f);
key_free(found);
if (!found_key)
debug2("key not found");
@@ -452,13 +439,191 @@ user_cert_trusted_ca(struct passwd *pw,
return ret;
}
-/* 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 0;
+
+ /* 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)
{
u_int success, i;
char *file;
+#ifdef WITH_AUTHORIZED_KEYS_COMMAND
+ success = user_key_via_command_allowed2(pw, key);
+ if (success > 0)
+ return success;
+#endif
+
if (auth_key_is_revoked(key))
return 0;
if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
diff -up openssh-5.9p1/configure.ac.akc openssh-5.9p1/configure.ac
--- openssh-5.9p1/configure.ac.akc 2012-02-06 20:47:36.656046570 +0100
+++ openssh-5.9p1/configure.ac 2012-02-06 20:47:36.666095176 +0100
@@ -1421,6 +1421,18 @@ AC_ARG_WITH([audit],
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 \
@@ -4239,6 +4251,7 @@ echo " SELinux support
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"
diff -up openssh-5.9p1/servconf.c.akc openssh-5.9p1/servconf.c
--- openssh-5.9p1/servconf.c.akc 2012-02-06 20:47:36.573033521 +0100
+++ openssh-5.9p1/servconf.c 2012-02-06 20:47:36.667106367 +0100
@@ -136,6 +136,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_runas = NULL;
options->zero_knowledge_password_authentication = -1;
options->revoked_keys_file = NULL;
options->trusted_user_ca_keys = NULL;
@@ -329,6 +331,7 @@ typedef enum {
sZeroKnowledgePasswordAuthentication, sHostCertificate,
sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
sKexAlgorithms, sIPQoS,
+ sAuthorizedKeysCommand, sAuthorizedKeysCommandRunAs,
sDeprecated, sUnsupported
} ServerOpCodes;
@@ -455,6 +458,13 @@ static struct {
{ "requiredauthentications1", sRequiredAuthentications1, SSHCFG_ALL },
{ "requiredauthentications2", sRequiredAuthentications2, SSHCFG_ALL },
{ "ipqos", sIPQoS, 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 }
};
@@ -1430,6 +1440,24 @@ process_server_config_line(ServerOptions
}
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 (!arg || *arg == '\0')
+ fatal("%s line %d: missing account.",
+ filename, linenum);
+
+ if (*activep && *charptr == NULL)
+ *charptr = xstrdup(arg);
+ break;
+
case sDeprecated:
logit("%s line %d: Deprecated option %s",
filename, linenum, arg);
@@ -1534,6 +1562,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_runas);
M_CP_INTOPT(permit_root_login);
M_CP_INTOPT(permit_empty_passwd);
@@ -1793,6 +1823,8 @@ dump_config(ServerOptions *o)
dump_cfg_string(sRevokedKeys, o->revoked_keys_file);
dump_cfg_string(sAuthorizedPrincipalsFile,
o->authorized_principals_file);
+ 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));
diff -up openssh-5.9p1/servconf.h.akc openssh-5.9p1/servconf.h
--- openssh-5.9p1/servconf.h.akc 2012-02-06 20:47:36.574033734 +0100
+++ openssh-5.9p1/servconf.h 2012-02-06 20:47:36.668096740 +0100
@@ -169,6 +169,8 @@ typedef struct {
char *revoked_keys_file;
char *trusted_user_ca_keys;
char *authorized_principals_file;
+ char *authorized_keys_command;
+ char *authorized_keys_command_runas;
} ServerOptions;
/*
diff -up openssh-5.9p1/sshd_config.akc openssh-5.9p1/sshd_config
--- openssh-5.9p1/sshd_config.akc 2011-05-29 13:39:39.000000000 +0200
+++ openssh-5.9p1/sshd_config 2012-02-06 20:47:36.669067546 +0100
@@ -49,6 +49,9 @@
# but this is overridden so installations will only check .ssh/authorized_keys
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
diff -up openssh-5.9p1/sshd_config.0.akc openssh-5.9p1/sshd_config.0
--- openssh-5.9p1/sshd_config.0.akc 2011-09-07 01:16:30.000000000 +0200
+++ openssh-5.9p1/sshd_config.0 2012-02-06 20:47:36.669067546 +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
@@ -401,7 +418,8 @@ DESCRIPTION
Only a subset of keywords may be used on the lines following a
Match keyword. Available keywords are AllowAgentForwarding,
- AllowTcpForwarding, AuthorizedKeysFile, AuthorizedPrincipalsFile,
+ AllowTcpForwarding, AuthorizedKeysFile, AuthorizedKeysCommand,
+ AuthorizedKeysCommandRunAs, AuthorizedPrincipalsFile,
Banner, ChrootDirectory, ForceCommand, GatewayPorts,
GSSAPIAuthentication, HostbasedAuthentication,
HostbasedUsesNameFromPacketOnly, KbdInteractiveAuthentication,
diff -up openssh-5.9p1/sshd_config.5.akc openssh-5.9p1/sshd_config.5
--- openssh-5.9p1/sshd_config.5.akc 2012-02-06 20:47:36.574891218 +0100
+++ openssh-5.9p1/sshd_config.5 2012-02-06 20:49:58.913878595 +0100
@@ -151,6 +151,19 @@ See
in
.Xr ssh_config 5
for more information on patterns.
+.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.
.It Cm AuthorizedKeysFile
Specifies the file that contains the public keys that can be used
for user authentication.
@@ -706,6 +719,8 @@ Available keywords are
.Cm AllowAgentForwarding ,
.Cm AllowTcpForwarding ,
.Cm AuthorizedKeysFile ,
+.Cm AuthorizedKeysCommand ,
+.Cm AuthorizedKeysCommandRunAs ,
.Cm AuthorizedPrincipalsFile ,
.Cm Banner ,
.Cm ChrootDirectory ,
@@ -718,6 +733,7 @@ Available keywords are
.Cm KerberosAuthentication ,
.Cm MaxAuthTries ,
.Cm MaxSessions ,
+.Cm PubkeyAuthentication ,
.Cm PasswordAuthentication ,
.Cm PermitEmptyPasswords ,
.Cm PermitOpen ,