File openssh-9.9p1-custom-3.patch of Package openssh

From 696316e858c31cd3c8c883b770fbf99308bd44aa Mon Sep 17 00:00:00 2001
From: Ciprian Dorin Craciun <ciprian@volution.ro>
Date: Fri, 7 Apr 2017 01:42:33 +0300
Subject: [PATCH 3/3] Add `PasswordCommand` option which overrides
 `SSH_ASKPASS` and should always provide a password  (this is used only for
 login passwords)

---
 misc.h        |  1 +
 readconf.c    |  9 +++++++
 readconf.h    |  1 +
 readpass.c    | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++
 ssh.c         | 10 ++++++++
 sshconnect2.c |  5 +++-
 6 files changed, 91 insertions(+), 1 deletion(-)

diff --git a/misc.h b/misc.h
index 113403896..f23515563 100644
--- a/misc.h
+++ b/misc.h
@@ -233,20 +233,21 @@ int ptimeout_isset(struct timespec *pt);
 /* readpass.c */
 
 #define RP_ECHO			0x0001
 #define RP_ALLOW_STDIN		0x0002
 #define RP_ALLOW_EOF		0x0004
 #define RP_USE_ASKPASS		0x0008
 
 struct notifier_ctx;
 
 char	*read_passphrase(const char *, int);
+char	*read_passphrase_from_command(const char *);
 int	 ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
 struct notifier_ctx *notify_start(int, const char *, ...)
 	__attribute__((format(printf, 2, 3)));
 void	notify_complete(struct notifier_ctx *, const char *, ...)
 	__attribute__((format(printf, 2, 3)));
 
 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
 #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
 #define ROUNDUP(x, y)   ((((x)+((y)-1))/(y))*(y))
 
diff --git a/readconf.c b/readconf.c
index 3d9cc6dbb..2393d4430 100644
--- a/readconf.c
+++ b/readconf.c
@@ -172,20 +172,21 @@ typedef enum {
 	oVisualHostKey,
 	oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType, oStdinNull,
 	oForkAfterAuthentication, oIgnoreUnknown, oProxyUseFdpass,
 	oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
 	oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
 	oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
 	oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms,
 	oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
 	oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize,
 	oEnableEscapeCommandline, oObscureKeystrokeTiming, oChannelTimeout,
+	oPasswordCommand,
 	oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
 } OpCodes;
 
 /* Textual representations of the tokens. */
 
 static struct {
 	const char *name;
 	OpCodes opcode;
 } keywords[] = {
 	/* Deprecated options */
@@ -322,20 +323,21 @@ static struct {
 	{ "pubkeyacceptedalgorithms", oPubkeyAcceptedAlgorithms },
 	{ "pubkeyacceptedkeytypes", oPubkeyAcceptedAlgorithms }, /* obsolete */
 	{ "ignoreunknown", oIgnoreUnknown },
 	{ "proxyjump", oProxyJump },
 	{ "securitykeyprovider", oSecurityKeyProvider },
 	{ "knownhostscommand", oKnownHostsCommand },
 	{ "requiredrsasize", oRequiredRSASize },
 	{ "enableescapecommandline", oEnableEscapeCommandline },
 	{ "obscurekeystroketiming", oObscureKeystrokeTiming },
 	{ "channeltimeout", oChannelTimeout },
+	{ "passwordcommand", oPasswordCommand },
 
 	{ NULL, oBadOption }
 };
 
 static const char *lookup_opcode_name(OpCodes code);
 
 const char *
 kex_default_pk_alg(void)
 {
 	static char *pkalgs;
@@ -2415,20 +2417,24 @@ parse_pubkey_algos:
 			    filename, linenum, keyword);
 		}
 		if (found && *activep) {
 			options->channel_timeouts = strs;
 			options->num_channel_timeouts = nstrs;
 			strs = NULL; /* transferred */
 			nstrs = 0;
 		}
 		break;
 
+	case oPasswordCommand:
+		charptr = &options->password_command;
+		goto parse_command;
+
 	case oDeprecated:
 		debug("%s line %d: Deprecated option \"%s\"",
 		    filename, linenum, keyword);
 		argv_consume(&ac);
 		break;
 
 	case oUnsupported:
 		error("%s line %d: Unsupported option \"%s\"",
 		    filename, linenum, keyword);
 		argv_consume(&ac);
