=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/sshconnect2.c,v retrieving revision 1.27 retrieving revision 1.27.2.3 diff -u -r1.27 -r1.27.2.3 --- src/usr.bin/ssh/sshconnect2.c 2000/10/19 16:45:16 1.27 +++ src/usr.bin/ssh/sshconnect2.c 2001/03/21 19:46:31 1.27.2.3 @@ -23,34 +23,35 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect2.c,v 1.27 2000/10/19 16:45:16 provos Exp $"); +RCSID("$OpenBSD: sshconnect2.c,v 1.27.2.3 2001/03/21 19:46:31 jason Exp $"); #include -#include -#include #include #include #include #include "ssh.h" +#include "ssh2.h" #include "xmalloc.h" #include "rsa.h" #include "buffer.h" #include "packet.h" #include "uidswap.h" #include "compat.h" -#include "readconf.h" #include "bufaux.h" -#include "ssh2.h" +#include "cipher.h" #include "kex.h" #include "myproposal.h" #include "key.h" -#include "dsa.h" #include "sshconnect.h" #include "authfile.h" #include "cli.h" #include "dispatch.h" #include "authfd.h" +#include "log.h" +#include "readconf.h" +#include "readpass.h" +#include "match.h" void ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); void ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); @@ -64,7 +65,7 @@ * SSH2 key exchange */ -unsigned char *session_id2 = NULL; +u_char *session_id2 = NULL; int session_id2_len = 0; void @@ -75,26 +76,25 @@ Buffer *client_kexinit, *server_kexinit; char *sprop[PROPOSAL_MAX]; - if (options.ciphers == NULL) { - if (options.cipher == SSH_CIPHER_3DES) { - options.ciphers = "3des-cbc"; - } else if (options.cipher == SSH_CIPHER_BLOWFISH) { - options.ciphers = "blowfish-cbc"; - } else if (options.cipher == SSH_CIPHER_DES) { - fatal("cipher DES not supported for protocol version 2"); - } + if (options.ciphers == (char *)-1) { + log("No valid ciphers for protocol version 2 given, using defaults."); + options.ciphers = NULL; } if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; } if (options.compression) { - myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; + myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; } else { - myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; + myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; } + if (options.macs != NULL) { + myproposal[PROPOSAL_MAC_ALGS_CTOS] = + myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; + } /* buffers with raw kexinit messages */ server_kexinit = xmalloc(sizeof(*server_kexinit)); @@ -150,28 +150,29 @@ /* diffie-hellman-group1-sha1 */ void -ssh_dh1_client(Kex *kex, char *host, struct sockaddr *hostaddr, +ssh_dh1_client(Kex *kex, char *host, struct sockaddr *hostaddr, Buffer *client_kexinit, Buffer *server_kexinit) { #ifdef DEBUG_KEXDH int i; #endif int plen, dlen; - unsigned int klen, kout; + u_int klen, kout; char *signature = NULL; - unsigned int slen; + u_int slen; char *server_host_key_blob = NULL; Key *server_host_key; - unsigned int sbloblen; + u_int sbloblen; DH *dh; BIGNUM *dh_server_pub = 0; BIGNUM *shared_secret = 0; - unsigned char *kbuf; - unsigned char *hash; + u_char *kbuf; + u_char *hash; debug("Sending SSH2_MSG_KEXDH_INIT."); /* generate and send 'e', client DH public key */ dh = dh_new_group1(); + dh_gen_key(dh, kex->we_need * 8); packet_start(SSH2_MSG_KEXDH_INIT); packet_put_bignum2(dh->pub_key); packet_send(); @@ -196,7 +197,7 @@ /* key, cert */ server_host_key_blob = packet_get_string(&sbloblen); - server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen); + server_host_key = key_from_blob(server_host_key_blob, sbloblen); if (server_host_key == NULL) fatal("cannot decode server_host_key_blob"); @@ -252,17 +253,20 @@ ); xfree(server_host_key_blob); DH_free(dh); + BN_free(dh_server_pub); #ifdef DEBUG_KEXDH fprintf(stderr, "hash == "); for (i = 0; i< 20; i++) fprintf(stderr, "%02x", (hash[i])&0xff); fprintf(stderr, "\n"); #endif - if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) - fatal("dsa_verify failed for server_host_key"); + if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1) + fatal("key_verify failed for server_host_key"); key_free(server_host_key); + xfree(signature); kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); packet_set_kex(kex); /* save session id */ @@ -282,7 +286,7 @@ int dh_estimate(int bits) { - + if (bits < 64) return (512); /* O(2**63) */ if (bits < 128) @@ -300,20 +304,20 @@ int i; #endif int plen, dlen; - unsigned int klen, kout; + u_int klen, kout; char *signature = NULL; - unsigned int slen, nbits; + u_int slen, nbits; char *server_host_key_blob = NULL; Key *server_host_key; - unsigned int sbloblen; + u_int sbloblen; DH *dh; BIGNUM *dh_server_pub = 0; BIGNUM *shared_secret = 0; BIGNUM *p = 0, *g = 0; - unsigned char *kbuf; - unsigned char *hash; + u_char *kbuf; + u_char *hash; - nbits = dh_estimate(kex->enc[MODE_OUT].cipher->key_len * 8); + nbits = dh_estimate(kex->we_need * 8); debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST."); packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST); @@ -337,9 +341,10 @@ if ((g = BN_new()) == NULL) fatal("BN_new"); packet_get_bignum2(g, &dlen); - if ((dh = dh_new_group(g, p)) == NULL) - fatal("dh_new_group"); + dh = dh_new_group(g, p); + dh_gen_key(dh, kex->we_need * 8); + #ifdef DEBUG_KEXDH fprintf(stderr, "\np= "); BN_print_fp(stderr, dh->p); @@ -366,7 +371,7 @@ /* key, cert */ server_host_key_blob = packet_get_string(&sbloblen); - server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen); + server_host_key = key_from_blob(server_host_key_blob, sbloblen); if (server_host_key == NULL) fatal("cannot decode server_host_key_blob"); @@ -416,24 +421,27 @@ buffer_ptr(client_kexinit), buffer_len(client_kexinit), buffer_ptr(server_kexinit), buffer_len(server_kexinit), server_host_key_blob, sbloblen, - nbits, dh->p, dh->g, + nbits, dh->p, dh->g, dh->pub_key, dh_server_pub, shared_secret ); xfree(server_host_key_blob); DH_free(dh); + BN_free(dh_server_pub); #ifdef DEBUG_KEXDH fprintf(stderr, "hash == "); for (i = 0; i< 20; i++) fprintf(stderr, "%02x", (hash[i])&0xff); fprintf(stderr, "\n"); #endif - if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) - fatal("dsa_verify failed for server_host_key"); + if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1) + fatal("key_verify failed for server_host_key"); key_free(server_host_key); + xfree(signature); kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); packet_set_kex(kex); /* save session id */ @@ -451,7 +459,7 @@ typedef int sign_cb_fn( Authctxt *authctxt, Key *key, - unsigned char **sigp, int *lenp, unsigned char *data, int datalen); + u_char **sigp, int *lenp, u_char *data, int datalen); struct Authctxt { const char *server_user; @@ -460,6 +468,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 */ @@ -470,22 +482,31 @@ void input_userauth_success(int type, int plen, void *ctxt); 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 authmethod_clear(); +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); + Authmethod *authmethod_get(char *authlist); Authmethod *authmethod_lookup(const char *name); +char *authmethods_get(void); Authmethod authmethods[] = { {"publickey", userauth_pubkey, - &options.dsa_authentication, + &options.pubkey_authentication, NULL}, {"password", userauth_passwd, @@ -509,6 +530,9 @@ int type; int plen; + if (options.challenge_reponse_authentication) + options.kbd_interactive_authentication = 1; + debug("send SSH2_MSG_SERVICE_REQUEST"); packet_start(SSH2_MSG_SERVICE_REQUEST); packet_put_cstring("ssh-userauth"); @@ -522,13 +546,15 @@ char *reply = packet_get_string(&plen); debug("service_accept: %s", reply); xfree(reply); - packet_done(); } else { debug("buggy server: service_accept w/o service"); } packet_done(); debug("got SSH2_MSG_SERVICE_ACCEPT"); + if (options.preferred_authentications == NULL) + options.preferred_authentications = authmethods_get(); + /* setup authentication context */ authctxt.agent = ssh_get_authentication_connection(); authctxt.server_user = server_user; @@ -536,9 +562,9 @@ 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(); /* initial userauth request */ userauth_none(&authctxt); @@ -546,30 +572,69 @@ dispatch_init(&input_userauth_error); dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); + dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ if (authctxt.agent != NULL) ssh_close_authentication_connection(authctxt.agent); - debug("ssh-userauth2 successfull: method %s", authctxt.method->name); + 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"); + fatal("input_userauth_error: bad message during authentication: " + "type %d", type); } void +input_userauth_banner(int type, int plen, void *ctxt) +{ + char *msg, *lang; + debug3("input_userauth_banner"); + msg = packet_get_string(NULL); + lang = packet_get_string(NULL); + fprintf(stderr, "%s", msg); + xfree(msg); + xfree(lang); +} +void 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; @@ -582,23 +647,78 @@ packet_done(); if (partial != 0) - debug("partial success"); + log("Authenticated with partial success."); debug("authentications that can continue: %s", authlist); - for (;;) { - method = authmethod_get(authlist); - if (method == NULL) - fatal("Unable to find an authentication method"); - 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, *fp; + + 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); + 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; + } + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + debug2("input_userauth_pk_ok: fp %s", fp); + xfree(fp); + 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 @@ -610,7 +730,6 @@ packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_send(); - packet_write_wait(); return 1; } @@ -627,7 +746,7 @@ if(attempt != 1) error("Permission denied, please try again."); - snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", + snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ", authctxt->server_user, authctxt->host); password = read_passphrase(prompt, 0); packet_start(SSH2_MSG_USERAUTH_REQUEST); @@ -638,28 +757,46 @@ packet_put_cstring(password); memset(password, 0, strlen(password)); 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) { Buffer b; - unsigned char *blob, *signature; + u_char *blob, *signature; int bloblen, slen; int skip = 0; int ret = -1; int have_sig = 1; - dsa_make_key_blob(k, &blob, &bloblen); + 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"); + return 0; + } /* data to be signed */ buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); - skip = session_id2_len; + skip = session_id2_len; } else { buffer_put_string(&b, session_id2, session_id2_len); skip = buffer_len(&b); @@ -667,36 +804,44 @@ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); buffer_put_cstring(&b, - datafellows & SSH_BUG_PUBKEYAUTH ? + datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); - buffer_put_cstring(&b, authctxt->method->name); - buffer_put_char(&b, have_sig); - buffer_put_cstring(&b, KEX_DSS); + if (datafellows & SSH_BUG_PKAUTH) { + buffer_put_char(&b, have_sig); + } else { + buffer_put_cstring(&b, authctxt->method->name); + buffer_put_char(&b, have_sig); + buffer_put_cstring(&b, key_ssh_name(k)); + } 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); return 0; } -#ifdef DEBUG_DSS +#ifdef DEBUG_PK buffer_dump(&b); #endif - if (datafellows & SSH_BUG_PUBKEYAUTH) { + 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); buffer_put_cstring(&b, authctxt->method->name); buffer_put_char(&b, have_sig); - buffer_put_cstring(&b, KEX_DSS); + if (!(datafellows & SSH_BUG_PKAUTH)) + buffer_put_cstring(&b, key_ssh_name(k)); buffer_put_string(&b, blob, bloblen); } xfree(blob); + /* append signature */ buffer_put_string(&b, signature, slen); xfree(signature); @@ -710,96 +855,143 @@ 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 dsa_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp, - unsigned char *data, int datalen) +int +send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback, + int hint) { - return dsa_sign(key, sigp, lenp, data, datalen); + u_char *blob; + int bloblen, have_sig = 0; + + 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; + } + /* 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; } -int -userauth_pubkey_identity(Authctxt *authctxt, char *filename) +Key * +load_identity_file(char *filename) { - Key *k; - int i, ret, try_next; + Key *private; + char prompt[300], *passphrase; + int success = 0, quit, i; struct stat st; - if (stat(filename, &st) != 0) { - debug("key does not exist: %s", filename); - return 0; + if (stat(filename, &st) < 0) { + debug3("no such identity: %s", filename); + return NULL; } - debug("try pubkey: %s", filename); - - k = key_new(KEY_DSA); - if (!load_private_key(filename, "", k, NULL)) { - int success = 0; - char *passphrase; - char prompt[300]; + private = key_new(KEY_UNSPEC); + if (!load_private_key(filename, "", private, NULL)) { + if (options.batch_mode) { + key_free(private); + return NULL; + } snprintf(prompt, sizeof prompt, - "Enter passphrase for %s key '%.100s': ", - key_type(k), filename); + "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, dsa_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, unsigned char **sigp, int *lenp, - unsigned char *data, int datalen) +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) { static int called = 0; + int ret = 0; char *comment; Key *k; - int ret; if (called == 0) { - k = ssh_get_first_identity(authctxt->agent, &comment, 2); + if (ssh_get_num_identities(authctxt->agent, 2) == 0) + debug2("userauth_pubkey_agent: no keys at all"); called = 1; - } else { - k = ssh_get_next_identity(authctxt->agent, &comment, 2); } + k = ssh_get_next_identity(authctxt->agent, &comment, 2); if (k == NULL) { - debug2("no more DSA keys from agent"); - return 0; + debug2("userauth_pubkey_agent: no more keys"); + } else { + debug("userauth_pubkey_agent: testing agent key %s", comment); + xfree(comment); + ret = send_pubkey_test(authctxt, k, agent_sign_cb, -1); + if (ret == 0) + key_free(k); } - debug("trying DSA agent key %s", comment); - xfree(comment); - ret = sign_and_send_pubkey(authctxt, k, agent_sign_cb); - key_free(k); + if (ret == 0) + debug2("userauth_pubkey_agent: no message sent"); return ret; } @@ -808,11 +1000,32 @@ { static int idx = 0; int sent = 0; + Key *key; + char *filename; - if (authctxt->agent != NULL) - sent = userauth_pubkey_agent(authctxt); - while (sent == 0 && idx < options.num_identity_files2) - sent = userauth_pubkey_identity(authctxt, options.identity_files2[idx++]); + if (authctxt->agent != NULL) { + do { + sent = userauth_pubkey_agent(authctxt); + } while(!sent && authctxt->agent->howmany > 0); + } + while (!sent && idx < options.num_identity_files) { + 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; } @@ -836,26 +1049,20 @@ 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; } /* - * parse SSH2_MSG_USERAUTH_INFO_REQUEST, prompt user and send - * SSH2_MSG_USERAUTH_INFO_RESPONSE + * parse INFO_REQUEST, prompt user and send INFO_RESPONSE */ void input_userauth_info_req(int type, int plen, void *ctxt) { Authctxt *authctxt = ctxt; - char *name = NULL; - char *inst = NULL; - char *lang = NULL; - char *prompt = NULL; - char *response = NULL; - unsigned int num_prompts, i; + char *name, *inst, *lang, *prompt, *response; + u_int num_prompts, i; int echo = 0; debug2("input_userauth_info_req"); @@ -866,15 +1073,13 @@ name = packet_get_string(NULL); inst = packet_get_string(NULL); lang = packet_get_string(NULL); - if (strlen(name) > 0) cli_mesg(name); - xfree(name); - if (strlen(inst) > 0) cli_mesg(inst); + xfree(name); xfree(inst); - xfree(lang); /* unused */ + xfree(lang); num_prompts = packet_get_int(); /* @@ -899,46 +1104,13 @@ } packet_done(); /* done with parsing incoming message. */ + packet_inject_ignore(64); packet_send(); - packet_write_wait(); } /* find auth method */ -#define DELIM "," - -static char *def_authlist = "publickey,password"; -static char *authlist_current = NULL; /* clean copy used for comparison */ -static char *authname_current = NULL; /* last used auth method */ -static char *authlist_working = NULL; /* copy that gets modified by strtok_r() */ -static char *authlist_state = NULL; /* state variable for strtok_r() */ - /* - * Before starting to use a new authentication method list sent by the - * server, reset internal variables. This should also be called when - * finished processing server list to free resources. - */ -void -authmethod_clear() -{ - if (authlist_current != NULL) { - xfree(authlist_current); - authlist_current = NULL; - } - if (authlist_working != NULL) { - xfree(authlist_working); - authlist_working = NULL; - } - if (authname_current != NULL) { - xfree(authname_current); - authlist_state = NULL; - } - if (authlist_state != NULL) - authlist_state = NULL; - return; -} - -/* * given auth method name, if configurable options permit this method fill * in auth_ident field and return true, otherwise return false. */ @@ -968,62 +1140,70 @@ return NULL; } +/* XXX internal state */ +static Authmethod *current = NULL; +static char *supported = NULL; +static char *preferred = NULL; /* * Given the authentication method list sent by the server, return the * next method we should try. If the server initially sends a nil list, - * use a built-in default list. If the server sends a nil list after - * previously sending a valid list, continue using the list originally - * sent. - */ - + * use a built-in default list. + */ Authmethod * authmethod_get(char *authlist) { - char *name = NULL, *authname_old; - Authmethod *method = NULL; - + + char *name = NULL; + int next; + /* Use a suitable default if we're passed a nil list. */ if (authlist == NULL || strlen(authlist) == 0) - authlist = def_authlist; + authlist = options.preferred_authentications; - if (authlist_current == NULL || strcmp(authlist, authlist_current) != 0) { - /* start over if passed a different list */ - debug3("start over, passed a different list"); - authmethod_clear(); - authlist_current = xstrdup(authlist); - authlist_working = xstrdup(authlist); - name = strtok_r(authlist_working, DELIM, &authlist_state); - } else { - /* - * try to use previously used authentication method - * or continue to use previously passed list - */ - name = (authname_current != NULL) ? - authname_current : strtok_r(NULL, DELIM, &authlist_state); - } + if (supported == NULL || strcmp(authlist, supported) != 0) { + debug3("start over, passed a different list %s", authlist); + if (supported != NULL) + xfree(supported); + supported = xstrdup(authlist); + preferred = options.preferred_authentications; + debug3("preferred %s", preferred); + current = NULL; + } else if (current != NULL && authmethod_is_enabled(current)) + return current; - while (name != NULL) { + for (;;) { + if ((name = match_list(preferred, supported, &next)) == NULL) { + debug("no more auth methods to try"); + current = NULL; + return NULL; + } + preferred += next; debug3("authmethod_lookup %s", name); - method = authmethod_lookup(name); - if (method != NULL && authmethod_is_enabled(method)) { + debug3("remaining preferred: %s", preferred); + if ((current = authmethod_lookup(name)) != NULL && + authmethod_is_enabled(current)) { debug3("authmethod_is_enabled %s", name); - break; + debug("next auth method to try is %s", name); + return current; } - name = strtok_r(NULL, DELIM, &authlist_state); - method = NULL; } +} - authname_old = authname_current; - if (method != NULL) { - debug("next auth method to try is %s", name); - authname_current = xstrdup(name); - } else { - debug("no more auth methods to try"); - authname_current = NULL; - } - if (authname_old != NULL) - xfree(authname_old); +#define DELIM "," +char * +authmethods_get(void) +{ + Authmethod *method = NULL; + char buf[1024]; - return (method); + buf[0] = '\0'; + for (method = authmethods; method->name != NULL; method++) { + if (authmethod_is_enabled(method)) { + if (buf[0] != '\0') + strlcat(buf, DELIM, sizeof buf); + strlcat(buf, method->name, sizeof buf); + } + } + return xstrdup(buf); }