=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/sshconnect2.c,v retrieving revision 1.27.2.8 retrieving revision 1.28 diff -u -r1.27.2.8 -r1.28 --- src/usr.bin/ssh/sshconnect2.c 2002/03/08 17:04:43 1.27.2.8 +++ src/usr.bin/ssh/sshconnect2.c 2000/11/12 19:50:38 1.28 @@ -23,29 +23,37 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect2.c,v 1.27.2.8 2002/03/08 17:04:43 brad Exp $"); +RCSID("$OpenBSD: sshconnect2.c,v 1.28 2000/11/12 19:50:38 markus 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 "cipher.h" +#include "ssh2.h" #include "kex.h" #include "myproposal.h" +#include "key.h" #include "sshconnect.h" #include "authfile.h" -#include "dh.h" -#include "authfd.h" -#include "log.h" -#include "readconf.h" -#include "readpass.h" -#include "match.h" +#include "cli.h" #include "dispatch.h" -#include "canohost.h" +#include "authfd.h" +void ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); +void ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); + /* import */ extern char *client_version_string; extern char *server_version_string; @@ -55,70 +63,79 @@ * SSH2 key exchange */ -u_char *session_id2 = NULL; +unsigned char *session_id2 = NULL; int session_id2_len = 0; -char *xxx_host; -struct sockaddr *xxx_hostaddr; - -Kex *xxx_kex = NULL; - -static int -verify_host_key_callback(Key *hostkey) -{ - if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) == -1) - fatal("Host key verification failed."); - return 0; -} - void ssh_kex2(char *host, struct sockaddr *hostaddr) { + int i, plen; Kex *kex; + Buffer *client_kexinit, *server_kexinit; + char *sprop[PROPOSAL_MAX]; - xxx_host = host; - xxx_hostaddr = hostaddr; - - if (options.ciphers == (char *)-1) { - log("No valid ciphers for protocol version 2 given, using defaults."); - options.ciphers = NULL; + 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 != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; } - myproposal[PROPOSAL_ENC_ALGS_CTOS] = - compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); - myproposal[PROPOSAL_ENC_ALGS_STOC] = - compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); if (options.compression) { - myproposal[PROPOSAL_COMP_ALGS_CTOS] = + myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; } else { - myproposal[PROPOSAL_COMP_ALGS_CTOS] = + myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; } - if (options.macs != NULL) { - myproposal[PROPOSAL_MAC_ALGS_CTOS] = - myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; - } - if (options.hostkeyalgorithms != NULL) - myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = - options.hostkeyalgorithms; - /* start key exchange */ - kex = kex_setup(myproposal); - kex->client_version_string=client_version_string; - kex->server_version_string=server_version_string; - kex->verify_host_key=&verify_host_key_callback; + /* buffers with raw kexinit messages */ + server_kexinit = xmalloc(sizeof(*server_kexinit)); + buffer_init(server_kexinit); + client_kexinit = kex_init(myproposal); - xxx_kex = kex; + /* algorithm negotiation */ + kex_exchange_kexinit(client_kexinit, server_kexinit, sprop); + kex = kex_choose_conf(myproposal, sprop, 0); + for (i = 0; i < PROPOSAL_MAX; i++) + xfree(sprop[i]); - dispatch_run(DISPATCH_BLOCK, &kex->done, kex); + /* server authentication and session key agreement */ + switch(kex->kex_type) { + case DH_GRP1_SHA1: + ssh_dh1_client(kex, host, hostaddr, + client_kexinit, server_kexinit); + break; + case DH_GEX_SHA1: + ssh_dhgex_client(kex, host, hostaddr, client_kexinit, + server_kexinit); + break; + default: + fatal("Unsupported key exchange %d", kex->kex_type); + } - session_id2 = kex->session_id; - session_id2_len = kex->session_id_len; + buffer_free(client_kexinit); + buffer_free(server_kexinit); + xfree(client_kexinit); + xfree(server_kexinit); + debug("Wait SSH2_MSG_NEWKEYS."); + packet_read_expect(&plen, SSH2_MSG_NEWKEYS); + packet_done(); + debug("GOT SSH2_MSG_NEWKEYS."); + + debug("send SSH2_MSG_NEWKEYS."); + packet_start(SSH2_MSG_NEWKEYS); + packet_send(); + packet_write_wait(); + debug("done: send SSH2_MSG_NEWKEYS."); + #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ packet_start(SSH2_MSG_IGNORE); @@ -126,10 +143,305 @@ packet_send(); packet_write_wait(); #endif - debug("done: ssh_kex2."); + debug("done: KEX2."); } +/* diffie-hellman-group1-sha1 */ + +void +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; + char *signature = NULL; + unsigned int slen; + char *server_host_key_blob = NULL; + Key *server_host_key; + unsigned int sbloblen; + DH *dh; + BIGNUM *dh_server_pub = 0; + BIGNUM *shared_secret = 0; + unsigned char *kbuf; + unsigned char *hash; + + debug("Sending SSH2_MSG_KEXDH_INIT."); + /* generate and send 'e', client DH public key */ + dh = dh_new_group1(); + packet_start(SSH2_MSG_KEXDH_INIT); + packet_put_bignum2(dh->pub_key); + packet_send(); + packet_write_wait(); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\np= "); + BN_print_fp(stderr, dh->p); + fprintf(stderr, "\ng= "); + BN_print_fp(stderr, dh->g); + fprintf(stderr, "\npub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); + DHparams_print_fp(stderr, dh); +#endif + + debug("Wait SSH2_MSG_KEXDH_REPLY."); + + packet_read_expect(&plen, SSH2_MSG_KEXDH_REPLY); + + debug("Got SSH2_MSG_KEXDH_REPLY."); + + /* key, cert */ + server_host_key_blob = packet_get_string(&sbloblen); + server_host_key = key_from_blob(server_host_key_blob, sbloblen); + if (server_host_key == NULL) + fatal("cannot decode server_host_key_blob"); + + check_host_key(host, hostaddr, server_host_key, + options.user_hostfile2, options.system_hostfile2); + + /* DH paramter f, server public DH key */ + dh_server_pub = BN_new(); + if (dh_server_pub == NULL) + fatal("dh_server_pub == NULL"); + packet_get_bignum2(dh_server_pub, &dlen); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\ndh_server_pub= "); + BN_print_fp(stderr, dh_server_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_server_pub)); +#endif + + /* signed H */ + signature = packet_get_string(&slen); + packet_done(); + + if (!dh_pub_is_valid(dh, dh_server_pub)) + packet_disconnect("bad server public DH value"); + + klen = DH_size(dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_server_pub, dh); +#ifdef DEBUG_KEXDH + debug("shared secret: len %d/%d", klen, kout); + fprintf(stderr, "shared secret == "); + for (i = 0; i< kout; i++) + fprintf(stderr, "%02x", (kbuf[i])&0xff); + fprintf(stderr, "\n"); +#endif + shared_secret = BN_new(); + + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + /* calc and verify H */ + hash = kex_hash( + client_version_string, + server_version_string, + buffer_ptr(client_kexinit), buffer_len(client_kexinit), + buffer_ptr(server_kexinit), buffer_len(server_kexinit), + server_host_key_blob, sbloblen, + dh->pub_key, + dh_server_pub, + shared_secret + ); + xfree(server_host_key_blob); + DH_free(dh); +#ifdef DEBUG_KEXDH + fprintf(stderr, "hash == "); + for (i = 0; i< 20; i++) + fprintf(stderr, "%02x", (hash[i])&0xff); + fprintf(stderr, "\n"); +#endif + if (key_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) + fatal("key_verify failed for server_host_key"); + key_free(server_host_key); + + kex_derive_keys(kex, hash, shared_secret); + packet_set_kex(kex); + + /* save session id */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); +} + +/* diffie-hellman-group-exchange-sha1 */ + /* + * Estimates the group order for a Diffie-Hellman group that has an + * attack complexity approximately the same as O(2**bits). Estimate + * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3))) + */ + +int +dh_estimate(int bits) +{ + + if (bits < 64) + return (512); /* O(2**63) */ + if (bits < 128) + return (1024); /* O(2**86) */ + if (bits < 192) + return (2048); /* O(2**116) */ + return (4096); /* O(2**156) */ +} + +void +ssh_dhgex_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; + char *signature = NULL; + unsigned int slen, nbits; + char *server_host_key_blob = NULL; + Key *server_host_key; + unsigned int sbloblen; + DH *dh; + BIGNUM *dh_server_pub = 0; + BIGNUM *shared_secret = 0; + BIGNUM *p = 0, *g = 0; + unsigned char *kbuf; + unsigned char *hash; + + nbits = dh_estimate(kex->enc[MODE_OUT].cipher->key_len * 8); + + debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST."); + packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST); + packet_put_int(nbits); + packet_send(); + packet_write_wait(); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\nnbits = %d", nbits); +#endif + + debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP."); + + packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP); + + debug("Got SSH2_MSG_KEX_DH_GEX_GROUP."); + + if ((p = BN_new()) == NULL) + fatal("BN_new"); + packet_get_bignum2(p, &dlen); + 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"); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\np= "); + BN_print_fp(stderr, dh->p); + fprintf(stderr, "\ng= "); + BN_print_fp(stderr, dh->g); + fprintf(stderr, "\npub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); + DHparams_print_fp(stderr, dh); +#endif + + debug("Sending SSH2_MSG_KEX_DH_GEX_INIT."); + /* generate and send 'e', client DH public key */ + packet_start(SSH2_MSG_KEX_DH_GEX_INIT); + packet_put_bignum2(dh->pub_key); + packet_send(); + packet_write_wait(); + + debug("Wait SSH2_MSG_KEX_DH_GEX_REPLY."); + + packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_REPLY); + + debug("Got SSH2_MSG_KEXDH_REPLY."); + + /* key, cert */ + server_host_key_blob = packet_get_string(&sbloblen); + server_host_key = key_from_blob(server_host_key_blob, sbloblen); + if (server_host_key == NULL) + fatal("cannot decode server_host_key_blob"); + + check_host_key(host, hostaddr, server_host_key, + options.user_hostfile2, options.system_hostfile2); + + /* DH paramter f, server public DH key */ + dh_server_pub = BN_new(); + if (dh_server_pub == NULL) + fatal("dh_server_pub == NULL"); + packet_get_bignum2(dh_server_pub, &dlen); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\ndh_server_pub= "); + BN_print_fp(stderr, dh_server_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_server_pub)); +#endif + + /* signed H */ + signature = packet_get_string(&slen); + packet_done(); + + if (!dh_pub_is_valid(dh, dh_server_pub)) + packet_disconnect("bad server public DH value"); + + klen = DH_size(dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_server_pub, dh); +#ifdef DEBUG_KEXDH + debug("shared secret: len %d/%d", klen, kout); + fprintf(stderr, "shared secret == "); + for (i = 0; i< kout; i++) + fprintf(stderr, "%02x", (kbuf[i])&0xff); + fprintf(stderr, "\n"); +#endif + shared_secret = BN_new(); + + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + /* calc and verify H */ + hash = kex_hash_gex( + client_version_string, + server_version_string, + 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, + dh->pub_key, + dh_server_pub, + shared_secret + ); + xfree(server_host_key_blob); + DH_free(dh); +#ifdef DEBUG_KEXDH + fprintf(stderr, "hash == "); + for (i = 0; i< 20; i++) + fprintf(stderr, "%02x", (hash[i])&0xff); + fprintf(stderr, "\n"); +#endif + if (key_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) + fatal("key_verify failed for server_host_key"); + key_free(server_host_key); + + kex_derive_keys(kex, hash, shared_secret); + packet_set_kex(kex); + + /* save session id */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); +} + +/* * Authenticate user */ @@ -138,26 +450,15 @@ typedef int sign_cb_fn( Authctxt *authctxt, Key *key, - u_char **sigp, u_int *lenp, u_char *data, u_int datalen); + unsigned char **sigp, int *lenp, unsigned char *data, int datalen); struct Authctxt { const char *server_user; - const char *local_user; const char *host; const char *service; + AuthenticationConnection *agent; Authmethod *method; int success; - char *authlist; - /* pubkey */ - Key *last_key; - sign_cb_fn *last_key_sign; - int last_key_hint; - AuthenticationConnection *agent; - /* hostbased */ - Key **keys; - int nkeys; - /* kbd-interactive */ - int info_req_seen; }; struct Authmethod { char *name; /* string to compare against server's list */ @@ -166,45 +467,33 @@ int *batch_flag; /* flag in option struct that disables method */ }; -void input_userauth_success(int, u_int32_t, void *); -void input_userauth_failure(int, u_int32_t, void *); -void input_userauth_banner(int, u_int32_t, void *); -void input_userauth_error(int, u_int32_t, void *); -void input_userauth_info_req(int, u_int32_t, void *); -void input_userauth_pk_ok(int, u_int32_t, void *); +void input_userauth_success(int type, int plen, void *ctxt); +void input_userauth_failure(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); -int userauth_none(Authctxt *); -int userauth_pubkey(Authctxt *); -int userauth_passwd(Authctxt *); -int userauth_kbdint(Authctxt *); -int userauth_hostbased(Authctxt *); +int userauth_none(Authctxt *authctxt); +int userauth_pubkey(Authctxt *authctxt); +int userauth_passwd(Authctxt *authctxt); +int userauth_kbdint(Authctxt *authctxt); -void userauth(Authctxt *, char *); +void authmethod_clear(); +Authmethod *authmethod_get(char *authlist); +Authmethod *authmethod_lookup(const char *name); -static int sign_and_send_pubkey(Authctxt *, Key *, sign_cb_fn *); -static void clear_auth_state(Authctxt *); - -static Authmethod *authmethod_get(char *authlist); -static Authmethod *authmethod_lookup(const char *name); -static char *authmethods_get(void); - Authmethod authmethods[] = { - {"hostbased", - userauth_hostbased, - &options.hostbased_authentication, - NULL}, {"publickey", userauth_pubkey, &options.pubkey_authentication, NULL}, - {"keyboard-interactive", - userauth_kbdint, - &options.kbd_interactive_authentication, - &options.batch_mode}, {"password", userauth_passwd, &options.password_authentication, &options.batch_mode}, + {"keyboard-interactive", + userauth_kbdint, + &options.kbd_interactive_authentication, + &options.batch_mode}, {"none", userauth_none, NULL, @@ -213,52 +502,42 @@ }; void -ssh_userauth2(const char *local_user, const char *server_user, char *host, - Key **keys, int nkeys) +ssh_userauth2(const char *server_user, char *host) { Authctxt authctxt; int type; + int plen; - if (options.challenge_response_authentication) - options.kbd_interactive_authentication = 1; - debug("send SSH2_MSG_SERVICE_REQUEST"); packet_start(SSH2_MSG_SERVICE_REQUEST); packet_put_cstring("ssh-userauth"); packet_send(); packet_write_wait(); - type = packet_read(); + type = packet_read(&plen); if (type != SSH2_MSG_SERVICE_ACCEPT) { fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type); } if (packet_remaining() > 0) { - char *reply = packet_get_string(NULL); + 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_check_eom(); + packet_done(); debug("got SSH2_MSG_SERVICE_ACCEPT"); - if (options.preferred_authentications == NULL) - options.preferred_authentications = authmethods_get(); - /* setup authentication context */ - memset(&authctxt, 0, sizeof(authctxt)); authctxt.agent = ssh_get_authentication_connection(); authctxt.server_user = server_user; - authctxt.local_user = local_user; authctxt.host = host; authctxt.service = "ssh-connection"; /* service name */ authctxt.success = 0; authctxt.method = authmethod_lookup("none"); - authctxt.authlist = NULL; - authctxt.keys = keys; - authctxt.nkeys = nkeys; - authctxt.info_req_seen = 0; if (authctxt.method == NULL) fatal("ssh_userauth2: internal error: cannot send userauth none request"); + authmethod_clear(); /* initial userauth request */ userauth_none(&authctxt); @@ -266,69 +545,30 @@ 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 successful: method %s", authctxt.method->name); + debug("ssh-userauth2 successfull: method %s", authctxt.method->name); } void -userauth(Authctxt *authctxt, char *authlist) +input_userauth_error(int type, int plen, void *ctxt) { - 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; - } - } + fatal("input_userauth_error: bad message during authentication"); } void -input_userauth_error(int type, u_int32_t seq, void *ctxt) +input_userauth_success(int type, int plen, void *ctxt) { - fatal("input_userauth_error: bad message during authentication: " - "type %d", type); -} -void -input_userauth_banner(int type, u_int32_t seq, 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, u_int32_t seq, 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, u_int32_t seq, void *ctxt) +input_userauth_failure(int type, int plen, void *ctxt) { + Authmethod *method = NULL; Authctxt *authctxt = ctxt; char *authlist = NULL; int partial; @@ -338,89 +578,26 @@ authlist = packet_get_string(NULL); partial = packet_get_char(); - packet_check_eom(); + packet_done(); if (partial != 0) - log("Authenticated with partial success."); + debug("partial success"); debug("authentications that can continue: %s", authlist); - clear_auth_state(authctxt); - userauth(authctxt, authlist); -} -void -input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) -{ - Authctxt *authctxt = ctxt; - Key *key = NULL; - Buffer b; - int pktype, sent = 0; - u_int alen, blen; - char *pkalg, *fp; - u_char *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_check_eom(); - - 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"); + 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); break; + } else { + debug2("we did not send a packet, disable method"); + method->enabled = NULL; } - 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; - } - if (key->type != pktype) { - error("input_userauth_pk_ok: type mismatch " - "for decoded key (received %d, expected %d)", - key->type, pktype); - 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); - + } + xfree(authlist); } int @@ -432,6 +609,7 @@ packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_send(); + packet_write_wait(); return 1; } @@ -445,10 +623,10 @@ if (attempt++ >= options.number_of_password_prompts) return 0; - if (attempt != 1) + if(attempt != 1) error("Permission denied, please try again."); - snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ", + snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", authctxt->server_user, authctxt->host); password = read_passphrase(prompt, 0); packet_start(SSH2_MSG_USERAUTH_REQUEST); @@ -459,46 +637,30 @@ packet_put_cstring(password); memset(password, 0, strlen(password)); xfree(password); - packet_add_padding(64); packet_send(); + packet_write_wait(); return 1; } -static 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; -} - -static int +int sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) { Buffer b; - u_char *blob, *signature; - u_int bloblen, slen; + unsigned char *blob, *signature; + int bloblen, slen; int skip = 0; int ret = -1; 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"); 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); @@ -506,21 +668,16 @@ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); buffer_put_cstring(&b, - datafellows & SSH_BUG_PKSERVICE ? + datafellows & SSH_BUG_PUBKEYAUTH ? "ssh-userauth" : authctxt->service); - 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_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); @@ -529,21 +686,18 @@ #ifdef DEBUG_PK buffer_dump(&b); #endif - if (datafellows & SSH_BUG_PKSERVICE) { + if (datafellows & SSH_BUG_PUBKEYAUTH) { 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); - if (!(datafellows & SSH_BUG_PKAUTH)) - buffer_put_cstring(&b, key_ssh_name(k)); + 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); @@ -557,120 +711,74 @@ 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; } -static int -send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback, - int hint) +/* sign callback */ +int key_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp, + unsigned char *data, int datalen) { - u_char *blob; - u_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; + return key_sign(key, sigp, lenp, data, datalen); } -static Key * -load_identity_file(char *filename) +int +userauth_pubkey_identity(Authctxt *authctxt, char *filename) { - Key *private; - char prompt[300], *passphrase; - int quit, i; + Key *k; + int i, ret, try_next; struct stat st; - if (stat(filename, &st) < 0) { - debug3("no such identity: %s", filename); - return NULL; + if (stat(filename, &st) != 0) { + debug("key does not exist: %s", filename); + return 0; } - private = key_load_private_type(KEY_UNSPEC, filename, "", NULL); - if (private == NULL) { - if (options.batch_mode) - return NULL; + debug("try pubkey: %s", filename); + + k = key_new(KEY_UNSPEC); + if (!load_private_key(filename, "", k, NULL)) { + int success = 0; + char *passphrase; + char prompt[300]; snprintf(prompt, sizeof prompt, - "Enter passphrase for key '%.100s': ", 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) { - private = key_load_private_type(KEY_UNSPEC, filename, - passphrase, NULL); - quit = 0; + success = load_private_key(filename, passphrase, k, NULL); + try_next = 0; } else { debug2("no passphrase given, try next key"); - quit = 1; + try_next = 1; } memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); - if (private != NULL || quit) + if (success || try_next) break; debug2("bad passphrase given, try again..."); } + if (!success) { + key_free(k); + return 0; + } } - return private; -} - -static int -identity_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, u_int *lenp, - u_char *data, u_int datalen) -{ - Key *private; - int idx, ret; - - idx = authctxt->last_key_hint; - if (idx < 0) - return -1; - - /* private key is stored in external hardware */ - if (options.identity_keys[idx]->flags & KEY_FLAG_EXT) - return key_sign(options.identity_keys[idx], sigp, lenp, data, datalen); - - private = load_identity_file(options.identity_files[idx]); - if (private == NULL) - return -1; - ret = key_sign(private, sigp, lenp, data, datalen); - key_free(private); + ret = sign_and_send_pubkey(authctxt, k, key_sign_cb); + key_free(k); return ret; } -static int -agent_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, u_int *lenp, - u_char *data, u_int datalen) +/* sign callback */ +int agent_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp, + unsigned char *data, int datalen) { return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen); } -static int -key_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, u_int *lenp, - u_char *data, u_int datalen) -{ - return key_sign(key, sigp, lenp, data, datalen); -} - -static int +int userauth_pubkey_agent(Authctxt *authctxt) { static int called = 0; @@ -687,11 +795,10 @@ if (k == NULL) { debug2("userauth_pubkey_agent: no more keys"); } else { - debug("userauth_pubkey_agent: testing agent key %s", comment); + debug("userauth_pubkey_agent: trying agent key %s", comment); xfree(comment); - ret = send_pubkey_test(authctxt, k, agent_sign_cb, -1); - if (ret == 0) - key_free(k); + ret = sign_and_send_pubkey(authctxt, k, agent_sign_cb); + key_free(k); } if (ret == 0) debug2("userauth_pubkey_agent: no message sent"); @@ -703,30 +810,16 @@ { static int idx = 0; int sent = 0; - Key *key; - char *filename; if (authctxt->agent != NULL) { do { sent = userauth_pubkey_agent(authctxt); - } while (!sent && authctxt->agent->howmany > 0); + } 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); - } + if (options.identity_files_type[idx] != KEY_RSA1) + sent = userauth_pubkey_identity(authctxt, + options.identity_files[idx]); idx++; } return sent; @@ -742,12 +835,6 @@ if (attempt++ >= options.number_of_password_prompts) return 0; - /* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */ - if (attempt > 1 && !authctxt->info_req_seen) { - debug3("userauth_kbdint: disable: no info_req_seen"); - dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL); - return 0; - } debug2("userauth_kbdint"); packet_start(SSH2_MSG_USERAUTH_REQUEST); @@ -758,20 +845,26 @@ 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 INFO_REQUEST, prompt user and send INFO_RESPONSE + * parse SSH2_MSG_USERAUTH_INFO_REQUEST, prompt user and send + * SSH2_MSG_USERAUTH_INFO_RESPONSE */ void -input_userauth_info_req(int type, u_int32_t seq, void *ctxt) +input_userauth_info_req(int type, int plen, void *ctxt) { Authctxt *authctxt = ctxt; - char *name, *inst, *lang, *prompt, *response; - u_int num_prompts, i; + char *name = NULL; + char *inst = NULL; + char *lang = NULL; + char *prompt = NULL; + char *response = NULL; + unsigned int num_prompts, i; int echo = 0; debug2("input_userauth_info_req"); @@ -779,18 +872,18 @@ if (authctxt == NULL) fatal("input_userauth_info_req: no authentication context"); - authctxt->info_req_seen = 1; - name = packet_get_string(NULL); inst = packet_get_string(NULL); lang = packet_get_string(NULL); + if (strlen(name) > 0) - log("%s", name); - if (strlen(inst) > 0) - log("%s", inst); + cli_mesg(name); xfree(name); + + if (strlen(inst) > 0) + cli_mesg(inst); xfree(inst); - xfree(lang); + xfree(lang); /* unused */ num_prompts = packet_get_int(); /* @@ -802,121 +895,63 @@ packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); packet_put_int(num_prompts); - debug2("input_userauth_info_req: num_prompts %d", num_prompts); for (i = 0; i < num_prompts; i++) { prompt = packet_get_string(NULL); echo = packet_get_char(); - response = read_passphrase(prompt, echo ? RP_ECHO : 0); + response = cli_prompt(prompt, echo); packet_put_cstring(response); memset(response, 0, strlen(response)); xfree(response); xfree(prompt); } - packet_check_eom(); /* done with parsing incoming message. */ + packet_done(); /* done with parsing incoming message. */ - packet_add_padding(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() */ + /* - * this will be move to an external program (ssh-keysign) ASAP. ssh-keysign - * will be setuid-root and the sbit can be removed from /usr/bin/ssh. + * 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. */ -int -userauth_hostbased(Authctxt *authctxt) +void +authmethod_clear() { - Key *private = NULL; - Buffer b; - u_char *signature, *blob; - char *chost, *pkalg, *p; - const char *service; - u_int blen, slen; - int ok, i, len, found = 0; - - /* check for a useful key */ - for (i = 0; i < authctxt->nkeys; i++) { - private = authctxt->keys[i]; - if (private && private->type != KEY_RSA1) { - found = 1; - /* we take and free the key */ - authctxt->keys[i] = NULL; - break; - } + if (authlist_current != NULL) { + xfree(authlist_current); + authlist_current = NULL; } - if (!found) { - debug("userauth_hostbased: no more client hostkeys"); - return 0; + if (authlist_working != NULL) { + xfree(authlist_working); + authlist_working = NULL; } - if (key_to_blob(private, &blob, &blen) == 0) { - key_free(private); - return 0; + if (authname_current != NULL) { + xfree(authname_current); + authlist_state = NULL; } - /* figure out a name for the client host */ - p = get_local_name(packet_get_connection_in()); - if (p == NULL) { - error("userauth_hostbased: cannot get local ipaddr/name"); - key_free(private); - return 0; - } - len = strlen(p) + 2; - chost = xmalloc(len); - strlcpy(chost, p, len); - strlcat(chost, ".", len); - debug2("userauth_hostbased: chost %s", chost); - - service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : - authctxt->service; - pkalg = xstrdup(key_ssh_name(private)); - buffer_init(&b); - /* construct data */ - buffer_put_string(&b, session_id2, session_id2_len); - buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); - buffer_put_cstring(&b, authctxt->server_user); - buffer_put_cstring(&b, service); - buffer_put_cstring(&b, authctxt->method->name); - buffer_put_cstring(&b, pkalg); - buffer_put_string(&b, blob, blen); - buffer_put_cstring(&b, chost); - buffer_put_cstring(&b, authctxt->local_user); -#ifdef DEBUG_PK - buffer_dump(&b); -#endif - ok = key_sign(private, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); - key_free(private); - buffer_free(&b); - if (ok != 0) { - error("key_sign failed"); - xfree(chost); - xfree(pkalg); - return 0; - } - 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_cstring(pkalg); - packet_put_string(blob, blen); - packet_put_cstring(chost); - packet_put_cstring(authctxt->local_user); - packet_put_string(signature, slen); - memset(signature, 's', slen); - xfree(signature); - xfree(chost); - xfree(pkalg); - - packet_send(); - return 1; + if (authlist_state != NULL) + authlist_state = NULL; + return; } -/* find auth method */ - /* * given auth method name, if configurable options permit this method fill * in auth_ident field and return true, otherwise return false. */ -static int +int authmethod_is_enabled(Authmethod *method) { if (method == NULL) @@ -930,7 +965,7 @@ return 1; } -static Authmethod * +Authmethod * authmethod_lookup(const char *name) { Authmethod *method = NULL; @@ -942,72 +977,62 @@ 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. - */ -static Authmethod * + * 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. + */ + +Authmethod * authmethod_get(char *authlist) { - - char *name = NULL; - u_int next; - + char *name = NULL, *authname_old; + Authmethod *method = NULL; + /* Use a suitable default if we're passed a nil list. */ if (authlist == NULL || strlen(authlist) == 0) - authlist = options.preferred_authentications; + authlist = def_authlist; - 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; + 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); + } - for (;;) { - if ((name = match_list(preferred, supported, &next)) == NULL) { - debug("no more auth methods to try"); - current = NULL; - return NULL; - } - preferred += next; + while (name != NULL) { debug3("authmethod_lookup %s", name); - debug3("remaining preferred: %s", preferred); - if ((current = authmethod_lookup(name)) != NULL && - authmethod_is_enabled(current)) { + method = authmethod_lookup(name); + if (method != NULL && authmethod_is_enabled(method)) { debug3("authmethod_is_enabled %s", name); - debug("next auth method to try is %s", name); - return current; + break; } + name = strtok_r(NULL, DELIM, &authlist_state); + method = NULL; } -} -static char * -authmethods_get(void) -{ - Authmethod *method = NULL; - Buffer b; - char *list; - - buffer_init(&b); - for (method = authmethods; method->name != NULL; method++) { - if (authmethod_is_enabled(method)) { - if (buffer_len(&b) > 0) - buffer_append(&b, ",", 1); - buffer_append(&b, method->name, strlen(method->name)); - } + 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; } - buffer_append(&b, "\0", 1); - list = xstrdup(buffer_ptr(&b)); - buffer_free(&b); - return list; + + if (authname_old != NULL) + xfree(authname_old); + + return (method); }