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