ssh-agent and id_dsa
Markus Friedl
markus.friedl at informatik.uni-erlangen.de
Thu Mar 8 10:56:43 EST 2001
On Tue, Feb 20, 2001 at 11:35:23PM +0100, Lutz Jaenicke wrote:
> > perhaps i add handling of SSH2_MSG_USERAUTH_PK_OK to the
> > ssh client, but i'm not sure.
>
> We'll see :-)
ok try this:
this patch implements client side handling of SSH2_MSG_USERAUTH_PK_OK
messages.
this means that the client can check whether the server will accept
the public key and can delay the expensive signature operation until
the server replies: "yes this key is valid for login".
Index: compat.c
===================================================================
RCS file: /home/markus/cvs/ssh/compat.c,v
retrieving revision 1.36
diff -u -r1.36 compat.c
--- compat.c 2001/02/27 11:00:11 1.36
+++ compat.c 2001/03/07 23:19:03
@@ -70,11 +70,12 @@
SSH_OLD_SESSIONID|SSH_BUG_DEBUG },
{ "^2\\.0\\.1[3-9]", SSH_BUG_SIGBLOB|SSH_BUG_HMAC|
SSH_OLD_SESSIONID|SSH_BUG_DEBUG|
- SSH_BUG_PKSERVICE|SSH_BUG_X11FWD },
+ SSH_BUG_PKSERVICE|SSH_BUG_X11FWD|
+ SSH_BUG_PKOK },
{ "^2\\.0\\.", SSH_BUG_SIGBLOB|SSH_BUG_HMAC|
SSH_OLD_SESSIONID|SSH_BUG_DEBUG|
SSH_BUG_PKSERVICE|SSH_BUG_X11FWD|
- SSH_BUG_PKAUTH },
+ SSH_BUG_PKAUTH|SSH_BUG_PKOK },
{ "^2\\.[23]\\.0", SSH_BUG_HMAC},
{ "^2\\.[2-9]\\.", 0 },
{ "^2\\.4$", SSH_OLD_SESSIONID}, /* Van Dyke */
Index: compat.h
===================================================================
RCS file: /home/markus/cvs/ssh/compat.h,v
retrieving revision 1.15
diff -u -r1.15 compat.h
--- compat.h 2001/02/19 09:53:31 1.15
+++ compat.h 2001/03/07 23:19:28
@@ -40,6 +40,7 @@
#define SSH_BUG_DEBUG 0x0040
#define SSH_BUG_BANNER 0x0080
#define SSH_BUG_IGNOREMSG 0x0100
+#define SSH_BUG_PKOK 0x0200
void enable_compat13(void);
void enable_compat20(void);
Index: readconf.c
Index: readconf.h
===================================================================
RCS file: /home/markus/cvs/ssh/readconf.h,v
retrieving revision 1.26
diff -u -r1.26 readconf.h
--- readconf.h 2001/02/11 12:59:25 1.26
+++ readconf.h 2001/03/07 21:00:05
@@ -16,6 +16,8 @@
#ifndef READCONF_H
#define READCONF_H
+#include "key.h"
+
/* Data structure for representing a forwarding request. */
typedef struct {
@@ -83,7 +85,7 @@
int num_identity_files; /* Number of files for RSA/DSA identities. */
char *identity_files[SSH_MAX_IDENTITY_FILES];
- int identity_files_type[SSH_MAX_IDENTITY_FILES];
+ Key *identity_keys[SSH_MAX_IDENTITY_FILES];
/* Local TCP/IP forward requests. */
int num_local_forwards;
Index: ssh.c
===================================================================
RCS file: /home/markus/cvs/ssh/ssh.c,v
retrieving revision 1.103
diff -u -r1.103 ssh.c
--- ssh.c 2001/03/04 17:42:28 1.103
+++ ssh.c 2001/03/07 23:46:31
@@ -225,7 +225,7 @@
int ssh_session(void);
int ssh_session2(void);
-int guess_identity_file_type(const char *filename);
+void load_public_identity_files(void);
/*
* Main program for the ssh client.
@@ -660,15 +660,11 @@
}
exit(1);
}
- /* Expand ~ in options.identity_files, known host file names. */
- /* XXX mem-leaks */
- for (i = 0; i < options.num_identity_files; i++) {
- options.identity_files[i] =
- tilde_expand_filename(options.identity_files[i], original_real_uid);
- options.identity_files_type[i] = guess_identity_file_type(options.identity_files[i]);
- debug("identity file %s type %d", options.identity_files[i],
- options.identity_files_type[i]);
- }
+ /* load options.identity_files */
+ load_public_identity_files();
+
+ /* Expand ~ in known host file names. */
+ /* XXX mem-leaks: */
options.system_hostfile =
tilde_expand_filename(options.system_hostfile, original_real_uid);
options.user_hostfile =
@@ -1076,4 +1072,32 @@
}
key_free(public);
return type;
+}
+
+void
+load_public_identity_files(void)
+{
+ char *filename;
+ Key *public;
+ int i;
+
+ for (i = 0; i < options.num_identity_files; i++) {
+ filename = tilde_expand_filename(options.identity_files[i],
+ original_real_uid);
+ public = key_new(KEY_RSA1);
+ if (!load_public_key(filename, public, NULL)) {
+ key_free(public);
+ public = key_new(KEY_UNSPEC);
+ if (!try_load_public_key(filename, public, NULL)) {
+ debug("unknown identity file %s", filename);
+ key_free(public);
+ public = NULL;
+ }
+ }
+ debug("identity file %s type %d", filename,
+ public ? public->type : -1);
+ xfree(options.identity_files[i]);
+ options.identity_files[i] = filename;
+ options.identity_keys[i] = public;
+ }
}
Index: sshconnect1.c
===================================================================
RCS file: /home/markus/cvs/ssh/sshconnect1.c,v
retrieving revision 1.27
diff -u -r1.27 sshconnect1.c
--- sshconnect1.c 2001/02/15 23:19:59 1.27
+++ sshconnect1.c 2001/03/07 23:49:39
@@ -1017,7 +1017,8 @@
/* Try RSA authentication for each identity. */
for (i = 0; i < options.num_identity_files; i++)
- if (options.identity_files_type[i] == KEY_RSA1 &&
+ if (options.identity_keys[i] != NULL &&
+ options.identity_keys[i]->type == KEY_RSA1 &&
try_rsa_authentication(options.identity_files[i]))
return;
}
Index: sshconnect2.c
===================================================================
RCS file: /home/markus/cvs/ssh/sshconnect2.c,v
retrieving revision 1.50
diff -u -r1.50 sshconnect2.c
--- sshconnect2.c 2001/03/05 17:17:21 1.50
+++ sshconnect2.c 2001/03/07 23:43:47
@@ -467,6 +467,10 @@
AuthenticationConnection *agent;
Authmethod *method;
int success;
+ char *authlist;
+ Key *last_key;
+ sign_cb_fn *last_key_sign;
+ int last_key_hint;
};
struct Authmethod {
char *name; /* string to compare against server's list */
@@ -480,12 +484,19 @@
void input_userauth_banner(int type, int plen, void *ctxt);
void input_userauth_error(int type, int plen, void *ctxt);
void input_userauth_info_req(int type, int plen, void *ctxt);
+void input_userauth_pk_ok(int type, int plen, void *ctxt);
int userauth_none(Authctxt *authctxt);
int userauth_pubkey(Authctxt *authctxt);
int userauth_passwd(Authctxt *authctxt);
int userauth_kbdint(Authctxt *authctxt);
+void userauth(Authctxt *authctxt, char *authlist);
+
+int
+sign_and_send_pubkey(Authctxt *authctxt, Key *k,
+ sign_cb_fn *sign_callback);
+
void authmethod_clear(void);
Authmethod *authmethod_get(char *authlist);
Authmethod *authmethod_lookup(const char *name);
@@ -546,6 +557,7 @@
authctxt.service = "ssh-connection"; /* service name */
authctxt.success = 0;
authctxt.method = authmethod_lookup("none");
+ authctxt.authlist = NULL;
if (authctxt.method == NULL)
fatal("ssh_userauth2: internal error: cannot send userauth none request");
authmethod_clear();
@@ -565,6 +577,30 @@
debug("ssh-userauth2 successful: method %s", authctxt.method->name);
}
void
+userauth(Authctxt *authctxt, char *authlist)
+{
+ if (authlist == NULL) {
+ authlist = authctxt->authlist;
+ } else {
+ if (authctxt->authlist)
+ xfree(authctxt->authlist);
+ authctxt->authlist = authlist;
+ }
+ for (;;) {
+ Authmethod *method = authmethod_get(authlist);
+ if (method == NULL)
+ fatal("Permission denied (%s).", authlist);
+ authctxt->method = method;
+ if (method->userauth(authctxt) != 0) {
+ debug2("we sent a %s packet, wait for reply", method->name);
+ break;
+ } else {
+ debug2("we did not send a packet, disable method");
+ method->enabled = NULL;
+ }
+ }
+}
+void
input_userauth_error(int type, int plen, void *ctxt)
{
fatal("input_userauth_error: bad message during authentication: "
@@ -587,12 +623,13 @@
Authctxt *authctxt = ctxt;
if (authctxt == NULL)
fatal("input_userauth_success: no authentication context");
+ if (authctxt->authlist)
+ xfree(authctxt->authlist);
authctxt->success = 1; /* break out */
}
void
input_userauth_failure(int type, int plen, void *ctxt)
{
- Authmethod *method = NULL;
Authctxt *authctxt = ctxt;
char *authlist = NULL;
int partial;
@@ -608,20 +645,59 @@
log("Authenticated with partial success.");
debug("authentications that can continue: %s", authlist);
- for (;;) {
- method = authmethod_get(authlist);
- if (method == NULL)
- fatal("Permission denied (%s).", authlist);
- authctxt->method = method;
- if (method->userauth(authctxt) != 0) {
- debug2("we sent a %s packet, wait for reply", method->name);
- break;
- } else {
- debug2("we did not send a packet, disable method");
- method->enabled = NULL;
- }
+ userauth(authctxt, authlist);
+}
+void
+input_userauth_pk_ok(int type, int plen, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ Key *key = NULL;
+ Buffer b;
+ int alen, blen, sent = 0;
+ char *pkalg, *pkblob;
+
+ if (authctxt == NULL)
+ fatal("input_userauth_pk_ok: no authentication context");
+ if (datafellows & SSH_BUG_PKOK) {
+ /* this is similar to SSH_BUG_PKAUTH */
+ debug2("input_userauth_pk_ok: SSH_BUG_PKOK");
+ pkblob = packet_get_string(&blen);
+ buffer_init(&b);
+ buffer_append(&b, pkblob, blen);
+ pkalg = buffer_get_string(&b, &alen);
+ buffer_free(&b);
+ } else {
+ pkalg = packet_get_string(&alen);
+ pkblob = packet_get_string(&blen);
}
- xfree(authlist);
+ packet_done();
+
+ debug("input_userauth_pk_ok: pkalg %s blen %d lastkey %p hint %d",
+ pkalg, blen, authctxt->last_key, authctxt->last_key_hint);
+
+ if (authctxt->last_key != NULL &&
+ authctxt->last_key_sign != NULL &&
+ key_type_from_name(pkalg) != KEY_UNSPEC &&
+ (key = key_from_blob(pkblob, blen)) != NULL &&
+ key_equal(key, authctxt->last_key)) {
+ debug2("input_userauth_pk_ok: fp %s", key_fingerprint(key));
+ sent = sign_and_send_pubkey(authctxt, key,
+ authctxt->last_key_sign);
+ }
+ if (key != NULL)
+ key_free(key);
+ xfree(pkalg);
+ xfree(pkblob);
+
+ /* or try another method */
+ if (sent == 0)
+ userauth(authctxt, NULL);
+
+ /* unregister */
+ authctxt->last_key_sign = NULL;
+ authctxt->last_key_hint = -1;
+ authctxt->last_key = NULL;
+ dispatch_set(SSH2_MSG_USERAUTH_PK_OK, NULL);
}
int
@@ -633,7 +709,6 @@
packet_put_cstring(authctxt->service);
packet_put_cstring(authctxt->method->name);
packet_send();
- packet_write_wait();
return 1;
}
@@ -663,7 +738,6 @@
xfree(password);
packet_inject_ignore(64);
packet_send();
- packet_write_wait();
return 1;
}
@@ -678,6 +752,7 @@
int have_sig = 1;
debug3("sign_and_send_pubkey");
+
if (key_to_blob(k, &blob, &bloblen) == 0) {
/* we cannot handle this key */
debug3("sign_and_send_pubkey: cannot handle key");
@@ -708,7 +783,8 @@
buffer_put_string(&b, blob, bloblen);
/* generate signature */
- ret = (*sign_callback)(authctxt, k, &signature, &slen, buffer_ptr(&b), buffer_len(&b));
+ ret = (*sign_callback)(authctxt, k, &signature, &slen,
+ buffer_ptr(&b), buffer_len(&b));
if (ret == -1) {
xfree(blob);
buffer_free(&b);
@@ -720,6 +796,7 @@
if (datafellows & SSH_BUG_PKSERVICE) {
buffer_clear(&b);
buffer_append(&b, session_id2, session_id2_len);
+ skip = session_id2_len;
buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
buffer_put_cstring(&b, authctxt->server_user);
buffer_put_cstring(&b, authctxt->service);
@@ -730,6 +807,7 @@
buffer_put_string(&b, blob, bloblen);
}
xfree(blob);
+
/* append signature */
buffer_put_string(&b, signature, slen);
xfree(signature);
@@ -743,76 +821,111 @@
packet_start(SSH2_MSG_USERAUTH_REQUEST);
packet_put_raw(buffer_ptr(&b), buffer_len(&b));
buffer_free(&b);
-
- /* send */
packet_send();
- packet_write_wait();
return 1;
}
-/* sign callback */
-int key_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
- u_char *data, int datalen)
-{
- return key_sign(key, sigp, lenp, data, datalen);
-}
-
int
-userauth_pubkey_identity(Authctxt *authctxt, char *filename)
+send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback,
+ int hint)
{
- Key *k;
- int i, ret, try_next, success = 0;
- struct stat st;
- char *passphrase;
- char prompt[300];
+ u_char *blob;
+ int bloblen, have_sig = 0;
- if (stat(filename, &st) != 0) {
- debug("key does not exist: %s", filename);
+ if (key_to_blob(k, &blob, &bloblen) == 0) {
+ /* we cannot handle this key */
+ debug3("send_pubkey_test: cannot handle key");
return 0;
}
- debug("try pubkey: %s", filename);
+ /* register callback for USERAUTH_PK_OK message */
+ authctxt->last_key_sign = sign_callback;
+ authctxt->last_key_hint = hint;
+ authctxt->last_key = k;
+ dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok);
+
+ packet_start(SSH2_MSG_USERAUTH_REQUEST);
+ packet_put_cstring(authctxt->server_user);
+ packet_put_cstring(authctxt->service);
+ packet_put_cstring(authctxt->method->name);
+ packet_put_char(have_sig);
+ if (!(datafellows & SSH_BUG_PKAUTH))
+ packet_put_cstring(key_ssh_name(k));
+ packet_put_string(blob, bloblen);
+ xfree(blob);
+ packet_send();
+ return 1;
+}
+
+Key *
+load_identity_file(char *filename)
+{
+ Key *private;
+ char prompt[300], *passphrase;
+ int success = 0, quit, i;
- k = key_new(KEY_UNSPEC);
- if (!load_private_key(filename, "", k, NULL)) {
+ private = key_new(KEY_UNSPEC);
+ if (!load_private_key(filename, "", private, NULL)) {
if (options.batch_mode) {
- key_free(k);
- return 0;
+ key_free(private);
+ 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) {
- success = load_private_key(filename, passphrase, k, NULL);
- try_next = 0;
+ success = load_private_key(filename,
+ passphrase, private, NULL);
+ quit = 0;
} else {
debug2("no passphrase given, try next key");
- try_next = 1;
+ quit = 1;
}
memset(passphrase, 0, strlen(passphrase));
xfree(passphrase);
- if (success || try_next)
+ if (success || quit)
break;
debug2("bad passphrase given, try again...");
}
if (!success) {
- key_free(k);
- return 0;
+ key_free(private);
+ return NULL;
}
}
- ret = sign_and_send_pubkey(authctxt, k, key_sign_cb);
- key_free(k);
+ return private;
+}
+
+int
+identity_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
+ u_char *data, int datalen)
+{
+ Key *private;
+ int idx, ret;
+
+ idx = authctxt->last_key_hint;
+ if (idx == -1)
+ return -1;
+ private = load_identity_file(options.identity_files[idx]);
+ if (private == NULL)
+ return -1;
+ ret = key_sign(private, sigp, lenp, data, datalen);
+ key_free(private);
return ret;
}
-/* sign callback */
int agent_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
u_char *data, int datalen)
{
return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen);
}
+int key_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
+ u_char *data, int datalen)
+{
+ return key_sign(key, sigp, lenp, data, datalen);
+}
+
int
userauth_pubkey_agent(Authctxt *authctxt)
{
@@ -830,9 +943,9 @@
if (k == NULL) {
debug2("userauth_pubkey_agent: no more keys");
} else {
- debug("userauth_pubkey_agent: trying agent key %s", comment);
+ debug("userauth_pubkey_agent: testing agent key %s", comment);
xfree(comment);
- ret = sign_and_send_pubkey(authctxt, k, agent_sign_cb);
+ ret = send_pubkey_test(authctxt, k, agent_sign_cb, -1);
key_free(k);
}
if (ret == 0)
@@ -845,6 +958,8 @@
{
static int idx = 0;
int sent = 0;
+ Key *key;
+ char *filename;
if (authctxt->agent != NULL) {
do {
@@ -852,9 +967,21 @@
} while(!sent && authctxt->agent->howmany > 0);
}
while (!sent && idx < options.num_identity_files) {
- if (options.identity_files_type[idx] != KEY_RSA1)
- sent = userauth_pubkey_identity(authctxt,
- options.identity_files[idx]);
+ key = options.identity_keys[idx];
+ filename = options.identity_files[idx];
+ if (key == NULL) {
+ debug("try privkey: %s", filename);
+ key = load_identity_file(filename);
+ if (key != NULL) {
+ sent = sign_and_send_pubkey(authctxt, key,
+ key_sign_cb);
+ key_free(key);
+ }
+ } else if (key->type != KEY_RSA1) {
+ debug("try pubkey: %s", filename);
+ sent = send_pubkey_test(authctxt, key,
+ identity_sign_cb, idx);
+ }
idx++;
}
return sent;
@@ -880,7 +1007,6 @@
packet_put_cstring(options.kbd_interactive_devices ?
options.kbd_interactive_devices : "");
packet_send();
- packet_write_wait();
dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
return 1;
@@ -938,7 +1064,6 @@
packet_inject_ignore(64);
packet_send();
- packet_write_wait();
}
/* find auth method */
More information about the openssh-unix-dev
mailing list