My keys are secured with a passphrase. That's good for security, but
having to type the passphrase either at every login or at every
invocation of ssh(1) is annoying.
I know I could invoke ssh-add(1) just before invoking ssh(1), if I keep
track of whether I invoked it already, or write some hacky scripts; but
the rest of OpenSSH is wonderfully usable without any hacks.
Hence, this patch. I'll just quote ssh_config(5):
AddKeyToAgent
If this option is set to ``yes'' and ssh-agent(1) is running, any
keys unlocked with a password will be added to the agent (with
the default lifetime). Setting this to ``ask'' will cause ssh to
require confirmation using the SSH_ASKPASS program before the key
is added (see ssh-add(1) for details). The argument must be
``yes'', ``ask'', or ``no''. The default is
``no''.
Having more knobs isn't really useful, IMHO. Default lifetime is
configurable via ssh-agent(1)'s -t flag, and if you want to confirm each
key use you should be willing to live without this convenience feature.
By the way, are there plans to replace ask_permission() (also used for
other "ask" type options, e.g. ControlMaster) by something a little
more user-friendly? Having to type "yes" works, but isn't exactly
elegant. (Not volunteering here, I know nothing about X.)
Please be gentle, but inspect thoroughly, as this is my first patch.
Joachim
P.S. Note that the patch to authfile.c I just posted should be applied
before testing this patch.
Index: readconf.c
==================================================================RCS file:
/usr/obsd-repos/src/usr.bin/ssh/readconf.c,v
retrieving revision 1.182
diff -u -N -p readconf.c
--- readconf.c 9 Jan 2010 23:04:13 -0000 1.182
+++ readconf.c 11 Jan 2010 22:19:10 -0000
@@ -128,7 +128,7 @@ typedef enum {
oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
- oDeprecated, oUnsupported
+ oAddKey, oDeprecated, oUnsupported
} OpCodes;
/* Textual representations of the tokens. */
@@ -232,6 +232,7 @@ static struct {
#else
{ "zeroknowledgepasswordauthentication", oUnsupported },
#endif
+ { "addkeytoagent", oAddKey },
{ NULL, oBadOption }
};
@@ -914,6 +915,10 @@ parse_int:
intptr = &options->use_roaming;
goto parse_flag;
+ case oAddKey:
+ intptr = &options->add_key;
+ goto parse_yesnoask;
+
case oDeprecated:
debug("%s line %d: Deprecated option \"%s\"",
filename, linenum, keyword);
@@ -1064,6 +1069,7 @@ initialize_options(Options * options)
options->local_command = NULL;
options->permit_local_command = -1;
options->use_roaming = -1;
+ options->add_key = -1;
options->visual_host_key = -1;
options->zero_knowledge_password_authentication = -1;
}
@@ -1202,6 +1208,8 @@ fill_default_options(Options * options)
options->permit_local_command = 0;
if (options->use_roaming == -1)
options->use_roaming = 1;
+ if (options->add_key == -1)
+ options->add_key = 0;
if (options->visual_host_key == -1)
options->visual_host_key = 0;
if (options->zero_knowledge_password_authentication == -1)
Index: readconf.h
==================================================================RCS file:
/usr/obsd-repos/src/usr.bin/ssh/readconf.h,v
retrieving revision 1.81
diff -u -N -p readconf.h
--- readconf.h 9 Jan 2010 23:04:13 -0000 1.81
+++ readconf.h 11 Jan 2010 22:19:18 -0000
@@ -125,6 +125,8 @@ typedef struct {
int use_roaming;
+ int add_key; /* add keys to agent */
+
} Options;
#define SSHCTL_MASTER_NO 0
Index: ssh-agent.1
==================================================================RCS file:
/usr/obsd-repos/src/usr.bin/ssh/ssh-agent.1,v
retrieving revision 1.49
diff -u -N -p ssh-agent.1
--- ssh-agent.1 22 Oct 2009 15:02:12 -0000 1.49
+++ ssh-agent.1 11 Jan 2010 23:39:47 -0000
@@ -109,6 +109,13 @@ When the command dies, so does the agent.
.Pp
The agent initially does not have any private keys.
Keys are added using
+.Xr ssh 1
+(see
+.Cm AddKeyToAgent
+in
+.Xr ssh_config 5
+for details)
+or
.Xr ssh-add 1 .
When executed without arguments,
.Xr ssh-add 1
Index: ssh.1
==================================================================RCS file:
/usr/obsd-repos/src/usr.bin/ssh/ssh.1,v
retrieving revision 1.290
diff -u -N -p ssh.1
--- ssh.1 11 Jan 2010 01:39:46 -0000 1.290
+++ ssh.1 11 Jan 2010 23:14:55 -0000
@@ -428,6 +428,7 @@ For full details of the options listed below, and thei
.Xr ssh_config 5 .
.Pp
.Bl -tag -width Ds -offset indent -compact
+.It AddKeyToAgent
.It AddressFamily
.It BatchMode
.It BindAddress
@@ -803,6 +804,10 @@ The most convenient way to use public key authenticati
authentication agent.
See
.Xr ssh-agent 1
+and (optionally) the
+.Cm AddKeyToAgent
+directive in
+.Xr ssh_config 5
for more information.
.Pp
Challenge-response authentication works as follows:
Index: ssh_config.5
==================================================================RCS file:
/usr/obsd-repos/src/usr.bin/ssh/ssh_config.5,v
retrieving revision 1.126
diff -u -N -p ssh_config.5
--- ssh_config.5 9 Jan 2010 23:04:13 -0000 1.126
+++ ssh_config.5 11 Jan 2010 23:31:21 -0000
@@ -116,6 +116,27 @@ a canonicalized host name before matching).
See
.Sx PATTERNS
for more information on patterns.
+.It Cm AddKeyToAgent
+If this option is set to
+.Dq yes
+and
+.Xr ssh-agent 1
+is running, any keys unlocked with a password will be added to the agent (with
+the default lifetime).
+Setting this to
+.Dq ask
+will cause ssh to require confirmation using the
+.Ev SSH_ASKPASS
+program before the key is added (see
+.Xr ssh-add 1
+for details).
+The argument must be
+.Dq yes ,
+.Dq ask ,
+or
+.Dq no .
+The default is
+.Dq no .
.It Cm AddressFamily
Specifies which address family to use when connecting.
Valid arguments are
Index: sshconnect1.c
==================================================================RCS file:
/usr/obsd-repos/src/usr.bin/ssh/sshconnect1.c,v
retrieving revision 1.70
diff -u -N -p sshconnect1.c
--- sshconnect1.c 6 Nov 2006 21:25:28 -0000 1.70
+++ sshconnect1.c 11 Jan 2010 22:49:11 -0000
@@ -57,21 +57,15 @@ extern char *__progname;
* authenticate using the agent.
*/
static int
-try_agent_authentication(void)
+try_agent_authentication(AuthenticationConnection *auth)
{
int type;
char *comment;
- AuthenticationConnection *auth;
u_char response[16];
u_int i;
Key *key;
BIGNUM *challenge;
- /* Get connection to the agent. */
- auth = ssh_get_authentication_connection();
- if (!auth)
- return 0;
-
if ((challenge = BN_new()) == NULL)
fatal("try_agent_authentication: BN_new failed");
/* Loop through identities served by the agent. */
@@ -134,7 +128,6 @@ try_agent_authentication(void)
/* The server returns success if it accepted the authentication. */
if (type == SSH_SMSG_SUCCESS) {
- ssh_close_authentication_connection(auth);
BN_clear_free(challenge);
debug("RSA authentication accepted by server.");
return 1;
@@ -144,7 +137,6 @@ try_agent_authentication(void)
packet_disconnect("Protocol error waiting RSA auth response: %d",
type);
}
- ssh_close_authentication_connection(auth);
BN_clear_free(challenge);
debug("RSA authentication using agent refused.");
return 0;
@@ -200,7 +192,7 @@ respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv
* the user using it.
*/
static int
-try_rsa_authentication(int idx)
+try_rsa_authentication(int idx, AuthenticationConnection *auth)
{
BIGNUM *challenge;
Key *public, *private;
@@ -293,6 +285,19 @@ try_rsa_authentication(int idx)
return 0;
}
+ /*
+ * Consider adding key to agent. We add keys for the default lifetime
+ * with no need to confirm each use.
+ */
+ if (auth != NULL && (options.add_key == 1 ||
+ (options.add_key == 2 &&
+ ask_permission("Add key %s (%s) to agent?", authfile,
comment)))) {
+ if (ssh_add_identity_constrained(auth, private, comment, 0, 0))
+ debug("Identity added: %s (%s)", authfile, comment);
+ else
+ verbose("Error while adding identity!");
+ }
+
/* Compute and send a response to the challenge. */
respond_to_rsa_challenge(challenge, private->rsa);
@@ -670,6 +675,7 @@ ssh_userauth1(const char *local_user, const char *serv
Sensitive *sensitive)
{
int i, type;
+ AuthenticationConnection *auth = NULL;
if (supported_authentications == 0)
fatal("ssh_userauth1: server supports no auth methods");
@@ -715,14 +721,15 @@ ssh_userauth1(const char *local_user, const char *serv
* agent is tried first because no passphrase is needed for
* it, whereas identity files may require passphrases.
*/
- if (try_agent_authentication())
+ auth = ssh_get_authentication_connection();
+ if (auth != NULL && try_agent_authentication(auth))
goto success;
/* Try RSA authentication for each identity. */
for (i = 0; i < options.num_identity_files; i++)
if (options.identity_keys[i] != NULL &&
options.identity_keys[i]->type == KEY_RSA1 &&
- try_rsa_authentication(i))
+ try_rsa_authentication(i, auth))
goto success;
}
/* Try challenge response authentication if the server supports it. */
@@ -746,5 +753,6 @@ ssh_userauth1(const char *local_user, const char *serv
/* NOTREACHED */
success:
- return; /* need statement after label */
+ if (auth)
+ ssh_close_authentication_connection(auth);
}
Index: sshconnect2.c
==================================================================RCS file:
/usr/obsd-repos/src/usr.bin/ssh/sshconnect2.c,v
retrieving revision 1.178
diff -u -N -p sshconnect2.c
--- sshconnect2.c 11 Jan 2010 04:46:45 -0000 1.178
+++ sshconnect2.c 11 Jan 2010 23:12:38 -0000
@@ -244,7 +244,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 *);
+static Key *load_identity_file(char *, AuthenticationConnection *);
static Authmethod *authmethod_get(char *authlist);
static Authmethod *authmethod_lookup(const char *name);
@@ -1102,7 +1102,7 @@ input_userauth_jpake_server_confirm(int type, u_int32_
static int
identity_sign(Identity *id, u_char **sigp, u_int *lenp,
- u_char *data, u_int datalen)
+ u_char *data, u_int datalen, AuthenticationConnection *auth)
{
Key *prv;
int ret;
@@ -1118,7 +1118,7 @@ identity_sign(Identity *id, u_char **sigp, u_int *lenp
if (id->isprivate || (id->key->flags & KEY_FLAG_EXT))
return (key_sign(id->key, sigp, lenp, data, datalen));
/* load the private key from the file */
- if ((prv = load_identity_file(id->filename)) == NULL)
+ if ((prv = load_identity_file(id->filename, auth)) == NULL)
return (-1);
ret = key_sign(prv, sigp, lenp, data, datalen);
key_free(prv);
@@ -1168,7 +1168,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id)
/* generate signature */
ret = identity_sign(id, &signature, &slen,
- buffer_ptr(&b), buffer_len(&b));
+ buffer_ptr(&b), buffer_len(&b), authctxt->agent);
if (ret == -1) {
xfree(blob);
buffer_free(&b);
@@ -1240,30 +1240,36 @@ send_pubkey_test(Authctxt *authctxt, Identity *id)
}
static Key *
-load_identity_file(char *filename)
+load_identity_file(char *filename, AuthenticationConnection *ac)
{
Key *private;
- char prompt[300], *passphrase;
- int perm_ok = 0, quit, i;
+ char prompt[300], *passphrase, *comment = NULL;
+ int perm_ok = 0, quit, i, allowed = 0;
struct stat st;
if (stat(filename, &st) < 0) {
debug3("no such identity: %s", filename);
return NULL;
}
- private = key_load_private_type(KEY_UNSPEC, filename, "", NULL,
&perm_ok);
- if (!perm_ok)
+ private = key_load_private_type(KEY_UNSPEC, filename, "",
&comment, &perm_ok);
+ if (!perm_ok) {
+ if (comment)
+ xfree(comment);
return NULL;
+ }
if (private == NULL) {
- if (options.batch_mode)
+ if (options.batch_mode) {
+ if (comment)
+ xfree(comment);
return NULL;
+ }
snprintf(prompt, sizeof prompt,
"Enter passphrase for key '%.100s': ", filename);
for (i = 0; i < options.number_of_password_prompts; i++) {
passphrase = read_passphrase(prompt, 0);
if (strcmp(passphrase, "") != 0) {
private = key_load_private_type(KEY_UNSPEC,
- filename, passphrase, NULL, NULL);
+ filename, passphrase, &comment, NULL);
quit = 0;
} else {
debug2("no passphrase given, try next key");
@@ -1273,9 +1279,39 @@ load_identity_file(char *filename)
xfree(passphrase);
if (private != NULL || quit)
break;
+ if (comment)
+ xfree(comment);
debug2("bad passphrase given, try again...");
}
}
+
+ /* If we loaded the key and have an agent, consider adding key. */
+ if (private == NULL || ac == NULL) {
+ if (comment)
+ xfree(comment);
+ return private;
+ }
+ if (options.add_key == 1)
+ allowed = 1;
+ if (options.add_key == 2) {
+ if (comment == NULL)
+ allowed = ask_permission("Add key %s to agent?",
+ filename);
+ else
+ allowed = ask_permission("Add key %s (%s) to agent?",
+ filename, comment);
+ }
+
+ if (allowed) {
+ /* Add for default lifetime; do not confirm each use. */
+ if (ssh_add_identity_constrained(ac, private, comment, 0, 0))
+ debug("Identity added: %s (%s)", filename, comment);
+ else
+ debug("Error while adding identity!");
+ }
+
+ if (comment)
+ xfree(comment);
return private;
}
@@ -1394,7 +1430,8 @@ userauth_pubkey(Authctxt *authctxt)
sent = send_pubkey_test(authctxt, id);
} else if (id->key == NULL) {
debug("Trying private key: %s", id->filename);
- id->key = load_identity_file(id->filename);
+ id->key = load_identity_file(id->filename,
+ authctxt->agent);
if (id->key != NULL) {
id->isprivate = 1;
sent = sign_and_send_pubkey(authctxt, id);
On Tue, Jan 12, 2010 at 01:24:34AM +0100, Joachim Schipper wrote:> My keys are secured with a passphrase. That's good for security, but > having to type the passphrase either at every login or at every > invocation of ssh(1) is annoying.> Hence, this patch. I'll just quote ssh_config(5): >AddKeyToAgent If this option is set to ``yes'' and ssh-agent(1) is running, any keys used will be added to the agent (with the default lifetime). Setting this to ``ask'' will cause ssh to require confirmation using the SSH_ASKPASS program before the key is added (see ssh-add(1) for details). The argument must be ``yes'', ``ask'', or ``no''. The default is ``no''. I am a bit disappointed by the total lack of response - does nobody else have this problem? I'm willing to do more work on it, if so desired, and I wouldn't mind having to wait until OpenBSD 4.7 is tagged if everyone's too busy right now. In short, what do I have to do to get this in, or at least a curt "no, it sucks because of <foo>"? The patch below is equivalent to the patch I posted tuesday, with the sole exception that it documents that all used keys (not just passphrased keys) are added to the agent. Which is probably less surprising anyway. Joachim Index: authfile.c ==================================================================RCS file: /usr/obsd-repos//src/usr.bin/ssh/authfile.c,v retrieving revision 1.78 diff -u -p -r1.78 authfile.c --- authfile.c 11 Jan 2010 04:46:45 -0000 1.78 +++ authfile.c 11 Jan 2010 22:35:04 -0000 @@ -552,8 +552,8 @@ key_load_private_type(int type, const ch strerror(errno)); if (perm_ok != NULL) *perm_ok = 0; - } return NULL; + } if (!key_perm_ok(fd, filename)) { if (perm_ok != NULL) *perm_ok = 0; Index: readconf.c ==================================================================RCS file: /usr/obsd-repos//src/usr.bin/ssh/readconf.c,v retrieving revision 1.182 diff -u -p -r1.182 readconf.c --- readconf.c 9 Jan 2010 23:04:13 -0000 1.182 +++ readconf.c 11 Jan 2010 22:19:10 -0000 @@ -128,7 +128,7 @@ typedef enum { oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, - oDeprecated, oUnsupported + oAddKey, oDeprecated, oUnsupported } OpCodes; /* Textual representations of the tokens. */ @@ -232,6 +232,7 @@ static struct { #else { "zeroknowledgepasswordauthentication", oUnsupported }, #endif + { "addkeytoagent", oAddKey }, { NULL, oBadOption } }; @@ -914,6 +915,10 @@ parse_int: intptr = &options->use_roaming; goto parse_flag; + case oAddKey: + intptr = &options->add_key; + goto parse_yesnoask; + case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); @@ -1064,6 +1069,7 @@ initialize_options(Options * options) options->local_command = NULL; options->permit_local_command = -1; options->use_roaming = -1; + options->add_key = -1; options->visual_host_key = -1; options->zero_knowledge_password_authentication = -1; } @@ -1202,6 +1208,8 @@ fill_default_options(Options * options) options->permit_local_command = 0; if (options->use_roaming == -1) options->use_roaming = 1; + if (options->add_key == -1) + options->add_key = 0; if (options->visual_host_key == -1) options->visual_host_key = 0; if (options->zero_knowledge_password_authentication == -1) Index: readconf.h ==================================================================RCS file: /usr/obsd-repos//src/usr.bin/ssh/readconf.h,v retrieving revision 1.81 diff -u -p -r1.81 readconf.h --- readconf.h 9 Jan 2010 23:04:13 -0000 1.81 +++ readconf.h 11 Jan 2010 22:19:18 -0000 @@ -125,6 +125,8 @@ typedef struct { int use_roaming; + int add_key; /* add keys to agent */ + } Options; #define SSHCTL_MASTER_NO 0 Index: ssh-agent.1 ==================================================================RCS file: /usr/obsd-repos//src/usr.bin/ssh/ssh-agent.1,v retrieving revision 1.49 diff -u -p -r1.49 ssh-agent.1 --- ssh-agent.1 22 Oct 2009 15:02:12 -0000 1.49 +++ ssh-agent.1 11 Jan 2010 23:39:47 -0000 @@ -109,6 +109,13 @@ When the command dies, so does the agent .Pp The agent initially does not have any private keys. Keys are added using +.Xr ssh 1 +(see +.Cm AddKeyToAgent +in +.Xr ssh_config 5 +for details) +or .Xr ssh-add 1 . When executed without arguments, .Xr ssh-add 1 Index: ssh.1 ==================================================================RCS file: /usr/obsd-repos//src/usr.bin/ssh/ssh.1,v retrieving revision 1.290 diff -u -p -r1.290 ssh.1 --- ssh.1 11 Jan 2010 01:39:46 -0000 1.290 +++ ssh.1 11 Jan 2010 23:14:55 -0000 @@ -428,6 +428,7 @@ For full details of the options listed b .Xr ssh_config 5 . .Pp .Bl -tag -width Ds -offset indent -compact +.It AddKeyToAgent .It AddressFamily .It BatchMode .It BindAddress @@ -803,6 +804,10 @@ The most convenient way to use public ke authentication agent. See .Xr ssh-agent 1 +and (optionally) the +.Cm AddKeyToAgent +directive in +.Xr ssh_config 5 for more information. .Pp Challenge-response authentication works as follows: Index: ssh_config.5 ==================================================================RCS file: /usr/obsd-repos//src/usr.bin/ssh/ssh_config.5,v retrieving revision 1.126 diff -u -p -r1.126 ssh_config.5 --- ssh_config.5 9 Jan 2010 23:04:13 -0000 1.126 +++ ssh_config.5 16 Jan 2010 19:20:09 -0000 @@ -116,6 +116,27 @@ a canonicalized host name before matchin See .Sx PATTERNS for more information on patterns. +.It Cm AddKeyToAgent +If this option is set to +.Dq yes +and +.Xr ssh-agent 1 +is running, any keys used will be added to the agent (with the default +lifetime). +Setting this to +.Dq ask +will cause ssh to require confirmation using the +.Ev SSH_ASKPASS +program before the key is added (see +.Xr ssh-add 1 +for details). +The argument must be +.Dq yes , +.Dq ask , +or +.Dq no . +The default is +.Dq no . .It Cm AddressFamily Specifies which address family to use when connecting. Valid arguments are Index: sshconnect1.c ==================================================================RCS file: /usr/obsd-repos//src/usr.bin/ssh/sshconnect1.c,v retrieving revision 1.70 diff -u -p -r1.70 sshconnect1.c --- sshconnect1.c 6 Nov 2006 21:25:28 -0000 1.70 +++ sshconnect1.c 16 Jan 2010 19:16:52 -0000 @@ -57,21 +57,15 @@ extern char *__progname; * authenticate using the agent. */ static int -try_agent_authentication(void) +try_agent_authentication(AuthenticationConnection *auth) { int type; char *comment; - AuthenticationConnection *auth; u_char response[16]; u_int i; Key *key; BIGNUM *challenge; - /* Get connection to the agent. */ - auth = ssh_get_authentication_connection(); - if (!auth) - return 0; - if ((challenge = BN_new()) == NULL) fatal("try_agent_authentication: BN_new failed"); /* Loop through identities served by the agent. */ @@ -134,7 +128,6 @@ try_agent_authentication(void) /* The server returns success if it accepted the authentication. */ if (type == SSH_SMSG_SUCCESS) { - ssh_close_authentication_connection(auth); BN_clear_free(challenge); debug("RSA authentication accepted by server."); return 1; @@ -144,7 +137,6 @@ try_agent_authentication(void) packet_disconnect("Protocol error waiting RSA auth response: %d", type); } - ssh_close_authentication_connection(auth); BN_clear_free(challenge); debug("RSA authentication using agent refused."); return 0; @@ -200,7 +192,7 @@ respond_to_rsa_challenge(BIGNUM * challe * the user using it. */ static int -try_rsa_authentication(int idx) +try_rsa_authentication(int idx, AuthenticationConnection *auth) { BIGNUM *challenge; Key *public, *private; @@ -293,6 +285,19 @@ try_rsa_authentication(int idx) return 0; } + /* + * Consider adding key to agent. We add keys for the default lifetime + * with no need to confirm each use. + */ + if (auth != NULL && (options.add_key == 1 || + (options.add_key == 2 && + ask_permission("Add key %s (%s) to agent?", authfile, comment)))) { + if (ssh_add_identity_constrained(auth, private, comment, 0, 0)) + debug("Identity added: %s (%s)", authfile, comment); + else + verbose("Error while adding identity!"); + } + /* Compute and send a response to the challenge. */ respond_to_rsa_challenge(challenge, private->rsa); @@ -670,6 +675,7 @@ ssh_userauth1(const char *local_user, co Sensitive *sensitive) { int i, type; + AuthenticationConnection *auth = NULL; if (supported_authentications == 0) fatal("ssh_userauth1: server supports no auth methods"); @@ -715,14 +721,15 @@ ssh_userauth1(const char *local_user, co * agent is tried first because no passphrase is needed for * it, whereas identity files may require passphrases. */ - if (try_agent_authentication()) + auth = ssh_get_authentication_connection(); + if (auth != NULL && try_agent_authentication(auth)) goto success; /* Try RSA authentication for each identity. */ for (i = 0; i < options.num_identity_files; i++) if (options.identity_keys[i] != NULL && options.identity_keys[i]->type == KEY_RSA1 && - try_rsa_authentication(i)) + try_rsa_authentication(i, auth)) goto success; } /* Try challenge response authentication if the server supports it. */ @@ -746,5 +753,6 @@ ssh_userauth1(const char *local_user, co /* NOTREACHED */ success: - return; /* need statement after label */ + if (auth) + ssh_close_authentication_connection(auth); } Index: sshconnect2.c ==================================================================RCS file: /usr/obsd-repos//src/usr.bin/ssh/sshconnect2.c,v retrieving revision 1.178 diff -u -p -r1.178 sshconnect2.c --- sshconnect2.c 11 Jan 2010 04:46:45 -0000 1.178 +++ sshconnect2.c 11 Jan 2010 23:12:38 -0000 @@ -244,7 +244,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 *); +static Key *load_identity_file(char *, AuthenticationConnection *); static Authmethod *authmethod_get(char *authlist); static Authmethod *authmethod_lookup(const char *name); @@ -1102,7 +1102,7 @@ input_userauth_jpake_server_confirm(int static int identity_sign(Identity *id, u_char **sigp, u_int *lenp, - u_char *data, u_int datalen) + u_char *data, u_int datalen, AuthenticationConnection *auth) { Key *prv; int ret; @@ -1118,7 +1118,7 @@ identity_sign(Identity *id, u_char **sig if (id->isprivate || (id->key->flags & KEY_FLAG_EXT)) return (key_sign(id->key, sigp, lenp, data, datalen)); /* load the private key from the file */ - if ((prv = load_identity_file(id->filename)) == NULL) + if ((prv = load_identity_file(id->filename, auth)) == NULL) return (-1); ret = key_sign(prv, sigp, lenp, data, datalen); key_free(prv); @@ -1168,7 +1168,7 @@ sign_and_send_pubkey(Authctxt *authctxt, /* generate signature */ ret = identity_sign(id, &signature, &slen, - buffer_ptr(&b), buffer_len(&b)); + buffer_ptr(&b), buffer_len(&b), authctxt->agent); if (ret == -1) { xfree(blob); buffer_free(&b); @@ -1240,30 +1240,36 @@ send_pubkey_test(Authctxt *authctxt, Ide } static Key * -load_identity_file(char *filename) +load_identity_file(char *filename, AuthenticationConnection *ac) { Key *private; - char prompt[300], *passphrase; - int perm_ok = 0, quit, i; + char prompt[300], *passphrase, *comment = NULL; + int perm_ok = 0, quit, i, allowed = 0; struct stat st; if (stat(filename, &st) < 0) { debug3("no such identity: %s", filename); return NULL; } - private = key_load_private_type(KEY_UNSPEC, filename, "", NULL, &perm_ok); - if (!perm_ok) + private = key_load_private_type(KEY_UNSPEC, filename, "", &comment, &perm_ok); + if (!perm_ok) { + if (comment) + xfree(comment); return NULL; + } if (private == NULL) { - if (options.batch_mode) + if (options.batch_mode) { + if (comment) + xfree(comment); return NULL; + } snprintf(prompt, sizeof prompt, "Enter passphrase for key '%.100s': ", filename); for (i = 0; i < options.number_of_password_prompts; i++) { passphrase = read_passphrase(prompt, 0); if (strcmp(passphrase, "") != 0) { private = key_load_private_type(KEY_UNSPEC, - filename, passphrase, NULL, NULL); + filename, passphrase, &comment, NULL); quit = 0; } else { debug2("no passphrase given, try next key"); @@ -1273,9 +1279,39 @@ load_identity_file(char *filename) xfree(passphrase); if (private != NULL || quit) break; + if (comment) + xfree(comment); debug2("bad passphrase given, try again..."); } } + + /* If we loaded the key and have an agent, consider adding key. */ + if (private == NULL || ac == NULL) { + if (comment) + xfree(comment); + return private; + } + if (options.add_key == 1) + allowed = 1; + if (options.add_key == 2) { + if (comment == NULL) + allowed = ask_permission("Add key %s to agent?", + filename); + else + allowed = ask_permission("Add key %s (%s) to agent?", + filename, comment); + } + + if (allowed) { + /* Add for default lifetime; do not confirm each use. */ + if (ssh_add_identity_constrained(ac, private, comment, 0, 0)) + debug("Identity added: %s (%s)", filename, comment); + else + debug("Error while adding identity!"); + } + + if (comment) + xfree(comment); return private; } @@ -1394,7 +1430,8 @@ userauth_pubkey(Authctxt *authctxt) sent = send_pubkey_test(authctxt, id); } else if (id->key == NULL) { debug("Trying private key: %s", id->filename); - id->key = load_identity_file(id->filename); + id->key = load_identity_file(id->filename, + authctxt->agent); if (id->key != NULL) { id->isprivate = 1; sent = sign_and_send_pubkey(authctxt, id);
On Tue, Jan 12, 2010 at 01:24:34AM +0100, Joachim Schipper wrote:> My keys are secured with a passphrase. That's good for security, but > having to type the passphrase either at every login or at every > invocation of ssh(1) is annoying. > > I know I could invoke ssh-add(1) just before invoking ssh(1), if I keep > track of whether I invoked it already, or write some hacky scripts; but > the rest of OpenSSH is wonderfully usable without any hacks. > > Hence, this patch. I'll just quote ssh_config(5): > > AddKeyToAgent > If this option is set to ``yes'' and ssh-agent(1) is running, any > keys unlocked with a password will be added to the agent (with > the default lifetime). Setting this to ``ask'' will cause ssh to > require confirmation using the SSH_ASKPASS program before the key > is added (see ssh-add(1) for details). The argument must be > ``yes'', ``ask'', or ``no''. The default is ``no''. > > Having more knobs isn't really useful, IMHO. Default lifetime is > configurable via ssh-agent(1)'s -t flag, and if you want to confirm each > key use you should be willing to live without this convenience feature. > > By the way, are there plans to replace ask_permission() (also used for > other "ask" type options, e.g. ControlMaster) by something a little > more user-friendly? Having to type "yes" works, but isn't exactly > elegant. (Not volunteering here, I know nothing about X.) > > Please be gentle, but inspect thoroughly, as this is my first patch.I sent the above message to the list quite a while ago, and was told to put my (revised) patch in the bug tracker. It's at https://bugzilla.mindrot.org/show_bug.cgi?id=1699; is there anything I can do to help the process along? I am open to suggestions/criticisms/being told to drop this - but I think it'd be a shame if this falls through the cracks. Joachim
Maybe Matching Threads
- [Bug 1693] New: ssh prompts for passphrase even when identity file is unreadable
- displaying identity key comment string in passphrase prompt
- [Bug 1967] Potential memory leak in ssh [detected by melton]
- [PATCH] Add syscall wrappers required by libkeyutils
- Linux in-kernel keys support