/* * 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.11 2000/05/25 20:45:20 markus Exp $"); #include #include #include #include #include #include #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_kex_dh(Kex *kex, char *host, struct sockaddr *hostaddr, Buffer *client_kexinit, Buffer *server_kexinit) { int i; 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= "); 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(&plen, 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); 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 (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); /* save session id */ session_id2_len = 20; session_id2 = xmalloc(session_id2_len); memcpy(session_id2, hash, session_id2_len); } void ssh_kex2(char *host, struct sockaddr *hostaddr) { int i, plen; Kex *kex; Buffer *client_kexinit, *server_kexinit; char *sprop[PROPOSAL_MAX]; if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; } else if (options.cipher == SSH_CIPHER_3DES) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = (char *) cipher_name(SSH_CIPHER_3DES_CBC); } else if (options.cipher == SSH_CIPHER_BLOWFISH) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = (char *) cipher_name(SSH_CIPHER_BLOWFISH_CBC); } 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"; } /* buffers with raw kexinit messages */ server_kexinit = xmalloc(sizeof(*server_kexinit)); buffer_init(server_kexinit); client_kexinit = kex_init(myproposal); /* 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]); /* server authentication and session key agreement */ ssh_kex_dh(kex, host, hostaddr, client_kexinit, server_kexinit); 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); 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) { static int attempt = 0; char prompt[80]; char *password; if (attempt++ > options.number_of_password_prompts) return 0; 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; struct stat st; if (stat(filename, &st) != 0) { debug("key does not exist: %s", filename); return 0; } 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, datafellows & SSH_BUG_PUBKEYAUTH ? "ssh-userauth" : service); buffer_put_cstring(&b, "publickey"); buffer_put_char(&b, 1); buffer_put_cstring(&b, KEX_DSS); buffer_put_string(&b, blob, bloblen); /* generate signature */ dsa_sign(k, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); key_free(k); #ifdef DEBUG_DSS buffer_dump(&b); #endif if (datafellows & SSH_BUG_PUBKEYAUTH) { /* e.g. ssh-2.0.13: data-to-be-signed != data-on-the-wire */ buffer_clear(&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); /* 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 ?? */ debug("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.dsa_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"); }