@@ -2671,20 +2677,21 @@ initialize_options(Options * options)
 	options->update_hostkeys = -1;
 	options->hostbased_accepted_algos = NULL;
 	options->pubkey_accepted_algos = NULL;
 	options->known_hosts_command = NULL;
 	options->required_rsa_size = -1;
 	options->enable_escape_commandline = -1;
 	options->obscure_keystroke_timing_interval = -1;
 	options->tag = NULL;
 	options->channel_timeouts = NULL;
 	options->num_channel_timeouts = 0;
+	options->password_command = NULL;
 }
 
 /*
  * A petite version of fill_default_options() that just fills the options
  * needed for hostname canonicalization to proceed.
  */
 void
 fill_default_options_for_canonicalization(Options *options)
 {
 	if (options->canonicalize_max_dots == -1)
@@ -2932,20 +2939,21 @@ fill_default_options(Options * options)
 	} while (0)
 	CLEAR_ON_NONE(options->local_command);
 	CLEAR_ON_NONE(options->remote_command);
 	CLEAR_ON_NONE(options->proxy_command);
 	CLEAR_ON_NONE(options->control_path);
 	CLEAR_ON_NONE(options->revoked_host_keys);
 	CLEAR_ON_NONE(options->pkcs11_provider);
 	CLEAR_ON_NONE(options->sk_provider);
 	CLEAR_ON_NONE(options->known_hosts_command);
 	CLEAR_ON_NONE_ARRAY(channel_timeouts, num_channel_timeouts, "none");
+	CLEAR_ON_NONE(options->password_command);
 #undef CLEAR_ON_NONE
 #undef CLEAR_ON_NONE_ARRAY
 	if (options->jump_host != NULL &&
 	    strcmp(options->jump_host, "none") == 0 &&
 	    options->jump_port == 0 && options->jump_user == NULL) {
 		free(options->jump_host);
 		options->jump_host = NULL;
 	}
 	if (options->num_permitted_cnames == 1 &&
 	    !config_has_permitted_cnames(options)) {
@@ -3622,20 +3630,21 @@ dump_client_config(Options *o, const char *host)
 #ifdef ENABLE_PKCS11
 	dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
 #endif
 	dump_cfg_string(oSecurityKeyProvider, o->sk_provider);
 	dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
 	dump_cfg_string(oPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos);
 	dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys);
 	dump_cfg_string(oXAuthLocation, o->xauth_location);
 	dump_cfg_string(oKnownHostsCommand, o->known_hosts_command);
 	dump_cfg_string(oTag, o->tag);
+	dump_cfg_string(oPasswordCommand, o->password_command);
 
 	/* Forwards */
 	dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards);
 	dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards);
 	dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards);
 
 	/* String array options */
 	dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files);
 	dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains);
 	dump_cfg_strarray(oCertificateFile, o->num_certificate_files, o->certificate_files);
diff --git a/readconf.h b/readconf.h
index 9447d5d6e..13ea04ec7 100644
--- a/readconf.h
+++ b/readconf.h
@@ -170,20 +170,21 @@ typedef struct {
 
 	char   *hostbased_accepted_algos;
 	char   *pubkey_accepted_algos;
 
 	char   *jump_user;
 	char   *jump_host;
 	int	jump_port;
 	char   *jump_extra;
 
 	char   *known_hosts_command;
+	char   *password_command;
 
 	int	required_rsa_size;	/* minimum size of RSA keys */
 	int	enable_escape_commandline;	/* ~C commandline */
 	int	obscure_keystroke_timing_interval;
 
 	char	**channel_timeouts;	/* inactivity timeout by channel type */
 	u_int	num_channel_timeouts;
 
 	char	*ignored_unknown; /* Pattern list of unknown tokens to ignore */
 }       Options;
diff --git a/readpass.c b/readpass.c
index d42b1185d..aba598734 100644
--- a/readpass.c
+++ b/readpass.c
@@ -324,10 +324,76 @@ notify_complete(struct notifier_ctx *ctx, const char *fmt, ...)
 	kill(ctx->pid, SIGTERM);
 	while ((ret = waitpid(ctx->pid, NULL, 0)) == -1) {
 		if (errno != EINTR)
 			break;
 	}
 	if (ret == -1)
 		fatal_f("waitpid: %s", strerror(errno));
 	ssh_signal(SIGCHLD, ctx->osigchld);
 	free(ctx);
 }
