diff options
Diffstat (limited to 'sshconnect2.c')
-rw-r--r-- | sshconnect2.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/sshconnect2.c b/sshconnect2.c new file mode 100644 index 00000000..31ef3084 --- /dev/null +++ b/sshconnect2.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$OpenBSD: sshconnect2.c,v 1.4 2000/04/27 17:54:01 markus Exp $"); + +#include <openssl/bn.h> +#include <openssl/rsa.h> +#include <openssl/dsa.h> +#include <openssl/md5.h> +#include <openssl/dh.h> +#include <openssl/hmac.h> + +#include "ssh.h" +#include "xmalloc.h" +#include "rsa.h" +#include "buffer.h" +#include "packet.h" +#include "cipher.h" +#include "uidswap.h" +#include "compat.h" +#include "readconf.h" +#include "bufaux.h" +#include "ssh2.h" +#include "kex.h" +#include "myproposal.h" +#include "key.h" +#include "dsa.h" +#include "sshconnect.h" +#include "authfile.h" + +/* import */ +extern char *client_version_string; +extern char *server_version_string; +extern Options options; + +/* + * SSH2 key exchange + */ + +unsigned char *session_id2 = NULL; +int session_id2_len = 0; + +void +ssh_kex2(char *host, struct sockaddr *hostaddr) +{ + Kex *kex; + char *cprop[PROPOSAL_MAX]; + char *sprop[PROPOSAL_MAX]; + Buffer *client_kexinit; + Buffer *server_kexinit; + int payload_len, dlen; + unsigned int klen, kout; + char *ptr; + 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; + int i; + unsigned char *kbuf; + unsigned char *hash; + +/* KEXINIT */ + + debug("Sending KEX init."); + if (options.ciphers != NULL) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; + } else if ( + options.cipher == SSH_CIPHER_ARCFOUR || + options.cipher == SSH_CIPHER_3DES_CBC || + options.cipher == SSH_CIPHER_CAST128_CBC || + options.cipher == SSH_CIPHER_BLOWFISH_CBC) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = cipher_name(options.cipher); + } + if (options.compression) { + myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; + myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; + } else { + myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; + myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; + } + for (i = 0; i < PROPOSAL_MAX; i++) + cprop[i] = xstrdup(myproposal[i]); + + client_kexinit = kex_init(cprop); + packet_start(SSH2_MSG_KEXINIT); + packet_put_raw(buffer_ptr(client_kexinit), buffer_len(client_kexinit)); + packet_send(); + packet_write_wait(); + + debug("done"); + + packet_read_expect(&payload_len, SSH2_MSG_KEXINIT); + + /* save payload for session_id */ + server_kexinit = xmalloc(sizeof(*server_kexinit)); + buffer_init(server_kexinit); + ptr = packet_get_raw(&payload_len); + buffer_append(server_kexinit, ptr, payload_len); + + /* skip cookie */ + for (i = 0; i < 16; i++) + (void) packet_get_char(); + /* kex init proposal strings */ + for (i = 0; i < PROPOSAL_MAX; i++) { + sprop[i] = packet_get_string(NULL); + debug("got kexinit string: %s", sprop[i]); + } + i = (int) packet_get_char(); + debug("first kex follow == %d", i); + i = packet_get_int(); + debug("reserved == %d", i); + packet_done(); + + debug("done read kexinit"); + kex = kex_choose_conf(cprop, sprop, 0); + +/* KEXDH */ + + 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= "); + bignum_print(dh->p); + fprintf(stderr, "\ng= "); + bignum_print(dh->g); + fprintf(stderr, "\npub= "); + bignum_print(dh->pub_key); + fprintf(stderr, "\n"); + DHparams_print_fp(stderr, dh); +#endif + + debug("Wait SSH2_MSG_KEXDH_REPLY."); + + packet_read_expect(&payload_len, SSH2_MSG_KEXDH_REPLY); + + debug("Got SSH2_MSG_KEXDH_REPLY."); + + /* key, cert */ + server_host_key_blob = packet_get_string(&sbloblen); + server_host_key = dsa_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= "); + bignum_print(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); + buffer_free(client_kexinit); + buffer_free(server_kexinit); + xfree(client_kexinit); + xfree(server_kexinit); +#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"); + key_free(server_host_key); + + kex_derive_keys(kex, hash, shared_secret); + packet_set_kex(kex); + + /* have keys, free DH */ + DH_free(dh); + + /* save session id */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); + + debug("Wait SSH2_MSG_NEWKEYS."); + packet_read_expect(&payload_len, 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); + packet_put_cstring("markus"); + packet_send(); + packet_write_wait(); +#endif + debug("done: KEX2."); +} +/* + * Authenticate user + */ +int +ssh2_try_passwd(const char *server_user, const char *host, const char *service) +{ + char prompt[80]; + char *password; + + snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", + server_user, host); + password = read_passphrase(prompt, 0); + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(server_user); + packet_put_cstring(service); + packet_put_cstring("password"); + packet_put_char(0); + packet_put_cstring(password); + memset(password, 0, strlen(password)); + xfree(password); + packet_send(); + packet_write_wait(); + return 1; +} + +int +ssh2_try_pubkey(char *filename, + const char *server_user, const char *host, const char *service) +{ + Buffer b; + Key *k; + unsigned char *blob, *signature; + int bloblen, slen; + + 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]; + snprintf(prompt, sizeof prompt, + "Enter passphrase for DSA key '%.100s': ", + filename); + passphrase = read_passphrase(prompt, 0); + success = load_private_key(filename, passphrase, k, NULL); + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + if (!success) + return 0; + } + dsa_make_key_blob(k, &blob, &bloblen); + + /* data to be signed */ + buffer_init(&b); + buffer_append(&b, session_id2, session_id2_len); + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, server_user); + buffer_put_cstring(&b, service); + buffer_put_cstring(&b, "publickey"); + buffer_put_char(&b, 1); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_string(&b, blob, bloblen); + xfree(blob); + + /* generate signature */ + dsa_sign(k, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); + key_free(k); +#ifdef DEBUG_DSS + buffer_dump(&b); +#endif + /* append signature */ + buffer_put_string(&b, signature, slen); + xfree(signature); + + /* skip session id and packet type */ + if (buffer_len(&b) < session_id2_len + 1) + fatal("ssh2_try_pubkey: internal error"); + buffer_consume(&b, session_id2_len + 1); + + /* put remaining data from buffer into packet */ + 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; +} + +void +ssh_userauth2(const char *server_user, char *host) +{ + int type; + int plen; + int sent; + unsigned int dlen; + int partial; + int i = 0; + char *auths; + char *service = "ssh-connection"; /* service name */ + + 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(&plen); + if (type != SSH2_MSG_SERVICE_ACCEPT) { + fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type); + } + if (packet_remaining() > 0) { + char *reply = packet_get_string(&plen); + debug("service_accept: %s", reply); + xfree(reply); + } else { + /* payload empty for ssh-2.0.13 ?? */ + log("buggy server: service_accept w/o service"); + } + packet_done(); + debug("got SSH2_MSG_SERVICE_ACCEPT"); + + /* INITIAL request for auth */ + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(server_user); + packet_put_cstring(service); + packet_put_cstring("none"); + packet_send(); + packet_write_wait(); + + for (;;) { + sent = 0; + type = packet_read(&plen); + if (type == SSH2_MSG_USERAUTH_SUCCESS) + break; + if (type != SSH2_MSG_USERAUTH_FAILURE) + fatal("access denied: %d", type); + /* SSH2_MSG_USERAUTH_FAILURE means: try again */ + auths = packet_get_string(&dlen); + debug("authentications that can continue: %s", auths); + partial = packet_get_char(); + packet_done(); + if (partial) + debug("partial success"); + if (options.rsa_authentication && + strstr(auths, "publickey") != NULL) { + while (i < options.num_identity_files2) { + sent = ssh2_try_pubkey( + options.identity_files2[i++], + server_user, host, service); + if (sent) + break; + } + } + if (!sent) { + if (options.password_authentication && + !options.batch_mode && + strstr(auths, "password") != NULL) { + sent = ssh2_try_passwd(server_user, host, service); + } + } + if (!sent) + fatal("Permission denied (%s).", auths); + xfree(auths); + } + packet_done(); + debug("ssh-userauth2 successfull"); +} |