summaryrefslogtreecommitdiff
path: root/sshconnect.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2000-04-06 12:32:37 +1000
committerDamien Miller <djm@mindrot.org>2000-04-06 12:32:37 +1000
commit1383bd8eb91a8ec9c8d283679faec5925b0ccc42 (patch)
treef71278df6c50983ea3dad850ae79c45c340d9362 /sshconnect.c
parent74a333bbe11f67c59c559e0f424d5945eb438577 (diff)
- OpenBSD CVS update:
- [channels.c] close efd on eof - [clientloop.c compat.c ssh.c sshconnect.c myproposal.h] ssh2 client implementation, interops w/ ssh.com and lsh servers. - [sshconnect.c] missing free. - [authfile.c cipher.c cipher.h packet.c sshconnect.c sshd.c] remove unused argument, split cipher_mask() - [clientloop.c] re-order: group ssh1 vs. ssh2 - Make Redhat spec require openssl >= 0.9.5a
Diffstat (limited to 'sshconnect.c')
-rw-r--r--sshconnect.c350
1 files changed, 333 insertions, 17 deletions
diff --git a/sshconnect.c b/sshconnect.c
index d64c0e2c..2f949609 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -5,27 +5,30 @@
* Created: Sat Mar 18 22:15:47 1995 ylo
* Code to connect to a remote host, and to perform the client side of the
* login (authentication) dialog.
+ *
+ * SSH2 support added by Markus Friedl.
*/
#include "includes.h"
-RCSID("$OpenBSD: sshconnect.c,v 1.58 2000/03/23 22:15:33 markus Exp $");
+RCSID("$OpenBSD: sshconnect.c,v 1.61 2000/04/04 21:37:27 markus Exp $");
#ifdef HAVE_OPENSSL
+#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/md5.h>
-#include <openssl/bn.h>
#endif
#ifdef HAVE_SSL
+#include <ssl/bn.h>
#include <ssl/rsa.h>
#include <ssl/dsa.h>
#include <ssl/md5.h>
-#include <ssl/bn.h>
#endif
#include "xmalloc.h"
#include "rsa.h"
#include "ssh.h"
+#include "buffer.h"
#include "packet.h"
#include "authfd.h"
#include "cipher.h"
@@ -33,7 +36,14 @@ RCSID("$OpenBSD: sshconnect.c,v 1.58 2000/03/23 22:15:33 markus Exp $");
#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 "hostfile.h"
/* Session id for the current session. */
@@ -42,6 +52,9 @@ unsigned char session_id[16];
/* authentications supported by server */
unsigned int supported_authentications;
+static char *client_version_string = NULL;
+static char *server_version_string = NULL;
+
extern Options options;
extern char *__progname;
@@ -957,6 +970,21 @@ try_password_authentication(char *prompt)
return 0;
}
+char *
+chop(char *s)
+{
+ char *t = s;
+ while (*t) {
+ if(*t == '\n' || *t == '\r') {
+ *t = '\0';
+ return s;
+ }
+ t++;
+ }
+ return s;
+
+}
+
/*
* Waits for the server identification string, and sends our own
* identification string.
@@ -979,7 +1007,7 @@ ssh_exchange_identification()
if (buf[i] == '\r') {
buf[i] = '\n';
buf[i + 1] = 0;
- break;
+ continue; /**XXX wait for \n */
}
if (buf[i] == '\n') {
buf[i + 1] = 0;
@@ -987,17 +1015,21 @@ ssh_exchange_identification()
}
}
buf[sizeof(buf) - 1] = 0;
+ server_version_string = xstrdup(buf);
/*
* Check that the versions match. In future this might accept
* several versions and set appropriate flags to handle them.
*/
- if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor,
- remote_version) != 3)
+ if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
+ &remote_major, &remote_minor, remote_version) != 3)
fatal("Bad remote protocol version identification: '%.100s'", buf);
debug("Remote protocol version %d.%d, remote software version %.100s",
remote_major, remote_minor, remote_version);
+/*** XXX option for disabling 2.0 or 1.5 */
+ compat_datafellows(remote_version);
+
/* Check if the remote protocol version is too old. */
if (remote_major == 1 && remote_minor < 3)
fatal("Remote machine has too old SSH software version.");
@@ -1010,6 +1042,10 @@ ssh_exchange_identification()
options.forward_agent = 0;
}
}
+ if ((remote_major == 2 && remote_minor == 0) ||
+ (remote_major == 1 && remote_minor == 99)) {
+ enable_compat20();
+ }
#if 0
/*
* Removed for now, to permit compatibility with latter versions. The
@@ -1020,16 +1056,19 @@ ssh_exchange_identification()
fatal("Protocol major versions differ: %d vs. %d",
PROTOCOL_MAJOR, remote_major);
#endif
-
/* Send our own protocol version identification. */
snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
- PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
+ compat20 ? 2 : PROTOCOL_MAJOR,
+ compat20 ? 0 : PROTOCOL_MINOR,
+ SSH_VERSION);
if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf))
fatal("write: %.100s", strerror(errno));
+ client_version_string = xstrdup(buf);
+ chop(client_version_string);
+ chop(server_version_string);
+ debug("Local version string %.100s", client_version_string);
}
-int ssh_cipher_default = SSH_CIPHER_3DES;
-
int
read_yes_or_no(const char *prompt, int defval)
{
@@ -1283,6 +1322,278 @@ check_rsa_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
}
/*
+ * SSH2 key exchange
+ */
+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.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] = cipher_name(options.cipher);
+ 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);
+
+ 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 = new_dh_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_serverkey_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);
+
+ /* 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);
+
+ 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
+ );
+ 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
+ dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20);
+ key_free(server_host_key);
+
+ kex_derive_keys(kex, hash, shared_secret);
+ packet_set_kex(kex);
+
+ /* have keys, free DH */
+ DH_free(dh);
+
+ debug("Wait SSH2_MSG_NEWKEYS.");
+ packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS);
+ 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.");
+
+ /* send 1st encrypted/maced/compressed message */
+ packet_start(SSH2_MSG_IGNORE);
+ packet_put_cstring("markus");
+ packet_send();
+ packet_write_wait();
+
+ debug("done: KEX2.");
+}
+/*
+ * Authenticate user
+ */
+void
+ssh_userauth2(int host_key_valid, RSA *own_host_key,
+ uid_t original_real_uid, char *host)
+{
+ int type;
+ int plen;
+ unsigned int dlen;
+ int partial;
+ struct passwd *pw;
+ char *server_user, *local_user;
+ char *auths;
+ char *password;
+ 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);
+ }
+ /* payload empty for ssh-2.0.13 ?? */
+ /* reply = packet_get_string(&payload_len); */
+ debug("got SSH2_MSG_SERVICE_ACCEPT");
+
+ /*XX COMMONCODE: */
+ /* Get local user name. Use it as server user if no user name was given. */
+ pw = getpwuid(original_real_uid);
+ if (!pw)
+ fatal("User id %d not found from user database.", original_real_uid);
+ local_user = xstrdup(pw->pw_name);
+ server_user = options.user ? options.user : local_user;
+
+ /* 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 (;;) {
+ 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();
+ if (partial)
+ debug("partial success");
+ if (strstr(auths, "password") == NULL)
+ fatal("passwd auth not supported: %s", auths);
+ xfree(auths);
+ /* try passwd */
+ password = read_passphrase("password: ", 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();
+ }
+ debug("ssh-userauth2 successfull");
+}
+
+/*
* SSH1 key exchange
*/
void
@@ -1293,6 +1604,7 @@ ssh_kex(char *host, struct sockaddr *hostaddr)
RSA *host_key;
RSA *public_key;
int bits, rbits;
+ int ssh_cipher_default = SSH_CIPHER_3DES;
unsigned char session_key[SSH_SESSION_KEY_LENGTH];
unsigned char cookie[8];
unsigned int supported_ciphers;
@@ -1427,7 +1739,7 @@ ssh_kex(char *host, struct sockaddr *hostaddr)
RSA_free(host_key);
if (options.cipher == SSH_CIPHER_NOT_SET) {
- if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default))
+ if (cipher_mask1() & supported_ciphers & (1 << ssh_cipher_default))
options.cipher = ssh_cipher_default;
else {
debug("Cipher %s not supported, using %.100s instead.",
@@ -1640,12 +1952,16 @@ ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost,
/* Put the connection into non-blocking mode. */
packet_set_nonblocking();
- supported_authentications = 0;
/* key exchange */
- ssh_kex(host, hostaddr);
- if (supported_authentications == 0)
- fatal("supported_authentications == 0.");
-
/* authenticate user */
- ssh_userauth(host_key_valid, own_host_key, original_real_uid, host);
+ if (compat20) {
+ ssh_kex2(host, hostaddr);
+ ssh_userauth2(host_key_valid, own_host_key, original_real_uid, host);
+ } else {
+ supported_authentications = 0;
+ ssh_kex(host, hostaddr);
+ if (supported_authentications == 0)
+ fatal("supported_authentications == 0.");
+ ssh_userauth(host_key_valid, own_host_key, original_real_uid, host);
+ }
}