diff options
author | Markus Friedl <markus@cvs.openbsd.org> | 2001-03-08 21:42:34 +0000 |
---|---|---|
committer | Markus Friedl <markus@cvs.openbsd.org> | 2001-03-08 21:42:34 +0000 |
commit | 85521a8799c3c203f00282511cabe9c65b45dacf (patch) | |
tree | e81a0aaa5ac7dfa2afc389506cc396a864a9d7b1 | |
parent | f6c787cef5223a931e0ae9df006b8942e86586b7 (diff) |
implement client side of SSH2_MSG_USERAUTH_PK_OK (test public key ->
no need to do enter passphrase or do expensive sign operations if the
server does not accept key).
-rw-r--r-- | usr.bin/ssh/compat.c | 7 | ||||
-rw-r--r-- | usr.bin/ssh/compat.h | 3 | ||||
-rw-r--r-- | usr.bin/ssh/readconf.h | 6 | ||||
-rw-r--r-- | usr.bin/ssh/ssh.c | 46 | ||||
-rw-r--r-- | usr.bin/ssh/sshconnect1.c | 5 | ||||
-rw-r--r-- | usr.bin/ssh/sshconnect2.c | 272 |
6 files changed, 263 insertions, 76 deletions
diff --git a/usr.bin/ssh/compat.c b/usr.bin/ssh/compat.c index 4d4ca291658..a47324e766f 100644 --- a/usr.bin/ssh/compat.c +++ b/usr.bin/ssh/compat.c @@ -23,7 +23,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: compat.c,v 1.36 2001/02/27 11:00:11 markus Exp $"); +RCSID("$OpenBSD: compat.c,v 1.37 2001/03/08 21:42:31 markus Exp $"); #include <regex.h> @@ -70,11 +70,12 @@ compat_datafellows(const char *version) 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 */ diff --git a/usr.bin/ssh/compat.h b/usr.bin/ssh/compat.h index 2726fafff72..9359d4b797f 100644 --- a/usr.bin/ssh/compat.h +++ b/usr.bin/ssh/compat.h @@ -21,7 +21,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* RCSID("$OpenBSD: compat.h,v 1.15 2001/02/19 09:53:31 markus Exp $"); */ +/* RCSID("$OpenBSD: compat.h,v 1.16 2001/03/08 21:42:32 markus Exp $"); */ #ifndef COMPAT_H #define COMPAT_H @@ -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); diff --git a/usr.bin/ssh/readconf.h b/usr.bin/ssh/readconf.h index 575b2646da9..97615620e78 100644 --- a/usr.bin/ssh/readconf.h +++ b/usr.bin/ssh/readconf.h @@ -11,11 +11,13 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: readconf.h,v 1.26 2001/02/11 12:59:25 markus Exp $"); */ +/* RCSID("$OpenBSD: readconf.h,v 1.27 2001/03/08 21:42:32 markus Exp $"); */ #ifndef READCONF_H #define READCONF_H +#include "key.h" + /* Data structure for representing a forwarding request. */ typedef struct { @@ -83,7 +85,7 @@ typedef struct { 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; diff --git a/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c index 6c2a64ff6c5..728830b3ca4 100644 --- a/usr.bin/ssh/ssh.c +++ b/usr.bin/ssh/ssh.c @@ -39,7 +39,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: ssh.c,v 1.103 2001/03/04 17:42:28 millert Exp $"); +RCSID("$OpenBSD: ssh.c,v 1.104 2001/03/08 21:42:32 markus Exp $"); #include <openssl/evp.h> #include <openssl/err.h> @@ -225,7 +225,7 @@ rsh_connect(char *host, char *user, Buffer * command) 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 @@ main(int ac, char **av) } 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 = @@ -1077,3 +1073,31 @@ guess_identity_file_type(const char *filename) 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; + } +} diff --git a/usr.bin/ssh/sshconnect1.c b/usr.bin/ssh/sshconnect1.c index c5ff7213a01..3d45ac5a26e 100644 --- a/usr.bin/ssh/sshconnect1.c +++ b/usr.bin/ssh/sshconnect1.c @@ -13,7 +13,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect1.c,v 1.27 2001/02/15 23:19:59 markus Exp $"); +RCSID("$OpenBSD: sshconnect1.c,v 1.28 2001/03/08 21:42:33 markus Exp $"); #include <openssl/bn.h> #include <openssl/evp.h> @@ -1017,7 +1017,8 @@ ssh_userauth( /* 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; } diff --git a/usr.bin/ssh/sshconnect2.c b/usr.bin/ssh/sshconnect2.c index 0baecf0a55d..81e1aef9307 100644 --- a/usr.bin/ssh/sshconnect2.c +++ b/usr.bin/ssh/sshconnect2.c @@ -23,7 +23,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect2.c,v 1.50 2001/03/05 17:17:21 markus Exp $"); +RCSID("$OpenBSD: sshconnect2.c,v 1.51 2001/03/08 21:42:33 markus Exp $"); #include <openssl/bn.h> #include <openssl/md5.h> @@ -467,6 +467,10 @@ struct Authctxt { 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,20 @@ void input_userauth_failure(int type, int plen, void *ctxt); 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 clear_auth_state(Authctxt *authctxt); + void authmethod_clear(void); Authmethod *authmethod_get(char *authlist); Authmethod *authmethod_lookup(const char *name); @@ -546,6 +558,7 @@ ssh_userauth2(const char *server_user, char *host) 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 +578,30 @@ ssh_userauth2(const char *server_user, char *host) 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 +624,14 @@ input_userauth_success(int type, int plen, void *ctxt) Authctxt *authctxt = ctxt; if (authctxt == NULL) fatal("input_userauth_success: no authentication context"); + if (authctxt->authlist) + xfree(authctxt->authlist); + clear_auth_state(authctxt); 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 +647,74 @@ input_userauth_failure(int type, int plen, void *ctxt) 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); + clear_auth_state(authctxt); + 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, pktype, 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); + } + packet_done(); + + debug("input_userauth_pk_ok: pkalg %s blen %d lastkey %p hint %d", + pkalg, blen, authctxt->last_key, authctxt->last_key_hint); + + do { + if (authctxt->last_key == NULL || + authctxt->last_key_sign == NULL) { + debug("no last key or no sign cb"); break; - } else { - debug2("we did not send a packet, disable method"); - method->enabled = NULL; } - } - xfree(authlist); + debug2("last_key %s", key_fingerprint(authctxt->last_key)); + if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) { + debug("unknown pkalg %s", pkalg); + break; + } + if ((key = key_from_blob(pkblob, blen)) == NULL) { + debug("no key from blob. pkalg %s", pkalg); + break; + } + debug2("input_userauth_pk_ok: fp %s", key_fingerprint(key)); + if (!key_equal(key, authctxt->last_key)) { + debug("key != last_key"); + break; + } + sent = sign_and_send_pubkey(authctxt, key, + authctxt->last_key_sign); + } while(0); + + if (key != NULL) + key_free(key); + xfree(pkalg); + xfree(pkblob); + + /* unregister */ + clear_auth_state(authctxt); + dispatch_set(SSH2_MSG_USERAUTH_PK_OK, NULL); + + /* try another method if we did not send a packet*/ + if (sent == 0) + userauth(authctxt, NULL); + } int @@ -633,7 +726,6 @@ userauth_none(Authctxt *authctxt) packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_send(); - packet_write_wait(); return 1; } @@ -663,10 +755,22 @@ userauth_passwd(Authctxt *authctxt) xfree(password); packet_inject_ignore(64); packet_send(); - packet_write_wait(); return 1; } +void +clear_auth_state(Authctxt *authctxt) +{ + /* XXX clear authentication state */ + if (authctxt->last_key != NULL && authctxt->last_key_hint == -1) { + debug3("clear_auth_state: key_free %p", authctxt->last_key); + key_free(authctxt->last_key); + } + authctxt->last_key = NULL; + authctxt->last_key_hint = -2; + authctxt->last_key_sign = NULL; +} + int sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) { @@ -678,6 +782,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) 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 +813,8 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) 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 +826,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) 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 +837,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) buffer_put_string(&b, blob, bloblen); } xfree(blob); + /* append signature */ buffer_put_string(&b, signature, slen); xfree(signature); @@ -743,76 +851,113 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) 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); + debug3("send_pubkey_test"); + + 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); - k = key_new(KEY_UNSPEC); - if (!load_private_key(filename, "", k, NULL)) { + 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; + + 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 < 0) + 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,10 +975,11 @@ userauth_pubkey_agent(Authctxt *authctxt) 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); - key_free(k); + ret = send_pubkey_test(authctxt, k, agent_sign_cb, -1); + if (ret == 0) + key_free(k); } if (ret == 0) debug2("userauth_pubkey_agent: no message sent"); @@ -845,6 +991,8 @@ userauth_pubkey(Authctxt *authctxt) { static int idx = 0; int sent = 0; + Key *key; + char *filename; if (authctxt->agent != NULL) { do { @@ -852,9 +1000,21 @@ userauth_pubkey(Authctxt *authctxt) } 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 +1040,6 @@ userauth_kbdint(Authctxt *authctxt) 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 +1097,6 @@ input_userauth_info_req(int type, int plen, void *ctxt) packet_inject_ignore(64); packet_send(); - packet_write_wait(); } /* find auth method */ |