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
This commit is contained in:
jcs@openbsd.org 2015-11-15 22:26:49 +00:00 committed by Damien Miller
parent d87063d9ba
commit f361df474c
9 changed files with 137 additions and 30 deletions

View File

@ -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 @@ typedef enum {
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 @@ static struct {
{ "identityfile2", oIdentityFile }, /* obsolete */
{ "identitiesonly", oIdentitiesOnly },
{ "certificatefile", oCertificateFile },
{ "addkeystoagent", oAddKeysToAgent },
{ "hostname", oHostName },
{ "hostkeyalias", oHostKeyAlias },
{ "proxycommand", oProxyCommand },
@ -712,6 +713,15 @@ static const struct multistate multistate_yesnoask[] = {
{ "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 @@ parse_keytypes:
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 @@ initialize_options(Options * options)
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 @@ fill_default_options(Options * options)
/* 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, "~/",

View File

@ -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 @@ typedef struct {
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;

View File

@ -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 @@ machines using
.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

9
ssh.1
View File

@ -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 @@ For full details of the options listed below, and their possible values, see
.Xr ssh_config 5 .
.Pp
.Bl -tag -width Ds -offset indent -compact
.It AddKeysToAgent
.It AddressFamily
.It BatchMode
.It BindAddress
@ -926,6 +927,10 @@ The most convenient way to use public key or certificate authentication
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:

View File

@ -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 @@ keyword matches against the name of the local user running
(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

View File

@ -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 @@ ssh_local_cmd(const char *args)
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);
}

View File

@ -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_userauth2(const char *, const char *, char *, Sensitive *);
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.
*/

View File

@ -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 @@ try_rsa_authentication(int idx)
{
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 @@ try_rsa_authentication(int idx)
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);

View File

@ -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 @@ void userauth(Authctxt *, char *);
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 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
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 @@ send_pubkey_test(Authctxt *authctxt, Identity *id)
}
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 @@ load_identity_file(char *filename, int userprovided)
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 @@ load_identity_file(char *filename, int userprovided)
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 @@ userauth_pubkey(Authctxt *authctxt)
}
} 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;