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