upstream commit
Add an AddKeysToAgent client option which can be set to
'yes', 'no', 'ask', or 'confirm', and defaults to 'no'. When enabled, a
private key that is used during authentication will be added to ssh-agent if
it is running (with confirmation enabled if set to 'confirm').
Initial version from Joachim Schipper many years ago.
ok markus@
Upstream-ID: a680db2248e8064ec55f8be72d539458c987d5f4
diff --git a/readconf.c b/readconf.c
index c062433..0a38091 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.245 2015/10/27 08:54:52 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.246 2015/11/15 22:26:49 jcs Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -135,7 +135,7 @@
oPasswordAuthentication, oRSAAuthentication,
oChallengeResponseAuthentication, oXAuthLocation,
oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
- oCertificateFile,
+ oCertificateFile, oAddKeysToAgent,
oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
@@ -204,6 +204,7 @@
{ "identityfile2", oIdentityFile }, /* obsolete */
{ "identitiesonly", oIdentitiesOnly },
{ "certificatefile", oCertificateFile },
+ { "addkeystoagent", oAddKeysToAgent },
{ "hostname", oHostName },
{ "hostkeyalias", oHostKeyAlias },
{ "proxycommand", oProxyCommand },
@@ -712,6 +713,15 @@
{ "ask", 2 },
{ NULL, -1 }
};
+static const struct multistate multistate_yesnoaskconfirm[] = {
+ { "true", 1 },
+ { "false", 0 },
+ { "yes", 1 },
+ { "no", 0 },
+ { "ask", 2 },
+ { "confirm", 3 },
+ { NULL, -1 }
+};
static const struct multistate multistate_addressfamily[] = {
{ "inet", AF_INET },
{ "inet6", AF_INET6 },
@@ -1533,6 +1543,11 @@
charptr = &options->pubkey_key_types;
goto parse_keytypes;
+ case oAddKeysToAgent:
+ intptr = &options->add_keys_to_agent;
+ multistate_ptr = multistate_yesnoaskconfirm;
+ goto parse_multistate;
+
case oDeprecated:
debug("%s line %d: Deprecated option \"%s\"",
filename, linenum, keyword);
@@ -1699,6 +1714,7 @@
options->local_command = NULL;
options->permit_local_command = -1;
options->use_roaming = -1;
+ options->add_keys_to_agent = -1;
options->visual_host_key = -1;
options->ip_qos_interactive = -1;
options->ip_qos_bulk = -1;
@@ -1803,6 +1819,8 @@
/* options->hostkeyalgorithms, default set in myproposals.h */
if (options->protocol == SSH_PROTO_UNKNOWN)
options->protocol = SSH_PROTO_2;
+ if (options->add_keys_to_agent == -1)
+ options->add_keys_to_agent = 0;
if (options->num_identity_files == 0) {
if (options->protocol & SSH_PROTO_1) {
add_identity_file(options, "~/",
diff --git a/readconf.h b/readconf.h
index 6d6927f..2034bfd 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.111 2015/09/24 06:15:11 djm Exp $ */
+/* $OpenBSD: readconf.h,v 1.112 2015/11/15 22:26:49 jcs Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -100,6 +100,8 @@
int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES];
struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES];
+ int add_keys_to_agent;
+
/* Local TCP/IP forward requests. */
int num_local_forwards;
struct Forward *local_forwards;
diff --git a/ssh-agent.1 b/ssh-agent.1
index 5a521cb..dabc5c4 100644
--- a/ssh-agent.1
+++ b/ssh-agent.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ssh-agent.1,v 1.60 2015/11/05 09:48:05 jmc Exp $
+.\" $OpenBSD: ssh-agent.1,v 1.61 2015/11/15 22:26:49 jcs Exp $
.\"
.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -34,7 +34,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: November 5 2015 $
+.Dd $Mdocdate: November 15 2015 $
.Dt SSH-AGENT 1
.Os
.Sh NAME
@@ -66,6 +66,13 @@
.Pp
The agent initially does not have any private keys.
Keys are added using
+.Xr ssh 1
+(see
+.Cm AddKeysToAgent
+in
+.Xr ssh_config 5
+for details)
+or
.Xr ssh-add 1 .
Multiple identities may be stored in
.Nm
diff --git a/ssh.1 b/ssh.1
index c4b7250..5b35b6c 100644
--- a/ssh.1
+++ b/ssh.1
@@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: ssh.1,v 1.365 2015/11/06 00:31:41 mmcc Exp $
-.Dd $Mdocdate: November 6 2015 $
+.\" $OpenBSD: ssh.1,v 1.366 2015/11/15 22:26:49 jcs Exp $
+.Dd $Mdocdate: November 15 2015 $
.Dt SSH 1
.Os
.Sh NAME
@@ -462,6 +462,7 @@
.Xr ssh_config 5 .
.Pp
.Bl -tag -width Ds -offset indent -compact
+.It AddKeysToAgent
.It AddressFamily
.It BatchMode
.It BindAddress
@@ -926,6 +927,10 @@
may be with an authentication agent.
See
.Xr ssh-agent 1
+and (optionally) the
+.Cm AddKeysToAgent
+directive in
+.Xr ssh_config 5
for more information.
.Pp
Challenge-response authentication works as follows:
diff --git a/ssh_config.5 b/ssh_config.5
index 39cf932..e6673b1 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: ssh_config.5,v 1.221 2015/09/24 06:15:11 djm Exp $
-.Dd $Mdocdate: September 24 2015 $
+.\" $OpenBSD: ssh_config.5,v 1.222 2015/11/15 22:26:49 jcs Exp $
+.Dd $Mdocdate: November 15 2015 $
.Dt SSH_CONFIG 5
.Os
.Sh NAME
@@ -221,6 +221,39 @@
(this keyword may be useful in system-wide
.Nm
files).
+.It Cm AddKeysToAgent
+Specifies whether keys should be automatically added to a running
+.Xr ssh-agent 5 .
+If this option is set to
+.Dq yes
+and a key is loaded from a file, the key and its passphrase are added to
+the agent with the default lifetime, as if by
+.Xr ssh-add 1 .
+If this option is set to
+.Dq ask ,
+.Nm ssh
+will require confirmation using the
+.Ev SSH_ASKPASS
+program before adding a key (see
+.Xr ssh-add 1
+for details).
+If this option is set to
+.Dq confirm ,
+each use of the key must be confirmed, as if the
+.Fl c
+option was specified to
+.Xr ssh-add 1 .
+If this option is set to
+.Dq no ,
+no keys are added to the agent.
+The argument must be
+.Dq yes ,
+.Dq confirm ,
+.Dq ask ,
+or
+.Dq no .
+The default is
+.Dq no .
.It Cm AddressFamily
Specifies which address family to use when connecting.
Valid arguments are
diff --git a/sshconnect.c b/sshconnect.c
index c9f88e0..19d393f 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect.c,v 1.265 2015/09/04 04:55:24 djm Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.266 2015/11/15 22:26:49 jcs Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -65,6 +65,7 @@
#include "version.h"
#include "authfile.h"
#include "ssherr.h"
+#include "authfd.h"
char *client_version_string = NULL;
char *server_version_string = NULL;
@@ -1487,3 +1488,30 @@
return (WEXITSTATUS(status));
}
+
+void
+maybe_add_key_to_agent(char *authfile, Key *private, char *comment,
+ char *passphrase)
+{
+ int auth_sock = -1, r;
+
+ if (options.add_keys_to_agent == 0)
+ return;
+
+ if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
+ debug3("no authentication agent, not adding key");
+ return;
+ }
+
+ if (options.add_keys_to_agent == 2 &&
+ !ask_permission("Add key %s (%s) to agent?", authfile, comment)) {
+ debug3("user denied adding this key");
+ return;
+ }
+
+ if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0,
+ (options.add_keys_to_agent == 3))) == 0)
+ debug("identity added to agent: %s", authfile);
+ else
+ debug("could not add identity to agent: %s (%d)", authfile, r);
+}
diff --git a/sshconnect.h b/sshconnect.h
index 0ea6e99..cf1851a 100644
--- a/sshconnect.h
+++ b/sshconnect.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect.h,v 1.28 2013/10/16 02:31:47 djm Exp $ */
+/* $OpenBSD: sshconnect.h,v 1.29 2015/11/15 22:26:49 jcs Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -55,6 +55,8 @@
void ssh_put_password(char *);
int ssh_local_cmd(const char *);
+void maybe_add_key_to_agent(char *, Key *, char *, char *);
+
/*
* Macros to raise/lower permissions.
*/
diff --git a/sshconnect1.c b/sshconnect1.c
index 016abbc..bfc523b 100644
--- a/sshconnect1.c
+++ b/sshconnect1.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect1.c,v 1.77 2015/01/14 20:05:27 djm Exp $ */
+/* $OpenBSD: sshconnect1.c,v 1.78 2015/11/15 22:26:49 jcs Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -221,7 +221,7 @@
{
BIGNUM *challenge;
Key *public, *private;
- char buf[300], *passphrase, *comment, *authfile;
+ char buf[300], *passphrase = NULL, *comment, *authfile;
int i, perm_ok = 1, type, quit;
public = options.identity_keys[idx];
@@ -283,13 +283,20 @@
debug2("no passphrase given, try next key");
quit = 1;
}
- explicit_bzero(passphrase, strlen(passphrase));
- free(passphrase);
if (private != NULL || quit)
break;
debug2("bad passphrase given, try again...");
}
}
+
+ if (private != NULL)
+ maybe_add_key_to_agent(authfile, private, comment, passphrase);
+
+ if (passphrase != NULL) {
+ explicit_bzero(passphrase, strlen(passphrase));
+ free(passphrase);
+ }
+
/* We no longer need the comment. */
free(comment);
diff --git a/sshconnect2.c b/sshconnect2.c
index 3ab686e..69d0bee 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect2.c,v 1.228 2015/10/13 16:15:21 djm Exp $ */
+/* $OpenBSD: sshconnect2.c,v 1.229 2015/11/15 22:26:49 jcs Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. All rights reserved.
@@ -313,7 +313,7 @@
static int sign_and_send_pubkey(Authctxt *, Identity *);
static void pubkey_prepare(Authctxt *);
static void pubkey_cleanup(Authctxt *);
-static Key *load_identity_file(char *, int);
+static Key *load_identity_file(Identity *);
static Authmethod *authmethod_get(char *authlist);
static Authmethod *authmethod_lookup(const char *name);
@@ -990,7 +990,7 @@
return (sshkey_sign(id->key, sigp, lenp, data, datalen,
compat));
/* load the private key from the file */
- if ((prv = load_identity_file(id->filename, id->userprovided)) == NULL)
+ if ((prv = load_identity_file(id)) == NULL)
return (-1); /* XXX return decent error code */
ret = sshkey_sign(prv, sigp, lenp, data, datalen, compat);
sshkey_free(prv);
@@ -1147,20 +1147,20 @@
}
static Key *
-load_identity_file(char *filename, int userprovided)
+load_identity_file(Identity *id)
{
Key *private;
- char prompt[300], *passphrase;
+ char prompt[300], *passphrase, *comment;
int r, perm_ok = 0, quit = 0, i;
struct stat st;
- if (stat(filename, &st) < 0) {
- (userprovided ? logit : debug3)("no such identity: %s: %s",
- filename, strerror(errno));
+ if (stat(id->filename, &st) < 0) {
+ (id->userprovided ? logit : debug3)("no such identity: %s: %s",
+ id->filename, strerror(errno));
return NULL;
}
snprintf(prompt, sizeof prompt,
- "Enter passphrase for key '%.100s': ", filename);
+ "Enter passphrase for key '%.100s': ", id->filename);
for (i = 0; i <= options.number_of_password_prompts; i++) {
if (i == 0)
passphrase = "";
@@ -1172,8 +1172,8 @@
break;
}
}
- switch ((r = sshkey_load_private_type(KEY_UNSPEC, filename,
- passphrase, &private, NULL, &perm_ok))) {
+ switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename,
+ passphrase, &private, &comment, &perm_ok))) {
case 0:
break;
case SSH_ERR_KEY_WRONG_PASSPHRASE:
@@ -1187,20 +1187,26 @@
case SSH_ERR_SYSTEM_ERROR:
if (errno == ENOENT) {
debug2("Load key \"%s\": %s",
- filename, ssh_err(r));
+ id->filename, ssh_err(r));
quit = 1;
break;
}
/* FALLTHROUGH */
default:
- error("Load key \"%s\": %s", filename, ssh_err(r));
+ error("Load key \"%s\": %s", id->filename, ssh_err(r));
quit = 1;
break;
}
+ if (!quit && private != NULL && !id->agent_fd &&
+ !(id->key && id->isprivate))
+ maybe_add_key_to_agent(id->filename, private, comment,
+ passphrase);
if (i > 0) {
explicit_bzero(passphrase, strlen(passphrase));
free(passphrase);
}
+ if (comment)
+ free(comment);
if (private != NULL || quit)
break;
}
@@ -1403,8 +1409,7 @@
}
} else {
debug("Trying private key: %s", id->filename);
- id->key = load_identity_file(id->filename,
- id->userprovided);
+ id->key = load_identity_file(id);
if (id->key != NULL) {
if (try_identity(id)) {
id->isprivate = 1;