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> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -135,7 +135,7 @@ typedef enum {
oPasswordAuthentication, oRSAAuthentication, oPasswordAuthentication, oRSAAuthentication,
oChallengeResponseAuthentication, oXAuthLocation, oChallengeResponseAuthentication, oXAuthLocation,
oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
oCertificateFile, oCertificateFile, oAddKeysToAgent,
oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
@ -204,6 +204,7 @@ static struct {
{ "identityfile2", oIdentityFile }, /* obsolete */ { "identityfile2", oIdentityFile }, /* obsolete */
{ "identitiesonly", oIdentitiesOnly }, { "identitiesonly", oIdentitiesOnly },
{ "certificatefile", oCertificateFile }, { "certificatefile", oCertificateFile },
{ "addkeystoagent", oAddKeysToAgent },
{ "hostname", oHostName }, { "hostname", oHostName },
{ "hostkeyalias", oHostKeyAlias }, { "hostkeyalias", oHostKeyAlias },
{ "proxycommand", oProxyCommand }, { "proxycommand", oProxyCommand },
@ -712,6 +713,15 @@ static const struct multistate multistate_yesnoask[] = {
{ "ask", 2 }, { "ask", 2 },
{ NULL, -1 } { 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[] = { static const struct multistate multistate_addressfamily[] = {
{ "inet", AF_INET }, { "inet", AF_INET },
{ "inet6", AF_INET6 }, { "inet6", AF_INET6 },
@ -1533,6 +1543,11 @@ parse_keytypes:
charptr = &options->pubkey_key_types; charptr = &options->pubkey_key_types;
goto parse_keytypes; goto parse_keytypes;
case oAddKeysToAgent:
intptr = &options->add_keys_to_agent;
multistate_ptr = multistate_yesnoaskconfirm;
goto parse_multistate;
case oDeprecated: case oDeprecated:
debug("%s line %d: Deprecated option \"%s\"", debug("%s line %d: Deprecated option \"%s\"",
filename, linenum, keyword); filename, linenum, keyword);
@ -1699,6 +1714,7 @@ initialize_options(Options * options)
options->local_command = NULL; options->local_command = NULL;
options->permit_local_command = -1; options->permit_local_command = -1;
options->use_roaming = -1; options->use_roaming = -1;
options->add_keys_to_agent = -1;
options->visual_host_key = -1; options->visual_host_key = -1;
options->ip_qos_interactive = -1; options->ip_qos_interactive = -1;
options->ip_qos_bulk = -1; options->ip_qos_bulk = -1;
@ -1803,6 +1819,8 @@ fill_default_options(Options * options)
/* options->hostkeyalgorithms, default set in myproposals.h */ /* options->hostkeyalgorithms, default set in myproposals.h */
if (options->protocol == SSH_PROTO_UNKNOWN) if (options->protocol == SSH_PROTO_UNKNOWN)
options->protocol = SSH_PROTO_2; 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->num_identity_files == 0) {
if (options->protocol & SSH_PROTO_1) { if (options->protocol & SSH_PROTO_1) {
add_identity_file(options, "~/", 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> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -100,6 +100,8 @@ typedef struct {
int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES]; int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES];
struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES];
int add_keys_to_agent;
/* Local TCP/IP forward requests. */ /* Local TCP/IP forward requests. */
int num_local_forwards; int num_local_forwards;
struct Forward *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> .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland .\" 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 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" 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 .Dt SSH-AGENT 1
.Os .Os
.Sh NAME .Sh NAME
@ -66,6 +66,13 @@ machines using
.Pp .Pp
The agent initially does not have any private keys. The agent initially does not have any private keys.
Keys are added using Keys are added using
.Xr ssh 1
(see
.Cm AddKeysToAgent
in
.Xr ssh_config 5
for details)
or
.Xr ssh-add 1 . .Xr ssh-add 1 .
Multiple identities may be stored in Multiple identities may be stored in
.Nm .Nm

9
ssh.1
View File

@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" 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 $ .\" $OpenBSD: ssh.1,v 1.366 2015/11/15 22:26:49 jcs Exp $
.Dd $Mdocdate: November 6 2015 $ .Dd $Mdocdate: November 15 2015 $
.Dt SSH 1 .Dt SSH 1
.Os .Os
.Sh NAME .Sh NAME
@ -462,6 +462,7 @@ For full details of the options listed below, and their possible values, see
.Xr ssh_config 5 . .Xr ssh_config 5 .
.Pp .Pp
.Bl -tag -width Ds -offset indent -compact .Bl -tag -width Ds -offset indent -compact
.It AddKeysToAgent
.It AddressFamily .It AddressFamily
.It BatchMode .It BatchMode
.It BindAddress .It BindAddress
@ -926,6 +927,10 @@ The most convenient way to use public key or certificate authentication
may be with an authentication agent. may be with an authentication agent.
See See
.Xr ssh-agent 1 .Xr ssh-agent 1
and (optionally) the
.Cm AddKeysToAgent
directive in
.Xr ssh_config 5
for more information. for more information.
.Pp .Pp
Challenge-response authentication works as follows: 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 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" 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 $ .\" $OpenBSD: ssh_config.5,v 1.222 2015/11/15 22:26:49 jcs Exp $
.Dd $Mdocdate: September 24 2015 $ .Dd $Mdocdate: November 15 2015 $
.Dt SSH_CONFIG 5 .Dt SSH_CONFIG 5
.Os .Os
.Sh NAME .Sh NAME
@ -221,6 +221,39 @@ keyword matches against the name of the local user running
(this keyword may be useful in system-wide (this keyword may be useful in system-wide
.Nm .Nm
files). 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 .It Cm AddressFamily
Specifies which address family to use when connecting. Specifies which address family to use when connecting.
Valid arguments are 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> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -65,6 +65,7 @@
#include "version.h" #include "version.h"
#include "authfile.h" #include "authfile.h"
#include "ssherr.h" #include "ssherr.h"
#include "authfd.h"
char *client_version_string = NULL; char *client_version_string = NULL;
char *server_version_string = NULL; char *server_version_string = NULL;
@ -1487,3 +1488,30 @@ ssh_local_cmd(const char *args)
return (WEXITSTATUS(status)); 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. * 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 *); void ssh_put_password(char *);
int ssh_local_cmd(const char *); int ssh_local_cmd(const char *);
void maybe_add_key_to_agent(char *, Key *, char *, char *);
/* /*
* Macros to raise/lower permissions. * 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> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -221,7 +221,7 @@ try_rsa_authentication(int idx)
{ {
BIGNUM *challenge; BIGNUM *challenge;
Key *public, *private; Key *public, *private;
char buf[300], *passphrase, *comment, *authfile; char buf[300], *passphrase = NULL, *comment, *authfile;
int i, perm_ok = 1, type, quit; int i, perm_ok = 1, type, quit;
public = options.identity_keys[idx]; public = options.identity_keys[idx];
@ -283,13 +283,20 @@ try_rsa_authentication(int idx)
debug2("no passphrase given, try next key"); debug2("no passphrase given, try next key");
quit = 1; quit = 1;
} }
explicit_bzero(passphrase, strlen(passphrase));
free(passphrase);
if (private != NULL || quit) if (private != NULL || quit)
break; break;
debug2("bad passphrase given, try again..."); 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. */ /* We no longer need the comment. */
free(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) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. 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 int sign_and_send_pubkey(Authctxt *, Identity *);
static void pubkey_prepare(Authctxt *); static void pubkey_prepare(Authctxt *);
static void pubkey_cleanup(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_get(char *authlist);
static Authmethod *authmethod_lookup(const char *name); 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, return (sshkey_sign(id->key, sigp, lenp, data, datalen,
compat)); compat));
/* load the private key from the file */ /* 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 */ return (-1); /* XXX return decent error code */
ret = sshkey_sign(prv, sigp, lenp, data, datalen, compat); ret = sshkey_sign(prv, sigp, lenp, data, datalen, compat);
sshkey_free(prv); sshkey_free(prv);
@ -1147,20 +1147,20 @@ send_pubkey_test(Authctxt *authctxt, Identity *id)
} }
static Key * static Key *
load_identity_file(char *filename, int userprovided) load_identity_file(Identity *id)
{ {
Key *private; Key *private;
char prompt[300], *passphrase; char prompt[300], *passphrase, *comment;
int r, perm_ok = 0, quit = 0, i; int r, perm_ok = 0, quit = 0, i;
struct stat st; struct stat st;
if (stat(filename, &st) < 0) { if (stat(id->filename, &st) < 0) {
(userprovided ? logit : debug3)("no such identity: %s: %s", (id->userprovided ? logit : debug3)("no such identity: %s: %s",
filename, strerror(errno)); id->filename, strerror(errno));
return NULL; return NULL;
} }
snprintf(prompt, sizeof prompt, 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++) { for (i = 0; i <= options.number_of_password_prompts; i++) {
if (i == 0) if (i == 0)
passphrase = ""; passphrase = "";
@ -1172,8 +1172,8 @@ load_identity_file(char *filename, int userprovided)
break; break;
} }
} }
switch ((r = sshkey_load_private_type(KEY_UNSPEC, filename, switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename,
passphrase, &private, NULL, &perm_ok))) { passphrase, &private, &comment, &perm_ok))) {
case 0: case 0:
break; break;
case SSH_ERR_KEY_WRONG_PASSPHRASE: case SSH_ERR_KEY_WRONG_PASSPHRASE:
@ -1187,20 +1187,26 @@ load_identity_file(char *filename, int userprovided)
case SSH_ERR_SYSTEM_ERROR: case SSH_ERR_SYSTEM_ERROR:
if (errno == ENOENT) { if (errno == ENOENT) {
debug2("Load key \"%s\": %s", debug2("Load key \"%s\": %s",
filename, ssh_err(r)); id->filename, ssh_err(r));
quit = 1; quit = 1;
break; break;
} }
/* FALLTHROUGH */ /* FALLTHROUGH */
default: default:
error("Load key \"%s\": %s", filename, ssh_err(r)); error("Load key \"%s\": %s", id->filename, ssh_err(r));
quit = 1; quit = 1;
break; 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) { if (i > 0) {
explicit_bzero(passphrase, strlen(passphrase)); explicit_bzero(passphrase, strlen(passphrase));
free(passphrase); free(passphrase);
} }
if (comment)
free(comment);
if (private != NULL || quit) if (private != NULL || quit)
break; break;
} }
@ -1403,8 +1409,7 @@ userauth_pubkey(Authctxt *authctxt)
} }
} else { } else {
debug("Trying private key: %s", id->filename); debug("Trying private key: %s", id->filename);
id->key = load_identity_file(id->filename, id->key = load_identity_file(id);
id->userprovided);
if (id->key != NULL) { if (id->key != NULL) {
if (try_identity(id)) { if (try_identity(id)) {
id->isprivate = 1; id->isprivate = 1;