ssh2 server side, see README.openssh2; enable with 'sshd -2'
authormarkus <markus@openbsd.org>
Thu, 6 Apr 2000 08:55:22 +0000 (08:55 +0000)
committermarkus <markus@openbsd.org>
Thu, 6 Apr 2000 08:55:22 +0000 (08:55 +0000)
usr.bin/ssh/README.openssh2 [new file with mode: 0644]
usr.bin/ssh/auth.c
usr.bin/ssh/auth.h
usr.bin/ssh/servconf.c
usr.bin/ssh/servconf.h
usr.bin/ssh/serverloop.c
usr.bin/ssh/session.c
usr.bin/ssh/session.h
usr.bin/ssh/ssh.h
usr.bin/ssh/sshd.c

diff --git a/usr.bin/ssh/README.openssh2 b/usr.bin/ssh/README.openssh2
new file mode 100644 (file)
index 0000000..7413baf
--- /dev/null
@@ -0,0 +1,35 @@
+$Id: README.openssh2,v 1.1 2000/04/06 08:55:22 markus Exp $
+
+works:
+       secsh-transport: works w/o rekey
+               proposal exchange, i.e. different enc/mac/comp per direction
+               encryption: blowfish-cbc, 3des-cbc, arcfour, cast128-cbc
+               mac: hmac-md5, hmac-sha1, (hmac-ripemd160)
+               compression: zlib, none
+       secsh-userauth: passwd only
+       secsh-connection: pty+shell or command, flow control works (window adjust)
+               tcp-forwarding: -L works
+       dss: verification works,
+               key database in ~/.ssh/known_hosts with bits == 0 hack
+       dss: signature works, keygen w/ openssl:
+               $ openssl dsaparam 1024 -out dsa1024.pem
+               $ openssl gendsa -out /etc/ssh_dsa_key dsa1024.pem -rand /dev/arandom
+               start sshd with '-2' flag
+       client interops w/ sshd2, lshd
+       server interops w/ ssh2, lsh, ssh.com's Windows client, SecureCRT
+       server supports multiple concurrent sessions (e.g. with SSH.com Windows client)
+todo:
+       re-keying
+       secsh-connection features:
+                tcp-forwarding, agent-fwd, x11-fwd
+       auth other than passwd:
+                pubkey, keyboard-interactive
+       config
+       server-auth w/ old host-keys
+       cleanup
+       advanced key storage?
+       keynote
+       sftp
+
+-markus
+$Date: 2000/04/06 08:55:22 $
index 6fc799e..f33790d 100644 (file)
@@ -1,10 +1,11 @@
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: auth.c,v 1.1 2000/03/28 21:15:45 markus Exp $");
+RCSID("$OpenBSD: auth.c,v 1.2 2000/04/06 08:55:22 markus Exp $");
 
 #include "xmalloc.h"
 #include "rsa.h"
@@ -15,12 +16,17 @@ RCSID("$OpenBSD: auth.c,v 1.1 2000/03/28 21:15:45 markus Exp $");
 #include "cipher.h"
 #include "mpaux.h"
 #include "servconf.h"
+#include "compat.h"
 #include "channels.h"
 #include "match.h"
 
+#include "bufaux.h"
+#include "ssh2.h"
+#include "auth.h"
 #include "session.h"
 #include "dispatch.h"
 
+
 /* import */
 extern ServerOptions options;
 extern char *forced_command;
@@ -552,3 +558,168 @@ do_authentication()
        /* Perform session preparation. */
        do_authenticated(pw);
 }
