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
Reasonably Related 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