+
+char *
+read_passphrase_from_command(const char *command)
+{
+	char *shell;
+	pid_t pid, ret;
+	size_t len;
+	char *pass;
+	int p[2], status;
+	char buf[1024];
+	void (*osigchld)(int);
+
+	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
+		shell = _PATH_BSHELL;
+
+	if (fflush(stdout) != 0)
+		error("password_command: fflush: %s", strerror(errno));
+	if (shell == NULL)
+		fatal("internal error: shell undefined");
+	if (pipe(p) < 0) {
+		error("password_command: pipe: %s", strerror(errno));
+		return NULL;
+	}
+	osigchld = signal(SIGCHLD, SIG_DFL);
+	if ((pid = fork()) < 0) {
+		error("password_command: fork: %s", strerror(errno));
+		signal(SIGCHLD, osigchld);
+		return NULL;
+	}
+	if (pid == 0) {
+		close(p[0]);
+		if (dup2(p[1], STDOUT_FILENO) < 0)
+			fatal("password_command: dup2: %s", strerror(errno));
+		debug3("Executing (via shell %s): %s", shell, command);
+		execlp(shell, shell, "-c", command, (char *)NULL);
+		fatal("password_command: exec(%s): %s", command, strerror(errno));
+	}
+	close(p[1]);
+
+	len = 0;
+	do {
+		ssize_t r = read(p[0], buf + len, sizeof(buf) - 1 - len);
+
+		if (r == -1 && errno == EINTR)
+			continue;
+		if (r <= 0)
+			break;
+		len += r;
+	} while (sizeof(buf) - 1 - len > 0);
+	buf[len] = '\0';
+
+	close(p[0]);
+	while ((ret = waitpid(pid, &status, 0)) < 0)
+		if (errno != EINTR)
+			break;
+	signal(SIGCHLD, osigchld);
+	if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+		explicit_bzero(buf, sizeof(buf));
+		return NULL;
+	}
+
+	buf[strcspn(buf, "\r\n")] = '\0';
+	pass = xstrdup(buf);
+	explicit_bzero(buf, sizeof(buf));
+	return pass;
+}
diff --git a/ssh.c b/ssh.c
index 0019281f4..7732f194e 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1448,20 +1448,30 @@ main(int ac, char **av)
 		cp = options.remote_command;
 		options.remote_command = default_client_percent_expand(cp,
 		    cinfo);
 		debug3("expanded RemoteCommand: %s", options.remote_command);
 		free(cp);
 		if ((r = sshbuf_put(command, options.remote_command,
 		    strlen(options.remote_command))) != 0)
 			fatal_fr(r, "buffer error");
 	}
 
+	if (options.password_command != NULL) {
+		debug3("expanding PasswordCommand: %s", options.password_command);
+		cp = options.password_command;
+		options.password_command = default_client_percent_expand(cp,
+		    cinfo);
+		debug3("expanded PasswordCommand: %s", options.password_command);
+		free(cp);
+	}
+
+
 	if (options.control_path != NULL) {
 		cp = tilde_expand_filename(options.control_path, getuid());
 		free(options.control_path);
 		options.control_path = default_client_percent_dollar_expand(cp,
 		    cinfo);
 		free(cp);
 	}
 
 	if (options.identity_agent != NULL) {
 		p = tilde_expand_filename(options.identity_agent, getuid());
diff --git a/sshconnect2.c b/sshconnect2.c
index 11fcdea8a..b29e5cb15 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1037,21 +1037,24 @@ userauth_passwd(struct ssh *ssh)
 	    authctxt->host;
 	int r;
 
 	if (authctxt->attempt_passwd++ >= options.number_of_password_prompts)
 		return 0;
 
 	if (authctxt->attempt_passwd != 1)
 		error("Permission denied, please try again.");
 
 	xasprintf(&prompt, "%s@%s's password: ", authctxt->server_user, host);
-	password = read_passphrase(prompt, 0);
+	if (options.password_command == NULL)
+		password = read_passphrase(prompt, 0);
+	else
+		password = read_passphrase_from_command(options.password_command);
 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
 	    (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
 	    (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
 	    (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
 	    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
 	    (r = sshpkt_put_cstring(ssh, password)) != 0 ||
 	    (r = sshpkt_add_padding(ssh, 64)) != 0 ||
 	    (r = sshpkt_send(ssh)) != 0)
 		fatal_fr(r, "send packet");
 
-- 
2.47.1

openSUSE Build Service is sponsored by