+
+
+void input_service_request(int type, int plen);
+void input_userauth_request(int type, int plen);
+void ssh2_pty_cleanup(void);
+
+typedef struct Authctxt Authctxt;
+struct Authctxt {
+       char *user;
+       char *service;
+       struct passwd pw;
+       int valid;
+};
+static Authctxt        *authctxt = NULL;
+static int userauth_success = 0;
+
+struct passwd*
+auth_get_user(void)
+{
+       return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL;
+}
+struct passwd*
+auth_set_user(char *u, char *s)
+{
+       struct passwd *pw, *copy;
+
+       if (authctxt == NULL) {
+               authctxt = xmalloc(sizeof(*authctxt));
+               authctxt->valid = 0;
+               authctxt->user = xstrdup(u);
+               authctxt->service = xstrdup(s);
+               setproctitle("%s", u);
+               pw = getpwnam(u);
+               if (!pw || !allowed_user(pw)) {
+                       log("auth_set_user: bad user %s", u);
+                       return NULL;
+               }
+               copy = &authctxt->pw;
+               memset(copy, 0, sizeof(*copy));
+               copy->pw_name = xstrdup(pw->pw_name);
+               copy->pw_passwd = xstrdup(pw->pw_passwd);
+               copy->pw_uid = pw->pw_uid;
+               copy->pw_gid = pw->pw_gid;
+               copy->pw_dir = xstrdup(pw->pw_dir);
+               copy->pw_shell = xstrdup(pw->pw_shell);
+               authctxt->valid = 1;
+       } else {
+               if (strcmp(u, authctxt->user) != 0 ||
+                   strcmp(s, authctxt->service) != 0) {
+                       log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)",
+                           u, s, authctxt->user, authctxt->service);
+                       return NULL;
+               }
+       }
+       return auth_get_user();
+}
+
+static void
+protocol_error(int type, int plen)
+{
+       log("auth: protocol error: type %d plen %d", type, plen);
+       packet_start(SSH2_MSG_UNIMPLEMENTED);
+       packet_put_int(0);
+       packet_send();
+       packet_write_wait();
+}
+void
+input_service_request(int type, int plen)
+{
+       unsigned int len;
+       int accept = 0;
+       char *service = packet_get_string(&len);
+
+       if (strcmp(service, "ssh-userauth") == 0) {
+               if (!userauth_success) {
+                       accept = 1;
+                       /* now we can handle user-auth requests */
+                       dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
+               }
+       }
+       /* XXX all other service requests are denied */
+
+       if (accept) {
+               packet_start(SSH2_MSG_SERVICE_ACCEPT);
+               packet_put_cstring(service);
+               packet_send();
+               packet_write_wait();
+       } else {
+               debug("bad service request %s", service);
+               packet_disconnect("bad service request %s", service);
+       }
+       xfree(service);
+}
+void
+input_userauth_request(int type, int plen)
+{
+       static int try = 0;
+       unsigned int len;
+       int c, authenticated = 0;
+       char *user, *service, *method;
+       struct passwd *pw;
+
+       if (++try == AUTH_FAIL_MAX)
+               packet_disconnect("too many failed userauth_requests");
+
+       user = packet_get_string(&len);
+       service = packet_get_string(&len);
+       method = packet_get_string(&len);
+       debug("userauth-request for user %s service %s method %s", user, service, method);
+
+       /* XXX we only allow the ssh-connection service */
+       pw = auth_set_user(user, service);
+       if (pw && strcmp(service, "ssh-connection")==0) {
+               if (strcmp(method, "none") == 0 && try == 1) {
+                       authenticated = auth_password(pw, "");
+               } else if (strcmp(method, "password") == 0) {
+                       char *password;
+                       c = packet_get_char();
+                       if (c)
+                               debug("password change not supported");
+                       password = packet_get_string(&len);
+                       authenticated = auth_password(pw, password);
+                       memset(password, 0, len);
+                       xfree(password);
+               } else if (strcmp(method, "publickey") == 0) {
+                       /* XXX TODO */
+                       char *pkalg;
+                       char *pkblob;
+                       c = packet_get_char();
+                       pkalg = packet_get_string(&len);
+                       pkblob = packet_get_string(&len);
+                       xfree(pkalg);
+                       xfree(pkblob);
+               }
+       }
+       /* XXX check if other auth methods are needed */
+       if (authenticated) {
+               /* turn off userauth */
+               dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
+               /* success! */
+               packet_start(SSH2_MSG_USERAUTH_SUCCESS);
+               packet_send();
+               packet_write_wait();
+               log("userauth success for %s", user);
+               /* now we can break out */
+               userauth_success = 1;
+       } else {
+               packet_start(SSH2_MSG_USERAUTH_FAILURE);
+               packet_put_cstring("password");
+               packet_put_char(0);             /* partial success */
+               packet_send();
+               packet_write_wait();
+       }
+       xfree(service);
+       xfree(user);
+       xfree(method);
+}
+void 
+do_authentication2()
+{
+       dispatch_init(&protocol_error);
+       dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
+       dispatch_run(DISPATCH_BLOCK, &userauth_success);
+       do_authenticated2();
+}
index 8051777..3771e82 100644 (file)
@@ -2,5 +2,9 @@
 #define AUTH_H
 
 void   do_authentication(void);
+void   do_authentication2(void);
+
+struct passwd *
+auth_get_user(void);
 
 #endif
index 15b326f..14d351c 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: servconf.c,v 1.31 2000/03/07 20:40:41 markus Exp $");
+RCSID("$Id: servconf.c,v 1.32 2000/04/06 08:55:22 markus Exp $");
 
 #include "ssh.h"
 #include "servconf.h"
@@ -31,6 +31,7 @@ initialize_server_options(ServerOptions *options)
        options->ports_from_cmdline = 0;
        options->listen_addrs = NULL;
        options->host_key_file = NULL;
+       options->dsa_key_file = NULL;
        options->server_key_bits = -1;
        options->login_grace_time = -1;
        options->key_regeneration_time = -1;
@@ -78,6 +79,8 @@ fill_default_server_options(ServerOptions *options)
                add_listen_addr(options, NULL);
        if (options->host_key_file == NULL)
                options->host_key_file = HOST_KEY_FILE;
+       if (options->dsa_key_file == NULL)
+               options->dsa_key_file = DSA_KEY_FILE;
        if (options->server_key_bits == -1)
                options->server_key_bits = 768;
        if (options->login_grace_time == -1)
@@ -159,7 +162,7 @@ typedef enum {
        sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset,
        sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail,
        sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
-       sIgnoreUserKnownHosts
+       sIgnoreUserKnownHosts, sDSAKeyFile
 } ServerOpCodes;
 
 /* Textual representation of the tokens. */
