Repost: [patch] Automatically add keys to agent
Joachim Schipper
joachim at joachimschipper.nl
Sun Jan 17 06:34:30 EST 2010
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);
More information about the openssh-unix-dev
mailing list