From 261f1ff9da68bde686a95aff6a125eadd2f70e14 Mon Sep 17 00:00:00 2001 From: markus Date: Tue, 4 Apr 2000 15:19:42 +0000 Subject: [PATCH] ssh2 client implementation, interops w/ ssh.com and lsh servers. --- usr.bin/ssh/clientloop.c | 166 +++++++++++++++---- usr.bin/ssh/compat.c | 6 +- usr.bin/ssh/myproposal.h | 20 +++ usr.bin/ssh/ssh.c | 162 ++++++++++++++++-- usr.bin/ssh/sshconnect.c | 345 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 633 insertions(+), 66 deletions(-) create mode 100644 usr.bin/ssh/myproposal.h diff --git a/usr.bin/ssh/clientloop.c b/usr.bin/ssh/clientloop.c index a5bfa0a5b49..744f63bebfa 100644 --- a/usr.bin/ssh/clientloop.c +++ b/usr.bin/ssh/clientloop.c @@ -12,10 +12,11 @@ * * The main loop for the interactive session (client side). * + * SSH2 support added by Markus Friedl. */ #include "includes.h" -RCSID("$Id: clientloop.c,v 1.15 2000/03/28 20:31:26 markus Exp $"); +RCSID("$Id: clientloop.c,v 1.16 2000/04/04 15:19:42 markus Exp $"); #include "xmalloc.h" #include "ssh.h" @@ -24,6 +25,7 @@ RCSID("$Id: clientloop.c,v 1.15 2000/03/28 20:31:26 markus Exp $"); #include "authfd.h" #include "readconf.h" +#include "ssh2.h" #include "compat.h" #include "channels.h" #include "dispatch.h" @@ -75,6 +77,10 @@ static unsigned long stdin_bytes, stdout_bytes, stderr_bytes; static int quit_pending; /* Set to non-zero to quit the client loop. */ static int escape_char; /* Escape character. */ + +void client_init_dispatch(void); +int session_ident = -1; + /* Returns the user\'s terminal to normal mode if it had been put in raw mode. */ void @@ -273,23 +279,32 @@ client_make_packets_from_stdin_data() void client_check_window_change() { - /* Send possible window change message to the server. */ - if (received_window_change_signal) { - struct winsize ws; - - /* Clear the window change indicator. */ - received_window_change_signal = 0; - - /* Read new window size. */ - if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) { - /* Successful, send the packet now. */ - packet_start(SSH_CMSG_WINDOW_SIZE); - packet_put_int(ws.ws_row); - packet_put_int(ws.ws_col); - packet_put_int(ws.ws_xpixel); - packet_put_int(ws.ws_ypixel); - packet_send(); - } + struct winsize ws; + + if (! received_window_change_signal) + return; + /** XXX race */ + received_window_change_signal = 0; + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) + return; + + debug("client_check_window_change: changed"); + + if (compat20) { + channel_request_start(session_ident, "window-change", 0); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_send(); + } else { + packet_start(SSH_CMSG_WINDOW_SIZE); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_send(); } } @@ -301,21 +316,28 @@ client_check_window_change() void client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) { + /*debug("client_wait_until_can_do_something"); */ + /* Initialize select masks. */ FD_ZERO(readset); /* Read from the connection, unless our buffers are full. */ - if (buffer_len(&stdout_buffer) < buffer_high && - buffer_len(&stderr_buffer) < buffer_high && - channel_not_very_much_buffered_data()) + if (!compat20) { + if (buffer_len(&stdout_buffer) < buffer_high && + buffer_len(&stderr_buffer) < buffer_high && + channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + } else { FD_SET(connection_in, readset); + } /* * Read from stdin, unless we have seen EOF or have very much * buffered data to send to the server. */ - if (!stdin_eof && packet_not_very_much_data_to_write()) - FD_SET(fileno(stdin), readset); + if (!compat20) + if (!stdin_eof && packet_not_very_much_data_to_write()) + FD_SET(fileno(stdin), readset); FD_ZERO(writeset); @@ -326,14 +348,17 @@ client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) if (packet_have_data_to_write()) FD_SET(connection_out, writeset); - /* Select stdout if have data in buffer. */ - if (buffer_len(&stdout_buffer) > 0) - FD_SET(fileno(stdout), writeset); + if (!compat20) { + /* Select stdout if have data in buffer. */ + if (buffer_len(&stdout_buffer) > 0) + FD_SET(fileno(stdout), writeset); - /* Select stderr if have data in buffer. */ - if (buffer_len(&stderr_buffer) > 0) - FD_SET(fileno(stderr), writeset); + /* Select stderr if have data in buffer. */ + if (buffer_len(&stderr_buffer) > 0) + FD_SET(fileno(stderr), writeset); + } +/* move UP XXX */ /* Update maximum file descriptor number, if appropriate. */ if (channel_max_fd() > max_fd) max_fd = channel_max_fd(); @@ -420,6 +445,7 @@ client_process_input(fd_set * readset) if (FD_ISSET(connection_in, readset)) { /* Read as much as possible. */ len = read(connection_in, buf, sizeof(buf)); +/*debug("read connection_in len %d", len); XXX */ if (len == 0) { /* Received EOF. The remote host has closed the connection. */ snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", @@ -447,6 +473,10 @@ client_process_input(fd_set * readset) } packet_process_incoming(buf, len); } + + if (compat20) + return; + /* Read input from stdin. */ if (FD_ISSET(fileno(stdin), readset)) { /* Read as much as possible. */ @@ -703,8 +733,6 @@ client_process_buffered_input_packets() * character for terminating or suspending the session. */ -void client_init_dispatch(void); - int client_loop(int have_pty, int escape_char_arg) { @@ -753,7 +781,8 @@ client_loop(int have_pty, int escape_char_arg) enter_raw_mode(); /* Check if we should immediately send of on stdin. */ - client_check_initial_eof_on_stdin(); + if(!compat20) + client_check_initial_eof_on_stdin(); /* Main loop of the client for the interactive session mode. */ while (!quit_pending) { @@ -762,11 +791,17 @@ client_loop(int have_pty, int escape_char_arg) /* Process buffered packets sent by the server. */ client_process_buffered_input_packets(); + if (compat20 && !channel_still_open()) { + debug("!channel_still_open."); + break; + } + /* * Make packets of buffered stdin data, and buffer them for * sending to the server. */ - client_make_packets_from_stdin_data(); + if(!compat20) + client_make_packets_from_stdin_data(); /* * Make packets from buffered channel data, and buffer them @@ -806,7 +841,8 @@ client_loop(int have_pty, int escape_char_arg) * Process output to stdout and stderr. Output to the * connection is processed elsewhere (above). */ - client_process_output(&writeset); + if(!compat20) + client_process_output(&writeset); /* Send as much buffered packet data as possible to the sender. */ if (FD_ISSET(connection_out, &writeset)) @@ -917,6 +953,19 @@ client_input_exit_status(int type, int plen) quit_pending = 1; } +void +client_init_dispatch_20() +{ + dispatch_init(&dispatch_protocol_error); + dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); + dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); + dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); + dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); +} void client_init_dispatch_13() { @@ -944,8 +993,55 @@ client_init_dispatch_15() void client_init_dispatch() { - if (compat13) + if (compat20) + client_init_dispatch_20(); + else if (compat13) client_init_dispatch_13(); else client_init_dispatch_15(); } + +void +client_input_channel_req(int id, void *arg) +{ + Channel *c = NULL; + unsigned int len; + int success = 0; + int reply; + char *rtype; + + rtype = packet_get_string(&len); + reply = packet_get_char(); + + log("session_input_channel_req: rtype %s reply %d", rtype, reply); + + c = channel_lookup(id); + if (c == NULL) + fatal("session_input_channel_req: channel %d: bad channel", id); + + if (session_ident == -1) { + error("client_input_channel_req: no channel %d", id); + } else if (id != session_ident) { + error("client_input_channel_req: bad channel %d != %d", + id, session_ident); + } else if (strcmp(rtype, "exit-status") == 0) { + success = 1; + exit_status = packet_get_int(); + } + if (reply) { + packet_start(success ? + SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); + packet_put_int(c->remote_id); + packet_send(); + } + xfree(rtype); +} + +void +client_set_session_ident(int id) +{ + debug("client_set_session_ident: id %d", id); + session_ident = id; + channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST, + client_input_channel_req, (void *)0); +} diff --git a/usr.bin/ssh/compat.c b/usr.bin/ssh/compat.c index a4579b46013..a9a3e755e28 100644 --- a/usr.bin/ssh/compat.c +++ b/usr.bin/ssh/compat.c @@ -28,7 +28,7 @@ */ #include "includes.h" -RCSID("$Id: compat.c,v 1.6 2000/04/03 07:07:15 markus Exp $"); +RCSID("$Id: compat.c,v 1.7 2000/04/04 15:19:42 markus Exp $"); #include "ssh.h" #include "packet.h" @@ -40,7 +40,9 @@ int datafellows = 0; void enable_compat20(void) { - fatal("protocol 2.0 not implemented"); + verbose("Enabling compatibility mode for protocol 2.0"); + compat20 = 1; + packet_set_ssh2_format(); } void enable_compat13(void) diff --git a/usr.bin/ssh/myproposal.h b/usr.bin/ssh/myproposal.h new file mode 100644 index 00000000000..7e4baff9dd0 --- /dev/null +++ b/usr.bin/ssh/myproposal.h @@ -0,0 +1,20 @@ +#define KEX_DEFAULT_KEX "diffie-hellman-group1-sha1" +#define KEX_DEFAULT_PK_ALG "ssh-dss" +#define KEX_DEFAULT_ENCRYPT "blowfish-cbc,3des-cbc,arcfour,cast128-cbc" +#define KEX_DEFAULT_MAC "hmac-sha1,hmac-md5,hmac-ripemd160@openssh.com" +#define KEX_DEFAULT_COMP "zlib,none" +#define KEX_DEFAULT_LANG "" + + +static const char *myproposal[PROPOSAL_MAX] = { + KEX_DEFAULT_KEX, + KEX_DEFAULT_PK_ALG, + KEX_DEFAULT_ENCRYPT, + KEX_DEFAULT_ENCRYPT, + KEX_DEFAULT_MAC, + KEX_DEFAULT_MAC, + KEX_DEFAULT_COMP, + KEX_DEFAULT_COMP, + KEX_DEFAULT_LANG, + KEX_DEFAULT_LANG +}; diff --git a/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c index bbfb9b50928..35dc45d2d1b 100644 --- a/usr.bin/ssh/ssh.c +++ b/usr.bin/ssh/ssh.c @@ -11,7 +11,7 @@ */ #include "includes.h" -RCSID("$Id: ssh.c,v 1.44 2000/03/28 20:31:28 markus Exp $"); +RCSID("$Id: ssh.c,v 1.45 2000/04/04 15:19:42 markus Exp $"); #include "xmalloc.h" #include "ssh.h" @@ -20,6 +20,9 @@ RCSID("$Id: ssh.c,v 1.44 2000/03/28 20:31:28 markus Exp $"); #include "authfd.h" #include "readconf.h" #include "uidswap.h" + +#include "ssh2.h" +#include "compat.h" #include "channels.h" /* Flag indicating whether IPv4 or IPv6. This can be set on the command line. @@ -31,6 +34,10 @@ int debug_flag = 0; int tty_flag = 0; +/* don't exec a shell */ +int no_shell_flag = 0; +int no_tty_flag = 0; + /* * Flag indicating that nothing should be read from stdin. This can be set * on the command line. @@ -80,6 +87,9 @@ RSA *host_private_key = NULL; /* Original real UID. */ uid_t original_real_uid; +/* command to be executed */ +Buffer command; + /* Prints a help message to the user. This function never returns. */ void @@ -94,9 +104,9 @@ usage() fprintf(stderr, " -k Disable Kerberos ticket and AFS token forwarding.\n"); #endif /* AFS */ fprintf(stderr, " -x Disable X11 connection forwarding.\n"); - fprintf(stderr, " -X Enable X11 connection forwarding.\n"); fprintf(stderr, " -i file Identity for RSA authentication (default: ~/.ssh/identity).\n"); fprintf(stderr, " -t Tty; allocate a tty even if command is given.\n"); + fprintf(stderr, " -T Do not allocate a tty.\n"); fprintf(stderr, " -v Verbose; display verbose debugging messages.\n"); fprintf(stderr, " -V Display version number only.\n"); fprintf(stderr, " -P Don't allocate a privileged port.\n"); @@ -113,6 +123,7 @@ usage() fprintf(stderr, " These cause %s to listen for connections on a port, and\n", av0); fprintf(stderr, " forward them to the other side by connecting to host:port.\n"); fprintf(stderr, " -C Enable compression.\n"); + fprintf(stderr, " -N Do not execute a shell or command.\n"); fprintf(stderr, " -g Allow remote hosts to connect to forwarded ports.\n"); fprintf(stderr, " -4 Use IPv4 only.\n"); fprintf(stderr, " -6 Use IPv6 only.\n"); @@ -158,23 +169,22 @@ rsh_connect(char *host, char *user, Buffer * command) exit(1); } +int ssh_session(void); +int ssh_session2(void); + /* * Main program for the ssh client. */ int main(int ac, char **av) { - int i, opt, optind, type, exit_status, ok, authfd; + int i, opt, optind, exit_status, ok; u_short fwd_port, fwd_host_port; char *optarg, *cp, buf[256]; - Buffer command; - struct winsize ws; struct stat st; struct passwd *pw, pwcopy; - int interactive = 0, dummy; - int have_pty = 0; + int dummy; uid_t original_effective_uid; - int plen; /* * Save the original real uid. It will be needed later (uid-swapping @@ -318,7 +328,7 @@ main(int ac, char **av) case 'V': fprintf(stderr, "SSH Version %s, protocol version %d.%d.\n", SSH_VERSION, PROTOCOL_MAJOR, PROTOCOL_MINOR); - fprintf(stderr, "Compiled with SSL.\n"); + fprintf(stderr, "Compiled with SSL (0x%8.8lx).\n", SSLeay()); if (opt == 'V') exit(0); debug_flag = 1; @@ -387,6 +397,15 @@ main(int ac, char **av) options.compression = 1; break; + case 'N': + no_shell_flag = 1; + no_tty_flag = 1; + break; + + case 'T': + no_tty_flag = 1; + break; + case 'o': dummy = 1; if (process_config_line(&options, host ? host : "", optarg, @@ -447,6 +466,10 @@ main(int ac, char **av) fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n"); tty_flag = 0; } + /* force */ + if (no_tty_flag) + tty_flag = 0; + /* Get user data. */ pw = getpwuid(original_real_uid); if (!pw) { @@ -612,6 +635,23 @@ main(int ac, char **av) if (host_private_key_loaded) RSA_free(host_private_key); /* Destroys contents safely */ + exit_status = compat20 ? ssh_session2() : ssh_session(); + packet_close(); + return exit_status; +} + +int +ssh_session(void) +{ + int type; + int i; + int plen; + int interactive = 0; + int have_tty = 0; + struct winsize ws; + int authfd; + char *cp; + /* Enable compression if requested. */ if (options.compression) { debug("Requesting compression at level %d.", options.compression_level); @@ -665,7 +705,7 @@ main(int ac, char **av) type = packet_read(&plen); if (type == SSH_SMSG_SUCCESS) { interactive = 1; - have_pty = 1; + have_tty = 1; } else if (type == SSH_SMSG_FAILURE) log("Warning: Remote host failed or refused to allocate a pseudo tty."); else @@ -794,11 +834,103 @@ main(int ac, char **av) } /* Enter the interactive session. */ - exit_status = client_loop(have_pty, tty_flag ? options.escape_char : -1); + return client_loop(have_tty, tty_flag ? options.escape_char : -1); +} - /* Close the connection to the remote host. */ - packet_close(); +void +init_local_fwd(void) +{ + int i; + /* Initiate local TCP/IP port forwardings. */ + for (i = 0; i < options.num_local_forwards; i++) { + debug("Connections to local port %d forwarded to remote address %.200s:%d", + options.local_forwards[i].port, + options.local_forwards[i].host, + options.local_forwards[i].host_port); + channel_request_local_forwarding(options.local_forwards[i].port, + options.local_forwards[i].host, + options.local_forwards[i].host_port, + options.gateway_ports); + } +} + +extern void client_set_session_ident(int id); + +void +client_init(int id, void *arg) +{ + int len; + debug("client_init id %d arg %d", id, (int)arg); + + if (no_shell_flag) + goto done; + + if (tty_flag) { + struct winsize ws; + char *cp; + cp = getenv("TERM"); + if (!cp) + cp = ""; + /* Store window size in the packet. */ + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) + memset(&ws, 0, sizeof(ws)); + + channel_request_start(id, "pty-req", 0); + packet_put_cstring(cp); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_put_cstring(""); /* XXX: encode terminal modes */ + packet_send(); + /* XXX wait for reply */ + } + len = buffer_len(&command); + if (len > 0) { + if (len > 900) + len = 900; + debug("Sending command: %.*s", len, buffer_ptr(&command)); + channel_request_start(id, "exec", 0); + packet_put_string(buffer_ptr(&command), len); + packet_send(); + } else { + channel_request(id, "shell", 0); + } + /* channel_callback(id, SSH2_MSG_OPEN_CONFIGMATION, client_init, 0); */ +done: + /* register different callback, etc. XXX */ + client_set_session_ident(id); +} + +int +ssh_session2(void) +{ + int window, packetmax, id; + int in = dup(STDIN_FILENO); + int out = dup(STDOUT_FILENO); + int err = dup(STDERR_FILENO); + + if (in < 0 || out < 0 || err < 0) + fatal("dump in/out/err failed"); + + /* should be pre-session */ + init_local_fwd(); + + window = 32*1024; + if (tty_flag) { + packetmax = window/8; + } else { + window *= 2; + packetmax = window/2; + } + + id = channel_new( + "session", SSH_CHANNEL_OPENING, in, out, err, + window, packetmax, CHAN_EXTENDED_WRITE, xstrdup("client-session")); + + + channel_open(id); + channel_register_callback(id, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, client_init, (void *)0); - /* Exit with the status returned by the program on the remote side. */ - exit(exit_status); + return client_loop(tty_flag, tty_flag ? options.escape_char : -1); } diff --git a/usr.bin/ssh/sshconnect.c b/usr.bin/ssh/sshconnect.c index 07277986949..3999d95ea86 100644 --- a/usr.bin/ssh/sshconnect.c +++ b/usr.bin/ssh/sshconnect.c @@ -5,15 +5,18 @@ * 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.59 2000/04/04 15:19:43 markus Exp $"); #include #include "xmalloc.h" #include "rsa.h" #include "ssh.h" +#include "buffer.h" #include "packet.h" #include "authfd.h" #include "cipher.h" @@ -22,10 +25,18 @@ RCSID("$OpenBSD: sshconnect.c,v 1.58 2000/03/23 22:15:33 markus Exp $"); #include "compat.h" #include "readconf.h" +#include "bufaux.h" #include #include + +#include "ssh2.h" #include +#include +#include +#include "kex.h" +#include "myproposal.h" #include "key.h" +#include "dsa.h" #include "hostfile.h" /* Session id for the current session. */ @@ -34,6 +45,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; @@ -949,6 +963,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. @@ -971,7 +1000,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; @@ -979,17 +1008,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."); @@ -1002,6 +1035,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 @@ -1012,16 +1049,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) { @@ -1270,6 +1310,278 @@ check_rsa_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key) check_host_key(host, hostaddr, &k); } +/* + * 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_clear(client_kexinit); + buffer_clear(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 */ @@ -1281,6 +1593,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; @@ -1628,12 +1941,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); + } } -- 2.20.1