@@ -169,6 +172,7 @@ static struct {
 } keywords[] = {
        { "port", sPort },
        { "hostkey", sHostKeyFile },
+       { "dsakey", sDSAKeyFile },
        { "serverkeybits", sServerKeyBits },
        { "logingracetime", sLoginGraceTime },
        { "keyregenerationinterval", sKeyRegenerationTime },
@@ -338,7 +342,9 @@ parse_int:
                        break;
 
                case sHostKeyFile:
-                       charptr = &options->host_key_file;
+               case sDSAKeyFile:
+                       charptr = (opcode == sHostKeyFile ) ?
+                           &options->host_key_file : &options->dsa_key_file;
                        cp = strtok(NULL, WHITESPACE);
                        if (!cp) {
                                fprintf(stderr, "%s line %d: missing file name.\n",
index dd4caf7..6b6ea9f 100644 (file)
@@ -13,7 +13,7 @@
  * 
  */
 
-/* RCSID("$Id: servconf.h,v 1.15 2000/01/04 00:08:00 markus Exp $"); */
+/* RCSID("$Id: servconf.h,v 1.16 2000/04/06 08:55:22 markus Exp $"); */
 
 #ifndef SERVCONF_H
 #define SERVCONF_H
@@ -32,6 +32,7 @@ typedef struct {
        char   *listen_addr;            /* Address on which the server listens. */
        struct addrinfo *listen_addrs;  /* Addresses on which the server listens. */
        char   *host_key_file;  /* File containing host key. */
+       char   *dsa_key_file;   /* File containing dsa host key. */
        int     server_key_bits;/* Size of the server key. */
        int     login_grace_time;       /* Disconnect if no auth in this time
                                         * (sec). */
index fb3903c..4cbb3ba 100644 (file)
@@ -5,6 +5,10 @@
  * Created: Sun Sep 10 00:30:37 1995 ylo
  * Server main loop for handling the interactive session.
  */
+/*
+ * SSH2 support by Markus Friedl.
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ */
 
 #include "includes.h"
 #include "xmalloc.h"
@@ -16,6 +20,8 @@
 #include "channels.h"
 
 #include "compat.h"
+#include "ssh2.h"
+#include "session.h"
 #include "dispatch.h"
 
 static Buffer stdin_buffer;    /* Buffer for stdin data. */
@@ -66,6 +72,15 @@ sigchld_handler(int sig)
        signal(SIGCHLD, sigchld_handler);
        errno = save_errno;
 }
+void 
+sigchld_handler2(int sig)
+{
+       int save_errno = errno;
+       debug("Received SIGCHLD.");
+       child_terminated = 1;
+       signal(SIGCHLD, sigchld_handler2);
+       errno = save_errno;
+}
 
 /*
  * Make packets from buffered stderr data, and buffer it for sending
@@ -148,15 +163,21 @@ retry_select:
         * Read packets from the client unless we have too much buffered
         * stdin or channel data.
         */
-       if (buffer_len(&stdin_buffer) < 4096 &&
-           channel_not_very_much_buffered_data())
-               FD_SET(connection_in, readset);
+       if (compat20) {
+               // wrong: bad conditionXXX
+               if (channel_not_very_much_buffered_data())
+                       FD_SET(connection_in, readset);
+       } else {
+               if (buffer_len(&stdin_buffer) < 4096 &&
+                   channel_not_very_much_buffered_data())
+                       FD_SET(connection_in, readset);
+       }
 
        /*
         * If there is not too much data already buffered going to the
         * client, try to get some more data from the program.
         */
-       if (packet_not_very_much_data_to_write()) {
+       if (!compat20 && packet_not_very_much_data_to_write()) {
                if (!fdout_eof)
                        FD_SET(fdout, readset);
                if (!fderr_eof)
@@ -176,7 +197,7 @@ retry_select:
 
        /* If we have buffered data, try to write some of that data to the
           program. */
-       if (fdin != -1 && buffer_len(&stdin_buffer) > 0)
+       if (!compat20 && fdin != -1 && buffer_len(&stdin_buffer) > 0)
                FD_SET(fdin, writeset);
 
        /* Update the maximum descriptor number if appropriate. */
@@ -198,6 +219,8 @@ retry_select:
                tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
                tvp = &tv;
        }
+       if (tvp!=NULL)
+               debug("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds);
 
        /* Wait for something to happen, or the timeout to expire. */
        ret = select(max_fd + 1, readset, writeset, NULL, tvp);
@@ -241,6 +264,9 @@ process_input(fd_set * readset)
                /* Buffer any received data. */
                packet_process_incoming(buf, len);
        }
+       if (compat20)
+               return;
+
        /* Read and buffer any available stdout data from the program. */
        if (!fdout_eof && FD_ISSET(fdout, readset)) {
                len = read(fdout, buf, sizeof(buf));
@@ -270,7 +296,7 @@ process_output(fd_set * writeset)
        int len;
 
        /* Write buffered data to program stdin. */
-       if (fdin != -1 && FD_ISSET(fdin, writeset)) {
+       if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) {
                len = write(fdin, buffer_ptr(&stdin_buffer),
                    buffer_len(&stdin_buffer));
                if (len <= 0) {
@@ -565,6 +591,51 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg)
        /* NOTREACHED */
 }
 
+void 
+server_loop2(void)
+{
+       fd_set readset, writeset;
+       int had_channel = 0;
+       int status;
+       pid_t pid;
+
+       debug("Entering interactive session for SSH2.");
+
+       signal(SIGCHLD, sigchld_handler2);
+       child_terminated = 0;
+       connection_in = packet_get_connection_in();
+       connection_out = packet_get_connection_out();
+       max_fd = connection_in;
+       if (connection_out > max_fd)
+               max_fd = connection_out;
+       server_init_dispatch();
+
+       for (;;) {
+               process_buffered_input_packets();
+               if (!had_channel && channel_still_open())
+                       had_channel = 1;
+               if (had_channel && !channel_still_open()) {
+                       debug("!channel_still_open.");
+                       break;
+               }
+               if (packet_not_very_much_data_to_write())
+                       channel_output_poll();
+               wait_until_can_do_something(&readset, &writeset, 0);
+               if (child_terminated) {
+                       while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+                               session_close_by_pid(pid, status);
+                       child_terminated = 0;
+               }
+               channel_after_select(&readset, &writeset);
+               process_input(&readset);
+               process_output(&writeset);
+       }
+       signal(SIGCHLD, SIG_DFL);
+       while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+               session_close_by_pid(pid, status);
+       channel_stop_listening();
+}
+
 void
 server_input_stdin_data(int type, int plen)
 {
@@ -609,6 +680,111 @@ server_input_window_size(int type, int plen)
                pty_change_window_size(fdin, row, col, xpixel, ypixel);
 }
 
+int
+input_direct_tcpip(void)
+{
+       int sock;
+       char *host, *originator;
+       int host_port, originator_port;
+
+       host = packet_get_string(NULL);
+       host_port = packet_get_int();
+       originator = packet_get_string(NULL);
+       originator_port = packet_get_int();
+       /* XXX check permission */
+       sock = channel_connect_to(host, host_port);
+       xfree(host);
+       xfree(originator);
+       if (sock < 0)
+               return -1;
+       return channel_new("direct-tcpip", SSH_CHANNEL_OPEN,
+           sock, sock, -1, 4*1024, 32*1024, 0, xstrdup("direct-tcpip"));
+}
+
+void 
+server_input_channel_open(int type, int plen)
+{
+       Channel *c = NULL;
+       char *ctype;
+       int id;
+       unsigned int len;
+       int rchan;
+       int rmaxpack;
+       int rwindow;
+
+       ctype = packet_get_string(&len);
+       rchan = packet_get_int();
+       rwindow = packet_get_int();
+       rmaxpack = packet_get_int();
+
+       log("channel_input_open: ctype %s rchan %d win %d max %d",
+           ctype, rchan, rwindow, rmaxpack);
+
+       if (strcmp(ctype, "session") == 0) {
+               debug("open session");
+               /*
+                * A server session has no fd to read or write
+                * until a CHANNEL_REQUEST for a shell is made,
+                * so we set the type to SSH_CHANNEL_LARVAL.
+                * Additionally, a callback for handling all
+                * CHANNEL_REQUEST messages is registered.
+                */
+               id = channel_new(ctype, SSH_CHANNEL_LARVAL,
+                   -1, -1, -1, 0, 32*1024, 0, xstrdup("server-session"));
+               if (session_open(id) == 1) {
+                       channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST,
+                           session_input_channel_req, (void *)0);
+                       channel_register_cleanup(id, session_close_by_channel);
+                       c = channel_lookup(id);
+               } else {
+                       debug("session open failed, free channel %d", id);
+                       channel_free(id);
+               }
+       } else if (strcmp(ctype, "direct-tcpip") == 0) {
+               debug("open direct-tcpip");
+               id = input_direct_tcpip();
+               if (id >= 0)
+                       c = channel_lookup(id);
+       }
+       if (c != NULL) {
+               debug("confirm %s", ctype);
+               c->remote_id = rchan;
+               c->remote_window = rwindow;
+               c->remote_maxpacket = rmaxpack;
+
+               packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
+               packet_put_int(c->remote_id);
+               packet_put_int(c->self);
+               packet_put_int(c->local_window);
+               packet_put_int(c->local_maxpacket);
+               packet_send();
+       } else {
+               debug("failure %s", ctype);
+               packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
+               packet_put_int(rchan);
+               packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
+               packet_put_cstring("bla bla");
+               packet_put_cstring("");
+               packet_send();
+       }
+       xfree(ctype);
+}
+
+void 
+server_init_dispatch_20()
+{
+       debug("server_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, &server_input_channel_open);
+       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 
 server_init_dispatch_13()
 {
@@ -635,7 +811,9 @@ server_init_dispatch_15()
 void 
 server_init_dispatch()
 {
-       if (compat13)
+       if (compat20)
+               server_init_dispatch_20();
+       else if (compat13)
                server_init_dispatch_13();
        else
                server_init_dispatch_15();
index 2e486a3..c98e65e 100644 (file)
@@ -2,9 +2,13 @@
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
  */
+/*
+ * SSH2 support by Markus Friedl.
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ */
 
 #include "includes.h"
-RCSID("$OpenBSD: session.c,v 1.1 2000/03/28 21:15:45 markus Exp $");
+RCSID("$OpenBSD: session.c,v 1.2 2000/04/06 08:55:22 markus Exp $");
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -19,6 +23,10 @@ RCSID("$OpenBSD: session.c,v 1.1 2000/03/28 21:15:45 markus Exp $");
 #include "channels.h"
 #include "nchan.h"
 
+#include "bufaux.h"
+#include "ssh2.h"
+#include "auth.h"
+
 /* types */
 
 #define TTYSZ 64
@@ -444,9 +452,13 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw)
        close(pout[1]);
        close(perr[1]);
 
-       /* Enter the interactive session. */
-       server_loop(pid, pin[1], pout[0], perr[0]);
-       /* server_loop has closed pin[1], pout[1], and perr[1]. */
+       if (compat20) {
+               session_set_fds(s, pin[1], pout[0], perr[0]);
+       } else {
+               /* Enter the interactive session. */
+               server_loop(pid, pin[1], pout[0], perr[0]);
+               /* server_loop has closed pin[1], pout[1], and perr[1]. */
+       }
 #else /* USE_PIPES */
        /* We are the parent.  Close the child sides of the socket pairs. */
        close(inout[0]);
@@ -456,8 +468,12 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw)
         * Enter the interactive session.  Note: server_loop must be able to
         * handle the case that fdin and fdout are the same.
         */
-       server_loop(pid, inout[1], inout[1], err[1]);
-       /* server_loop has closed inout[1] and err[1]. */
+       if (compat20) {
+               session_set_fds(s, inout[1], inout[1], err[1]);
+       } else {
+               server_loop(pid, inout[1], inout[1], err[1]);
+               /* server_loop has closed inout[1] and err[1]. */
+       }
 #endif /* USE_PIPES */
 }
 
@@ -617,9 +633,13 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw)
        s->ptymaster = ptymaster;
 
        /* Enter interactive session. */
-       server_loop(pid, ptyfd, fdout, -1);
-       /* server_loop _has_ closed ptyfd and fdout. */
-       session_pty_cleanup(s);
+       if (compat20) {
+               session_set_fds(s, ptyfd, fdout, -1);
+       } else {
+               server_loop(pid, ptyfd, fdout, -1);
+               /* server_loop _has_ closed ptyfd and fdout. */
+               session_pty_cleanup(s);
+       }
 }
 
 /*
@@ -1058,6 +1078,181 @@ session_dump(void)
        }
 }
 
+int
+session_open(int chanid)
+{
+       Session *s = session_new();
+       debug("session_open: channel %d", chanid);
+       if (s == NULL) {
+               error("no more sessions");
+               return 0;
+       }
+       debug("session_open: session %d: link with channel %d", s->self, chanid);
+       s->chanid = chanid;
+       s->pw = auth_get_user();
+       if (s->pw == NULL)
+               fatal("no user for session %i channel %d",
+                   s->self, s->chanid);
+       return 1;
+}
+
+Session *
+session_by_channel(int id)
+{
+       int i;
+       for(i = 0; i < MAX_SESSIONS; i++) {
+               Session *s = &sessions[i];
+               if (s->used && s->chanid == id) {
+                       debug("session_by_channel: session %d channel %d", i, id);
+                       return s;
+               }
+       }
+       debug("session_by_channel: unknown channel %d", id);
+       session_dump();
+       return NULL;
+}
+
+Session *
+session_by_pid(pid_t pid)
+{
+       int i;
+       debug("session_by_pid: pid %d", pid);
+       for(i = 0; i < MAX_SESSIONS; i++) {
+               Session *s = &sessions[i];
+               if (s->used && s->pid == pid)
+                       return s;
+       }
+       error("session_by_pid: unknown pid %d", pid);
+       session_dump();
+       return NULL;
+}
+
+int
+session_window_change_req(Session *s)
+{
+       s->col = packet_get_int();
+       s->row = packet_get_int();
+       s->xpixel = packet_get_int();
+       s->ypixel = packet_get_int();
+       pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
+       return 1;
+}
+
+int
+session_pty_req(Session *s)
+{
+       unsigned int len;
+
+       if (s->ttyfd != -1)
+               return -1;
+       s->term = packet_get_string(&len);
+       s->col = packet_get_int();
+       s->row = packet_get_int();
+       s->xpixel = packet_get_int();
+       s->ypixel = packet_get_int();
+
+       if (strcmp(s->term, "") == 0) {
+               xfree(s->term);
+               s->term = NULL;
+       }
+       /* Allocate a pty and open it. */
+       if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) {
+               xfree(s->term);
+               s->term = NULL;
+               s->ptyfd = -1;
+               s->ttyfd = -1;
+               error("session_pty_req: session %d alloc failed", s->self);
+               return -1;
+       }
+       debug("session_pty_req: session %d alloc %s", s->self, s->tty);
+       /*
+        * Add a cleanup function to clear the utmp entry and record logout
+        * time in case we call fatal() (e.g., the connection gets closed).
+        */
+       fatal_add_cleanup(pty_cleanup_proc, (void *)s);
+       pty_setowner(s->pw, s->tty);
+       /* Get window size from the packet. */
+       pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
+
+       return 1;
+}
+
+void
+session_input_channel_req(int id, void *arg)
+{
+       unsigned int len;
+       int reply;
+       int success = 0;
+       char *rtype;
+       Session *s;
+       Channel *c;
+
+       rtype = packet_get_string(&len);
+       reply = packet_get_char();
+
+       s = session_by_channel(id);
+       if (s == NULL)
+               fatal("session_input_channel_req: channel %d: no session", id);
+       c = channel_lookup(id);
+       if (c == NULL)
+               fatal("session_input_channel_req: channel %d: bad channel", id);
+
+       debug("session_input_channel_req: session %d channel %d request %s reply %d",
+           s->self, id, rtype, reply);
+
+       /*
+        * a session is in LARVAL state until a shell
+        * or programm is executed
+        */
+       if (c->type == SSH_CHANNEL_LARVAL) {
+               if (strcmp(rtype, "shell") == 0) {
+                       if (s->ttyfd == -1)
+                               do_exec_no_pty(s, NULL, s->pw);
+                       else
+                               do_exec_pty(s, NULL, s->pw);
+                       success = 1;
+               } else if (strcmp(rtype, "exec") == 0) {
+                       char *command = packet_get_string(&len);
+                       if (s->ttyfd == -1)
+                               do_exec_no_pty(s, command, s->pw);
+                       else
+                               do_exec_pty(s, command, s->pw);
+                       xfree(command);
+                       success = 1;
+               } else if (strcmp(rtype, "pty-req") == 0) {
+                       if (session_pty_req(s) > 0)
+                               success = 1;
+               }
+       }
+       if (strcmp(rtype, "window-change") == 0) {
+               success = session_window_change_req(s);
+       }
+
+       if (reply) {
+               packet_start(success ?
+                   SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
+               packet_put_int(c->remote_id);
+               packet_send();
+       }
+       xfree(rtype);
+}
+
+void
+session_set_fds(Session *s, int fdin, int fdout, int fderr)
+{
+       if (!compat20)
+               fatal("session_set_fds: called for proto != 2.0");
+       /*
+        * now that have a child and a pipe to the child,
+        * we can activate our channel and register the fd's
+        */
+       if (s->chanid == -1)
+               fatal("no channel for session %d", s->self);
+       channel_set_fds(s->chanid,
+           fdout, fdin, fderr,
+           fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ);
+}
+
 void
 session_pty_cleanup(Session *s)
 {
@@ -1083,3 +1278,118 @@ session_pty_cleanup(Session *s)
        if (close(s->ptymaster) < 0)
                error("close(s->ptymaster): %s", strerror(errno));
 }
+
+void
+session_exit_message(Session *s, int status)
+{
+       Channel *c;
+       if (s == NULL)
+               fatal("session_close: no session");
+       c = channel_lookup(s->chanid);
+       if (c == NULL)
+               fatal("session_close: session %d: no channel %d",
+                   s->self, s->chanid);
+       debug("session_exit_message: session %d channel %d pid %d",
+           s->self, s->chanid, s->pid);
+
+       if (WIFEXITED(status)) {
+               channel_request_start(s->chanid,
+                   "exit-status", 0);
+               packet_put_int(WEXITSTATUS(status));
+               packet_send();
+       } else if (WIFSIGNALED(status)) {
+               channel_request_start(s->chanid,
+                   "exit-signal", 0);
+               packet_put_int(WTERMSIG(status));
+               packet_put_char(WCOREDUMP(status));
+               packet_put_cstring("");
+               packet_put_cstring("");
+               packet_send();
+       } else {
+               /* Some weird exit cause.  Just exit. */
+               packet_disconnect("wait returned status %04x.", status);
+       }
+
+       /* disconnect channel */
+       debug("session_exit_message: release channel %d", s->chanid);
+       channel_cancel_cleanup(s->chanid);
+       if (c->istate == CHAN_INPUT_OPEN)
+               chan_read_failed(c);
+       chan_write_failed(c);
+       s->chanid = -1;
+}
+
+void
+session_free(Session *s)
+{
+       debug("session_free: session %d pid %d", s->self, s->pid);
+       if (s->term)
+               xfree(s->term);
+       if (s->display)
+               xfree(s->display);
+       if (s->auth_data)
+               xfree(s->auth_data);
+       if (s->auth_proto)
+               xfree(s->auth_proto);
+       s->used = 0;
+}
+
+void
+session_close(Session *s)
+{
+       session_pty_cleanup(s);
+       session_free(s);
+}
+
+void
+session_close_by_pid(pid_t pid, int status)
+{
+       Session *s = session_by_pid(pid);
+       if (s == NULL) {
+               debug("session_close_by_pid: no session for pid %d", s->pid);
+               return;
+       }
+       if (s->chanid != -1)
+               session_exit_message(s, status);
+       session_close(s);
+}
+
+/*
+ * this is called when a channel dies before
+ * the session 'child' itself dies
+ */
+void
+session_close_by_channel(int id, void *arg)
+{
+       Session *s = session_by_channel(id);
+       if (s == NULL) {
+               debug("session_close_by_channel: no session for channel %d", id);
+               return;
+       }
+       /* disconnect channel */
+       channel_cancel_cleanup(s->chanid);
+       s->chanid = -1;
+
+       debug("session_close_by_channel: channel %d kill %d", id, s->pid);
+       if (s->pid == 0) {
+               /* close session immediately */
+               session_close(s);
+       } else {
+               /* notify child, delay session cleanup */
+               if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0)
+                       error("session_close_by_channel: kill %d: %s",
+                           s->pid, strerror(errno));
+       }
+}
+
+void
+do_authenticated2(void)
+{
+       /*
+        * Cancel the alarm we set to limit the time taken for
+        * authentication.
+        */
+       alarm(0);
+       log("do_authenticated2");
+       server_loop2();
+}
index 2051b73..a3427bc 100644 (file)
@@ -4,4 +4,11 @@
 /* SSH1 */
 void   do_authenticated(struct passwd * pw);
 
+/* SSH2 */
+void   do_authenticated2(void);
+int    session_open(int id);
+void   session_input_channel_req(int id, void *arg);
+void   session_close_by_pid(pid_t pid, int status);
+void   session_close_by_channel(int id, void *arg);
+
 #endif
index 0c55ef0..c8db7da 100644 (file)
@@ -13,7 +13,7 @@
  * 
  */
 
-/* RCSID("$Id: ssh.h,v 1.35 2000/03/28 20:31:29 markus Exp $"); */
+/* RCSID("$Id: ssh.h,v 1.36 2000/04/06 08:55:22 markus Exp $"); */
 
 #ifndef SSH_H
 #define SSH_H
@@ -77,6 +77,7 @@
 #define HOST_KEY_FILE          ETCDIR "/ssh_host_key"
 #define SERVER_CONFIG_FILE     ETCDIR "/sshd_config"
 #define HOST_CONFIG_FILE       ETCDIR "/ssh_config"
+#define DSA_KEY_FILE           ETCDIR "/ssh_dsa_key"
 
 #define SSH_PROGRAM            "/usr/bin/ssh"
 
@@ -461,6 +462,8 @@ void    fatal_add_cleanup(void (*proc) (void *context), void *context);
 /* Removes a cleanup function to be called at fatal(). */
 void    fatal_remove_cleanup(void (*proc) (void *context), void *context);
 
+/* ---- misc */
+
 /*
  * Expands tildes in the file name.  Returns data allocated by xmalloc.
  * Warning: this calls getpw*.
@@ -475,6 +478,7 @@ char   *tilde_expand_filename(const char *filename, uid_t my_uid);
  * program).
  */
 void    server_loop(int pid, int fdin, int fdout, int fderr);
+void    server_loop2(void);
 
 /* Client side main loop for the interactive session. */
 int     client_loop(int have_pty, int escape_char);
index e6a7327..40d2aa6 100644 (file)
@@ -8,10 +8,13 @@
  * information to/from the application to the user client over an encrypted
  * connection.  This can also handle forwarding of X11, TCP/IP, and authentication
  * agent connections.
+ *
+ * SSH2 implementation,
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshd.c,v 1.97 2000/04/04 21:37:27 markus Exp $");
+RCSID("$OpenBSD: sshd.c,v 1.98 2000/04/06 08:55:22 markus Exp $");
 
 #include "xmalloc.h"
 #include "rsa.h"
@@ -25,14 +28,18 @@ RCSID("$OpenBSD: sshd.c,v 1.97 2000/04/04 21:37:27 markus Exp $");
 #include "compat.h"
 #include "buffer.h"
 
+#include "ssh2.h"
 #include <ssl/dh.h>
 #include <ssl/bn.h>
 #include <ssl/hmac.h>
+#include "kex.h"
 #include <ssl/dsa.h>
 #include <ssl/rsa.h>
 #include "key.h"
+#include "dsa.h"
 
 #include "auth.h"
+#include "myproposal.h"
 
 #ifdef LIBWRAP
 #include <tcpd.h>
@@ -57,6 +64,9 @@ char *config_file_name = SERVER_CONFIG_FILE;
  */
 int IPv4or6 = AF_UNSPEC;
 
+/* Flag indicating whether SSH2 is enabled */
+int allow_ssh2 = 0;
+
 /*
  * Debug mode flag.  This can be set on the command line.  If debug
  * mode is enabled, extra debugging output will be sent to the system
@@ -123,6 +133,7 @@ unsigned char session_id[16];
 
 /* Prototypes for various functions defined later in this file. */
 void do_ssh1_kex();
+void do_ssh2_kex();
 
 /*
  * Close all listening sockets
@@ -242,6 +253,21 @@ key_regeneration_alarm(int sig)
        errno = save_errno;
 }
 
+char *
+chop(char *s)
+{
+        char *t = s;
+        while (*t) {
+                if(*t == '\n' || *t == '\r') {
+                        *t = '\0';
+                        return s;
+                }
+                t++;
+        }
+        return s;
+
+}
+
 void
 sshd_exchange_identification(int sock_in, int sock_out)
 {
@@ -252,7 +278,9 @@ sshd_exchange_identification(int sock_in, int sock_out)
        char remote_version[256];       /* Must be at least as big as buf. */
 
        snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
-           PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
+                allow_ssh2 ? 1  : PROTOCOL_MAJOR,
+                allow_ssh2 ? 99 : PROTOCOL_MINOR,
+                SSH_VERSION);
        server_version_string = xstrdup(buf);
 
        if (client_version_string == NULL) {
@@ -273,7 +301,7 @@ sshd_exchange_identification(int sock_in, int sock_out)
                                buf[i] = '\n';
                                buf[i + 1] = 0;
                                continue;
-                               /*break; XXX eat \r */
+                               //break;
                        }
                        if (buf[i] == '\n') {
                                /* buf[i] == '\n' */
@@ -302,6 +330,8 @@ sshd_exchange_identification(int sock_in, int sock_out)
        debug("Client protocol version %d.%d; client software version %.100s",
              remote_major, remote_minor, remote_version);
 
+       compat_datafellows(remote_version);
+
        switch(remote_major) {
        case 1:
                if (remote_minor < 3) {
@@ -312,6 +342,11 @@ sshd_exchange_identification(int sock_in, int sock_out)
                        enable_compat13();
                }
                break;
+       case 2:
+               if (allow_ssh2) {
+                       enable_compat20();
+                       break;
+               }
        default: 
                s = "Protocol major versions differ.\n";
                (void) atomicio(write, sock_out, s, strlen(s));
@@ -322,6 +357,8 @@ sshd_exchange_identification(int sock_in, int sock_out)
                fatal_cleanup();
                break;
        }
+       chop(server_version_string);
+       chop(client_version_string);
 }
 
 /*
@@ -357,8 +394,11 @@ main(int ac, char **av)
        initialize_server_options(&options);
 
        /* Parse command-line arguments. */
