vishalmishra434 / rpms / openssh

Forked from rpms/openssh a month ago
Clone
Blob Blame History Raw
diff -up openssh-6.1p1/auth2-pubkey.c.akc openssh-6.1p1/auth2-pubkey.c
--- openssh-6.1p1/auth2-pubkey.c.akc	2012-09-14 20:20:48.459445650 +0200
+++ openssh-6.1p1/auth2-pubkey.c	2012-09-14 20:20:48.520446072 +0200
@@ -27,6 +27,7 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/wait.h>
 
 #include <fcntl.h>
 #include <pwd.h>
@@ -277,27 +278,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);
 
@@ -390,8 +379,6 @@ user_key_allowed2(struct passwd *pw, Key
 			break;
 		}
 	}
-	restore_uid();
-	fclose(f);
 	key_free(found);
 	if (!found_key)
 		debug2("key not found");
@@ -453,13 +440,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-6.1p1/configure.ac.akc openssh-6.1p1/configure.ac
--- openssh-6.1p1/configure.ac.akc	2012-07-06 03:49:29.000000000 +0200
+++ openssh-6.1p1/configure.ac	2012-09-14 20:20:48.525446106 +0200
@@ -1512,6 +1512,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 \
@@ -4407,6 +4419,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-6.1p1/servconf.c.akc openssh-6.1p1/servconf.c
--- openssh-6.1p1/servconf.c.akc	2012-09-14 20:20:48.138443423 +0200
+++ openssh-6.1p1/servconf.c	2012-09-14 20:27:34.546107295 +0200
@@ -139,6 +139,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;
@@ -334,6 +336,7 @@ typedef enum {
 	sZeroKnowledgePasswordAuthentication, sHostCertificate,
 	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
 	sKexAlgorithms, sIPQoS, sVersionAddendum,
+	sAuthorizedKeysCommand, sAuthorizedKeysCommandRunAs,
 	sDeprecated, sUnsupported
 } ServerOpCodes;
 
@@ -461,6 +464,14 @@ static struct {
 	{ "requiredauthentications2", sRequiredAuthentications2, SSHCFG_ALL },
 	{ "ipqos", sIPQoS, SSHCFG_ALL },
 	{ "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
+#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 }
 };
 
@@ -1532,6 +1543,24 @@ 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);
+		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);
@@ -1682,6 +1711,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);
 
@@ -1942,6 +1973,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(sAuthorizedKeysCommandRunAs, o->authorized_keys_command_runas);
 
 	/* 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-09-14 20:20:48.000000000 +0200
+++ openssh-6.1p1/servconf.h	2012-09-14 20:23:16.691844577 +0200
@@ -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;
 
 	char   *version_addendum;	/* Appended to SSH banner */
 }       ServerOptions;
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-09-14 20:30:46.950095769 +0200
@@ -49,6 +49,9 @@
 # but this is overridden so installations will only check .ssh/authorized_keys
 AuthorizedKeysFile	.ssh/authorized_keys
 
+#AuthorizedKeysCommand none
+#AuthorizedKeysCommandRunAs nobody
+
 #AuthorizedPrincipalsFile none
 
 # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
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-09-14 20:32:23.539624859 +0200
@@ -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-09-14 20:20:48.142443448 +0200
+++ openssh-6.1p1/sshd_config.5	2012-09-14 20:29:56.003873873 +0200
@@ -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.
@@ -712,6 +725,8 @@ Available keywords are
 .Cm AllowTcpForwarding ,
 .Cm AllowUsers ,
 .Cm AuthorizedKeysFile ,
+.Cm AuthorizedKeysCommand ,
+.Cm AuthorizedKeysCommandRunAs ,
 .Cm AuthorizedPrincipalsFile ,
 .Cm Banner ,
 .Cm ChrootDirectory ,
@@ -726,6 +741,7 @@ Available keywords are
 .Cm KerberosAuthentication ,
 .Cm MaxAuthTries ,
 .Cm MaxSessions ,
+.Cm PubkeyAuthentication ,
 .Cm PasswordAuthentication ,
 .Cm PermitEmptyPasswords ,
 .Cm PermitOpen ,