-       while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ46")) != EOF) {
+       while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ246")) != EOF) {
                switch (opt) {
+               case '2':
+                       allow_ssh2 = 1;
+                       break;
                case '4':
                        IPv4or6 = AF_INET;
                        break;
@@ -823,9 +863,14 @@ main(int ac, char **av)
        packet_set_nonblocking();
 
        /* perform the key exchange */
-       do_ssh1_kex();
        /* authenticate user and start session */
-       do_authentication();
+       if (compat20) {
+               do_ssh2_kex();
+               do_authentication2();
+       } else {
+               do_ssh1_kex();
+               do_authentication();
+       }
 
 #ifdef KRB4
        /* Cleanup user's ticket cache file. */
@@ -1030,3 +1075,181 @@ do_ssh1_kex()
        packet_send();
        packet_write_wait();
 }
+
+/*
+ * SSH2 key exchange: diffie-hellman-group1-sha1
+ */
+void
+do_ssh2_kex()
+{
+       Buffer *server_kexinit;
+       Buffer *client_kexinit;
+       int payload_len, dlen;
+       int slen;
+       unsigned int klen, kout;
+       char *ptr;
+       unsigned char *signature = NULL;
+       unsigned char *server_host_key_blob = NULL;
+       unsigned int sbloblen;
+       DH *dh;
+       BIGNUM *dh_client_pub = 0;
+       BIGNUM *shared_secret = 0;
+       int i;
+       unsigned char *kbuf;
+       unsigned char *hash;
+       Kex *kex;
+       Key *server_host_key;
+       char *cprop[PROPOSAL_MAX];
+       char *sprop[PROPOSAL_MAX];
+
+/* KEXINIT */
+
+       debug("Sending KEX init.");
+
+       for (i = 0; i < PROPOSAL_MAX; i++)
+               sprop[i] = xstrdup(myproposal[i]);
+       server_kexinit = kex_init(sprop);
+       packet_start(SSH2_MSG_KEXINIT);
+       packet_put_raw(buffer_ptr(server_kexinit), buffer_len(server_kexinit)); 
+       packet_send();
+       packet_write_wait();
+
+       debug("done");
+
+       packet_read_expect(&payload_len, SSH2_MSG_KEXINIT);
+
+       /*
+        * save raw KEXINIT payload in buffer. this is used during
+        * computation of the session_id and the session keys.
+        */
+       client_kexinit = xmalloc(sizeof(*client_kexinit));
+       buffer_init(client_kexinit);
+       ptr = packet_get_raw(&payload_len);
+       buffer_append(client_kexinit, ptr, payload_len);
+
+       /* skip cookie */
+       for (i = 0; i < 16; i++)
+               (void) packet_get_char();
+       /* save kex init proposal strings */
+       for (i = 0; i < PROPOSAL_MAX; i++) {
+               cprop[i] = packet_get_string(NULL);
+               debug("got kexinit string: %s", cprop[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, 1);
+
+/* KEXDH */
+
+       debug("Wait SSH2_MSG_KEXDH_INIT.");
+       packet_read_expect(&payload_len, SSH2_MSG_KEXDH_INIT);
+
+       /* key, cert */
+       dh_client_pub = BN_new();
+       if (dh_client_pub == NULL)
+               fatal("dh_client_pub == NULL");
+       packet_get_bignum2(dh_client_pub, &dlen);
+
+#ifdef DEBUG_KEXDH
+       fprintf(stderr, "\ndh_client_pub= ");
+       bignum_print(dh_client_pub);
+       fprintf(stderr, "\n");
+       debug("bits %d", BN_num_bits(dh_client_pub));
+#endif
+
+       /* generate DH key */
+       dh = new_dh_group1();                   /* XXX depends on 'kex' */
+
+#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");
+#endif
+
+       klen = DH_size(dh);
+       kbuf = xmalloc(klen);
+       kout = DH_compute_key(kbuf, dh_client_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);
+
+       server_host_key = dsa_get_serverkey(options.dsa_key_file);
+       dsa_make_serverkey_blob(server_host_key, &server_host_key_blob, &sbloblen);
+
+       /* calc H */                    /* XXX depends on 'kex' */
+       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),
+           (char *)server_host_key_blob, sbloblen,
+           dh_client_pub,
+           dh->pub_key,
+           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
+       /* sign H */
+       dsa_sign(server_host_key, &signature, &slen, hash, 20);
+               /* hashlen depends on KEX */
+       key_free(server_host_key);
+
+       /* send server hostkey, DH pubkey 'f' and singed H */
+       packet_start(SSH2_MSG_KEXDH_REPLY);
+       packet_put_string((char *)server_host_key_blob, sbloblen);
+       packet_put_bignum2(dh->pub_key);        // f
+       packet_put_string((char *)signature, slen);
+       packet_send();
+       packet_write_wait();
+
+       kex_derive_keys(kex, hash, shared_secret);
+       packet_set_kex(kex);
+
+       /* have keys, free DH */
+       DH_free(dh);
+
+       debug("send SSH2_MSG_NEWKEYS.");
+       packet_start(SSH2_MSG_NEWKEYS);
+       packet_send();
+       packet_write_wait();
+       debug("done: send SSH2_MSG_NEWKEYS.");
+
+       debug("Wait SSH2_MSG_NEWKEYS.");
+       packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS);
+       debug("GOT 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.");
+}