]> andersk Git - openssh.git/blobdiff - serverloop.c
- reyk@cvs.openbsd.org 2005/12/06 22:38:28
[openssh.git] / serverloop.c
index 651d3feb5b29ee0e1c7c8630efb12e2571b52f61..199f7696d05be9a55dde2cfa41df95c6862836c6 100644 (file)
@@ -11,7 +11,7 @@
  * called by a name other than "ssh" or "Secure Shell".
  *
  * SSH2 support by Markus Friedl.
- * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: serverloop.c,v 1.51 2001/02/23 15:34:53 markus Exp $");
+RCSID("$OpenBSD: serverloop.c,v 1.122 2005/12/06 22:38:27 reyk Exp $");
 
 #include "xmalloc.h"
 #include "packet.h"
 #include "buffer.h"
 #include "log.h"
 #include "servconf.h"
+#include "canohost.h"
 #include "sshpty.h"
 #include "channels.h"
 #include "compat.h"
@@ -53,9 +54,15 @@ RCSID("$OpenBSD: serverloop.c,v 1.51 2001/02/23 15:34:53 markus Exp $");
 #include "auth-options.h"
 #include "serverloop.h"
 #include "misc.h"
+#include "kex.h"
 
 extern ServerOptions options;
 
+/* XXX */
+extern Kex *xxx_kex;
+extern Authctxt *the_authctxt;
+extern int use_privsep;
+
 static Buffer stdin_buffer;    /* Buffer for stdin data. */
 static Buffer stdout_buffer;   /* Buffer for stdout data. */
 static Buffer stderr_buffer;   /* Buffer for stderr data. */
@@ -73,56 +80,95 @@ static int fderr_eof = 0;   /* EOF encountered readung from fderr. */
 static int fdin_is_tty = 0;    /* fdin points to a tty. */
 static int connection_in;      /* Connection to client (input). */
 static int connection_out;     /* Connection to client (output). */
-static u_int buffer_high;/* "Soft" max buffer size. */
+static int connection_closed = 0;      /* Connection to client closed. */
+static u_int buffer_high;      /* "Soft" max buffer size. */
+static int client_alive_timeouts = 0;
 
 /*
  * This SIGCHLD kludge is used to detect when the child exits.  The server
  * will exit after that, as soon as forwarded connections have terminated.
  */
 
-static pid_t child_pid;                        /* Pid of the child. */
-static volatile int child_terminated;  /* The child has terminated. */
-static volatile int child_wait_status; /* Status from wait(). */
+static volatile sig_atomic_t child_terminated = 0;     /* The child has terminated. */
 
-void   server_init_dispatch(void);
+/* Cleanup on signals (!use_privsep case only) */
+static volatile sig_atomic_t received_sigterm = 0;
 
-void
-sigchld_handler(int sig)
-{
-       int save_errno = errno;
-       pid_t wait_pid;
+/* prototypes */
+static void server_init_dispatch(void);
 
-       debug("Received SIGCHLD.");
-       wait_pid = wait((int *) &child_wait_status);
-       if (wait_pid != -1) {
-               if (wait_pid != child_pid)
-                       error("Strange, got SIGCHLD and wait returned pid %d but child is %d",
-                             wait_pid, child_pid);
-               if (WIFEXITED(child_wait_status) ||
-                   WIFSIGNALED(child_wait_status))
-                       child_terminated = 1;
+/*
+ * we write to this pipe if a SIGCHLD is caught in order to avoid
+ * the race between select() and child_terminated
+ */
+static int notify_pipe[2];
+static void
+notify_setup(void)
+{
+       if (pipe(notify_pipe) < 0) {
+               error("pipe(notify_pipe) failed %s", strerror(errno));
+       } else if ((fcntl(notify_pipe[0], F_SETFD, 1) == -1) ||
+           (fcntl(notify_pipe[1], F_SETFD, 1) == -1)) {
+               error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno));
+               close(notify_pipe[0]);
+               close(notify_pipe[1]);
+       } else {
+               set_nonblock(notify_pipe[0]);
+               set_nonblock(notify_pipe[1]);
+               return;
        }
-       signal(SIGCHLD, sigchld_handler);
-       errno = save_errno;
+       notify_pipe[0] = -1;    /* read end */
+       notify_pipe[1] = -1;    /* write end */
 }
-void
-sigchld_handler2(int sig)
+static void
+notify_parent(void)
+{
+       if (notify_pipe[1] != -1)
+               write(notify_pipe[1], "", 1);
+}
+static void
+notify_prepare(fd_set *readset)
+{
+       if (notify_pipe[0] != -1)
+               FD_SET(notify_pipe[0], readset);
+}
+static void
+notify_done(fd_set *readset)
+{
+       char c;
+
+       if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset))
+               while (read(notify_pipe[0], &c, 1) != -1)
+                       debug2("notify_done: reading");
+}
+
+static void
+sigchld_handler(int sig)
 {
        int save_errno = errno;
        debug("Received SIGCHLD.");
        child_terminated = 1;
-       mysignal(SIGCHLD, sigchld_handler2);
+#ifndef _UNICOS
+       mysignal(SIGCHLD, sigchld_handler);
+#endif
+       notify_parent();
        errno = save_errno;
 }
 
+static void
+sigterm_handler(int sig)
+{
+       received_sigterm = sig;
+}
+
 /*
  * Make packets from buffered stderr data, and buffer it for sending
  * to the client.
  */
-void
+static void
 make_packets_from_stderr_data(void)
 {
-       int len;
+       u_int len;
 
        /* Send buffered stderr data to the client. */
        while (buffer_len(&stderr_buffer) > 0 &&
@@ -148,10 +194,10 @@ make_packets_from_stderr_data(void)
  * Make packets from buffered stdout data, and buffer it for sending to the
  * client.
  */
-void
+static void
 make_packets_from_stdout_data(void)
 {
-       int len;
+       u_int len;
 
        /* Send buffered stdout data to the client. */
        while (buffer_len(&stdout_buffer) > 0 &&
@@ -173,29 +219,66 @@ make_packets_from_stdout_data(void)
        }
 }
 
+static void
+client_alive_check(void)
+{
+       int channel_id;
+
+       /* timeout, check to see how many we have had */
+       if (++client_alive_timeouts > options.client_alive_count_max)
+               packet_disconnect("Timeout, your session not responding.");
+
+       /*
+        * send a bogus global/channel request with "wantreply",
+        * we should get back a failure
+        */
+       if ((channel_id = channel_find_open()) == -1) {
+               packet_start(SSH2_MSG_GLOBAL_REQUEST);
+               packet_put_cstring("keepalive@openssh.com");
+               packet_put_char(1);     /* boolean: want reply */
+       } else {
+               channel_request_start(channel_id, "keepalive@openssh.com", 1);
+       }
+       packet_send();
+}
+
 /*
  * Sleep in select() until we can do something.  This will initialize the
  * select masks.  Upon return, the masks will indicate which descriptors
  * have data or can accept data.  Optionally, a maximum time can be specified
  * for the duration of the wait (0 = infinite).
  */
-void
+static void
 wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
-    u_int max_time_milliseconds)
+    u_int *nallocp, u_int max_time_milliseconds)
 {
        struct timeval tv, *tvp;
        int ret;
+       int client_alive_scheduled = 0;
 
-       /* When select fails we restart from here. */
-retry_select:
+       /*
+        * if using client_alive, set the max timeout accordingly,
+        * and indicate that this particular timeout was for client
+        * alive by setting the client_alive_scheduled flag.
+        *
+        * this could be randomized somewhat to make traffic
+        * analysis more difficult, but we're not doing it yet.
+        */
+       if (compat20 &&
+           max_time_milliseconds == 0 && options.client_alive_interval) {
+               client_alive_scheduled = 1;
+               max_time_milliseconds = options.client_alive_interval * 1000;
+       }
 
        /* Allocate and update select() masks for channel descriptors. */
-       channel_prepare_select(readsetp, writesetp, maxfdp);
+       channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 0);
 
        if (compat20) {
+#if 0
                /* wrong: bad condition XXX */
                if (channel_not_very_much_buffered_data())
-                       FD_SET(connection_in, *readsetp);
+#endif
+               FD_SET(connection_in, *readsetp);
        } else {
                /*
                 * Read packets from the client unless we have too much
@@ -221,6 +304,7 @@ retry_select:
                if (fdin != -1 && buffer_len(&stdin_buffer) > 0)
                        FD_SET(fdin, *writesetp);
        }
+       notify_prepare(*readsetp);
 
        /*
         * If we have buffered packet data going to the client, mark that
@@ -234,7 +318,7 @@ retry_select:
         * from it, then read as much as is available and exit.
         */
        if (child_terminated && packet_not_very_much_data_to_write())
-               if (max_time_milliseconds == 0)
+               if (max_time_milliseconds == 0 || client_alive_scheduled)
                        max_time_milliseconds = 100;
 
        if (max_time_milliseconds == 0)
@@ -244,25 +328,26 @@ retry_select:
                tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
                tvp = &tv;
        }
-       if (tvp!=NULL)
-               debug3("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds);
 
        /* Wait for something to happen, or the timeout to expire. */
        ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
 
-       if (ret < 0) {
+       if (ret == -1) {
+               memset(*readsetp, 0, *nallocp);
+               memset(*writesetp, 0, *nallocp);
                if (errno != EINTR)
                        error("select: %.100s", strerror(errno));
-               else
-                       goto retry_select;
-       }
+       } else if (ret == 0 && client_alive_scheduled)
+               client_alive_check();
+
+       notify_done(*readsetp);
 }
 
 /*
  * Processes input from the client and the program.  Input data is stored
  * in buffers and processed later.
  */
-void
+static void
 process_input(fd_set * readset)
 {
        int len;
@@ -272,12 +357,18 @@ process_input(fd_set * readset)
        if (FD_ISSET(connection_in, readset)) {
                len = read(connection_in, buf, sizeof(buf));
                if (len == 0) {
-                       verbose("Connection closed by remote host.");
-                       fatal_cleanup();
+                       verbose("Connection closed by %.100s",
+                           get_remote_ipaddr());
+                       connection_closed = 1;
+                       if (compat20)
+                               return;
+                       cleanup_exit(255);
                } else if (len < 0) {
                        if (errno != EINTR && errno != EAGAIN) {
-                               verbose("Read error from remote host: %.100s", strerror(errno));
-                               fatal_cleanup();
+                               verbose("Read error from remote host "
+                                   "%.100s: %.100s",
+                                   get_remote_ipaddr(), strerror(errno));
+                               cleanup_exit(255);
                        }
                } else {
                        /* Buffer any received data. */
@@ -315,39 +406,37 @@ process_input(fd_set * readset)
 /*
  * Sends data from internal buffers to client program stdin.
  */
-void
+static void
 process_output(fd_set * writeset)
 {
        struct termios tio;
+       u_char *data;
+       u_int dlen;
        int len;
 
        /* Write buffered data to program stdin. */
        if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) {
-               len = write(fdin, buffer_ptr(&stdin_buffer),
-                   buffer_len(&stdin_buffer));
+               data = buffer_ptr(&stdin_buffer);
+               dlen = buffer_len(&stdin_buffer);
+               len = write(fdin, data, dlen);
                if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
                        /* do nothing */
                } else if (len <= 0) {
-#ifdef USE_PIPES
-                       close(fdin);
-#else
                        if (fdin != fdout)
                                close(fdin);
                        else
                                shutdown(fdin, SHUT_WR); /* We will no longer send. */
-#endif
                        fdin = -1;
                } else {
                        /* Successful write. */
-                       if (fdin_is_tty && tcgetattr(fdin, &tio) == 0 &&
+                       if (fdin_is_tty && dlen >= 1 && data[0] != '\r' &&
+                           tcgetattr(fdin, &tio) == 0 &&
                            !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
                                /*
                                 * Simulate echo to reduce the impact of
                                 * traffic analysis
                                 */
-                               packet_start(SSH_MSG_IGNORE);
-                               memset(buffer_ptr(&stdin_buffer), 0, len);
-                               packet_put_string(buffer_ptr(&stdin_buffer), len);
+                               packet_send_ignore(len);
                                packet_send();
                        }
                        /* Consume the data from the buffer. */
@@ -365,7 +454,7 @@ process_output(fd_set * writeset)
  * Wait until all buffered output has been sent to the client.
  * This is used when the program terminates.
  */
-void
+static void
 drain_output(void)
 {
        /* Send any buffered stdout data to the client. */
@@ -390,10 +479,10 @@ drain_output(void)
        packet_write_wait();
 }
 
-void
+static void
 process_buffered_input_packets(void)
 {
-       dispatch_run(DISPATCH_NONBLOCK, NULL, NULL);
+       dispatch_run(DISPATCH_NONBLOCK, NULL, compat20 ? xxx_kex : NULL);
 }
 
 /*
@@ -407,7 +496,8 @@ void
 server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
 {
        fd_set *readset = NULL, *writeset = NULL;
-       int max_fd;
+       int max_fd = 0;
+       u_int nalloc = 0;
        int wait_status;        /* Status returned by wait(). */
        pid_t wait_pid;         /* pid returned by wait(). */
        int waiting_termination = 0;    /* Have displayed waiting close message. */
@@ -419,10 +509,14 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
        debug("Entering interactive session.");
 
        /* Initialize the SIGCHLD kludge. */
-       child_pid = pid;
        child_terminated = 0;
-       signal(SIGCHLD, sigchld_handler);
-       signal(SIGPIPE, SIG_IGN);
+       mysignal(SIGCHLD, sigchld_handler);
+
+       if (!use_privsep) {
+               signal(SIGTERM, sigterm_handler);
+               signal(SIGINT, sigterm_handler);
+               signal(SIGQUIT, sigterm_handler);
+       }
 
        /* Initialize our global variables. */
        fdin = fdin_arg;
@@ -442,6 +536,8 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
        connection_in = packet_get_connection_in();
        connection_out = packet_get_connection_out();
 
+       notify_setup();
+
        previous_stdout_buffer_bytes = 0;
 
        /* Set approximate I/O buffer size. */
@@ -450,12 +546,14 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
        else
                buffer_high = 64 * 1024;
 
+#if 0
        /* Initialize max_fd to the maximum of the known file descriptors. */
-       max_fd = MAX(fdin, fdout);
+       max_fd = MAX(connection_in, connection_out);
+       max_fd = MAX(max_fd, fdin);
+       max_fd = MAX(max_fd, fdout);
        if (fderr != -1)
                max_fd = MAX(max_fd, fderr);
-       max_fd = MAX(max_fd, connection_in);
-       max_fd = MAX(max_fd, connection_out);
+#endif
 
        /* Initialize Initialize buffers. */
        buffer_init(&stdin_buffer);
@@ -466,7 +564,7 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
         * If we have no separate fderr (which is the case when we have a pty
         * - there we cannot make difference between data sent to stdout and
         * stderr), indicate that we have seen an EOF from stderr.  This way
-        * we don\'t need to check the descriptor everywhere.
+        * we don't need to check the descriptor everywhere.
         */
        if (fderr == -1)
                fderr_eof = 1;
@@ -484,14 +582,10 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
                 * input data, cause a real eof by closing fdin.
                 */
                if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) {
-#ifdef USE_PIPES
-                       close(fdin);
-#else
                        if (fdin != fdout)
                                close(fdin);
                        else
                                shutdown(fdin, SHUT_WR); /* We will no longer send. */
-#endif
                        fdin = -1;
                }
                /* Make packets from buffered stderr data to send to the client. */
@@ -541,9 +635,21 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
                                xfree(cp);
                        }
                }
+               max_fd = MAX(connection_in, connection_out);
+               max_fd = MAX(max_fd, fdin);
+               max_fd = MAX(max_fd, fdout);
+               max_fd = MAX(max_fd, fderr);
+               max_fd = MAX(max_fd, notify_pipe[0]);
+
                /* Sleep in select() until we can do something. */
                wait_until_can_do_something(&readset, &writeset, &max_fd,
-                   max_time_milliseconds);
+                   &nalloc, max_time_milliseconds);
+
+               if (received_sigterm) {
+                       logit("Exiting on signal %d", received_sigterm);
+                       /* Clean up sessions, utmp, etc. */
+                       cleanup_exit(255);
+               }
 
                /* Process any channel events. */
                channel_after_select(readset, writeset);
@@ -565,7 +671,7 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
        drain_output();
 
        debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.",
-             stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
+           stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
 
        /* Free and clear the buffers. */
        buffer_free(&stdin_buffer);
@@ -585,30 +691,17 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
                close(fdin);
        fdin = -1;
 
-       /* Stop listening for channels; this removes unix domain sockets. */
-       channel_stop_listening();
-
-       /* Wait for the child to exit.  Get its exit status. */
-       wait_pid = wait(&wait_status);
-       if (wait_pid == -1) {
-               /*
-                * It is possible that the wait was handled by SIGCHLD
-                * handler.  This may result in either: this call
-                * returning with EINTR, or: this call returning ECHILD.
-                */
-               if (child_terminated)
-                       wait_status = child_wait_status;
-               else
-                       packet_disconnect("wait: %.100s", strerror(errno));
-       } else {
-               /* Check if it matches the process we forked. */
-               if (wait_pid != pid)
-                       error("Strange, wait returned pid %d, expected %d",
-                              wait_pid, pid);
-       }
+       channel_free_all();
 
        /* We no longer want our SIGCHLD handler to be called. */
-       signal(SIGCHLD, SIG_DFL);
+       mysignal(SIGCHLD, SIG_DFL);
+
+       while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0)
+               if (errno != EINTR)
+                       packet_disconnect("wait: %.100s", strerror(errno));
+       if (wait_pid != pid)
+               error("Strange, wait returned pid %ld, expected %ld",
+                   (long)wait_pid, (long)pid);
 
        /* Check if it exited normally. */
        if (WIFEXITED(wait_status)) {
@@ -627,8 +720,7 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
                 * the exit status.
                 */
                do {
-                       int plen;
-                       type = packet_read(&plen);
+                       type = packet_read();
                }
                while (type != SSH_CMSG_EXIT_CONFIRMATION);
 
@@ -645,60 +737,111 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
        /* NOTREACHED */
 }
 
+static void
+collect_children(void)
+{
+       pid_t pid;
+       sigset_t oset, nset;
+       int status;
+
+       /* block SIGCHLD while we check for dead children */
+       sigemptyset(&nset);
+       sigaddset(&nset, SIGCHLD);
+       sigprocmask(SIG_BLOCK, &nset, &oset);
+       if (child_terminated) {
+               while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
+                   (pid < 0 && errno == EINTR))
+                       if (pid > 0)
+                               session_close_by_pid(pid, status);
+               child_terminated = 0;
+       }
+       sigprocmask(SIG_SETMASK, &oset, NULL);
+}
+
 void
-server_loop2(void)
+server_loop2(Authctxt *authctxt)
 {
        fd_set *readset = NULL, *writeset = NULL;
-       int max_fd;
-       int had_channel = 0;
-       int status;
-       pid_t pid;
+       int rekeying = 0, max_fd, nalloc = 0;
 
        debug("Entering interactive session for SSH2.");
 
-       mysignal(SIGCHLD, sigchld_handler2);
-       signal(SIGPIPE, SIG_IGN);
+       mysignal(SIGCHLD, sigchld_handler);
        child_terminated = 0;
        connection_in = packet_get_connection_in();
        connection_out = packet_get_connection_out();
 
+       if (!use_privsep) {
+               signal(SIGTERM, sigterm_handler);
+               signal(SIGINT, sigterm_handler);
+               signal(SIGQUIT, sigterm_handler);
+       }
+
+       notify_setup();
+
        max_fd = MAX(connection_in, connection_out);
+       max_fd = MAX(max_fd, notify_pipe[0]);
 
        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())
+
+               rekeying = (xxx_kex != NULL && !xxx_kex->done);
+
+               if (!rekeying && packet_not_very_much_data_to_write())
                        channel_output_poll();
-               wait_until_can_do_something(&readset, &writeset, &max_fd, 0);
-               if (child_terminated) {
-                       while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
-                               session_close_by_pid(pid, status);
-                       child_terminated = 0;
+               wait_until_can_do_something(&readset, &writeset, &max_fd,
+                   &nalloc, 0);
+
+               if (received_sigterm) {
+                       logit("Exiting on signal %d", received_sigterm);
+                       /* Clean up sessions, utmp, etc. */
+                       cleanup_exit(255);
+               }
+
+               collect_children();
+               if (!rekeying) {
+                       channel_after_select(readset, writeset);
+                       if (packet_need_rekeying()) {
+                               debug("need rekeying");
+                               xxx_kex->done = 0;
+                               kex_send_kexinit(xxx_kex);
+                       }
                }
-               channel_after_select(readset, writeset);
                process_input(readset);
+               if (connection_closed)
+                       break;
                process_output(writeset);
        }
+       collect_children();
+
        if (readset)
                xfree(readset);
        if (writeset)
                xfree(writeset);
 
-       signal(SIGCHLD, SIG_DFL);
-       while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
-               session_close_by_pid(pid, status);
-       channel_stop_listening();
+       /* free all channels, no more reads and writes */
+       channel_free_all();
+
+       /* free remaining sessions, e.g. remove wtmp entries */
+       session_destroy_all(NULL);
 }
 
-void
-server_input_stdin_data(int type, int plen, void *ctxt)
+static void
+server_input_keep_alive(int type, u_int32_t seq, void *ctxt)
+{
+       debug("Got %d/%u for keepalive", type, seq);
+       /*
+        * reset timeout, since we got a sane answer from the client.
+        * even if this was generated by something other than
+        * the bogus CHANNEL_REQUEST we send for keepalives.
+        */
+       client_alive_timeouts = 0;
+}
+
+static void
+server_input_stdin_data(int type, u_int32_t seq, void *ctxt)
 {
        char *data;
        u_int data_len;
@@ -708,14 +851,14 @@ server_input_stdin_data(int type, int plen, void *ctxt)
        if (fdin == -1)
                return;
        data = packet_get_string(&data_len);
-       packet_integrity_check(plen, (4 + data_len), type);
+       packet_check_eom();
        buffer_append(&stdin_buffer, data, data_len);
        memset(data, 0, data_len);
        xfree(data);
 }
 
-void
-server_input_eof(int type, int plen, void *ctxt)
+static void
+server_input_eof(int type, u_int32_t seq, void *ctxt)
 {
        /*
         * Eof from the client.  The stdin descriptor to the
@@ -723,12 +866,12 @@ server_input_eof(int type, int plen, void *ctxt)
         * drained.
         */
        debug("EOF received for stdin.");
-       packet_integrity_check(plen, 0, type);
+       packet_check_eom();
        stdin_eof = 1;
 }
 
-void
-server_input_window_size(int type, int plen, void *ctxt)
+static void
+server_input_window_size(int type, u_int32_t seq, void *ctxt)
 {
        int row = packet_get_int();
        int col = packet_get_int();
@@ -736,15 +879,16 @@ server_input_window_size(int type, int plen, void *ctxt)
        int ypixel = packet_get_int();
 
        debug("Window change received.");
-       packet_integrity_check(plen, 4 * 4, type);
+       packet_check_eom();
        if (fdin != -1)
                pty_change_window_size(fdin, row, col, xpixel, ypixel);
 }
 
-Channel *
-server_request_direct_tcpip(char *ctype)
+static Channel *
+server_request_direct_tcpip(void)
 {
-       int sock, newch;
+       Channel *c;
+       int sock;
        char *target, *originator;
        int target_port, originator_port;
 
@@ -752,65 +896,85 @@ server_request_direct_tcpip(char *ctype)
        target_port = packet_get_int();
        originator = packet_get_string(NULL);
        originator_port = packet_get_int();
-       packet_done();
+       packet_check_eom();
 
        debug("server_request_direct_tcpip: originator %s port %d, target %s port %d",
-          originator, originator_port, target, target_port);
+           originator, originator_port, target, target_port);
 
        /* XXX check permission */
-       if (no_port_forwarding_flag || !options.allow_tcp_forwarding) {
-               xfree(target);
-               xfree(originator);
-               return NULL;
-       }
        sock = channel_connect_to(target, target_port);
        xfree(target);
        xfree(originator);
        if (sock < 0)
                return NULL;
-       newch = channel_new(ctype, SSH_CHANNEL_CONNECTING,
+       c = channel_new("direct-tcpip", SSH_CHANNEL_CONNECTING,
            sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT,
-           CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip"), 1);
-       return (newch >= 0) ? channel_lookup(newch) : NULL;
+           CHAN_TCP_PACKET_DEFAULT, 0, "direct-tcpip", 1);
+       return c;
 }
 
-Channel *
-server_request_session(char *ctype)
+static Channel *
+server_request_tun(void)
 {
-       int newch;
+       Channel *c = NULL;
+       int sock, tun;
+
+       if (!options.permit_tun) {
+               packet_send_debug("Server has disabled tunnel device forwarding.");
+               return NULL;
+       }
+
+       tun = packet_get_int();
+       if (forced_tun_device != -1) {
+               if (tun != -1 && forced_tun_device != tun)
+                       goto done;
+               tun = forced_tun_device;
+       }
+       sock = tun_open(tun);
+       if (sock < 0)
+               goto done;
+       c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
+           CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
+       c->datagram = 1;
+
+ done:
+       if (c == NULL)
+               packet_send_debug("Failed to open the tunnel device.");
+       return c;
+}
+
+static Channel *
+server_request_session(void)
+{
+       Channel *c;
 
        debug("input_session_request");
-       packet_done();
+       packet_check_eom();
        /*
         * 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.
         */
-       newch = channel_new(ctype, SSH_CHANNEL_LARVAL,
-           -1, -1, -1, 0, CHAN_SES_PACKET_DEFAULT,
-           0, xstrdup("server-session"), 1);
-       if (session_open(newch) == 1) {
-               channel_register_callback(newch, SSH2_MSG_CHANNEL_REQUEST,
-                   session_input_channel_req, (void *)0);
-               channel_register_cleanup(newch, session_close_by_channel);
-               return channel_lookup(newch);
-       } else {
-               debug("session open failed, free channel %d", newch);
-               channel_free(newch);
+       c = channel_new("session", SSH_CHANNEL_LARVAL,
+           -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT,
+           0, "server-session", 1);
+       if (session_open(the_authctxt, c->self) != 1) {
+               debug("session open failed, free channel %d", c->self);
+               channel_free(c);
+               return NULL;
        }
-       return NULL;
+       channel_register_cleanup(c->self, session_close_by_channel, 0);
+       return c;
 }
 
-void
-server_input_channel_open(int type, int plen, void *ctxt)
+static void
+server_input_channel_open(int type, u_int32_t seq, void *ctxt)
 {
        Channel *c = NULL;
        char *ctype;
-       u_int len;
        int rchan;
-       int rmaxpack;
-       int rwindow;
+       u_int rmaxpack, rwindow, len;
 
        ctype = packet_get_string(&len);
        rchan = packet_get_int();
@@ -821,36 +985,41 @@ server_input_channel_open(int type, int plen, void *ctxt)
            ctype, rchan, rwindow, rmaxpack);
 
        if (strcmp(ctype, "session") == 0) {
-               c = server_request_session(ctype);
+               c = server_request_session();
        } else if (strcmp(ctype, "direct-tcpip") == 0) {
-               c = server_request_direct_tcpip(ctype);
+               c = server_request_direct_tcpip();
+       } else if (strcmp(ctype, "tun@openssh.com") == 0) {
+               c = server_request_tun();
        }
        if (c != NULL) {
                debug("server_input_channel_open: 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();
+               if (c->type != SSH_CHANNEL_CONNECTING) {
+                       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("server_input_channel_open: 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("");
+               if (!(datafellows & SSH_BUG_OPENFAILURE)) {
+                       packet_put_cstring("open failed");
+                       packet_put_cstring("");
+               }
                packet_send();
        }
        xfree(ctype);
 }
 
-void
-server_input_global_request(int type, int plen, void *ctxt)
+static void
+server_input_global_request(int type, u_int32_t seq, void *ctxt)
 {
        char *rtype;
        int want_reply;
@@ -860,34 +1029,46 @@ server_input_global_request(int type, int plen, void *ctxt)
        want_reply = packet_get_char();
        debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply);
 
+       /* -R style forwarding */
        if (strcmp(rtype, "tcpip-forward") == 0) {
                struct passwd *pw;
                char *listen_address;
                u_short listen_port;
 
-               pw = auth_get_user();
-               if (pw == NULL)
-                       fatal("server_input_global_request: no user");
-               listen_address = packet_get_string(NULL); /* XXX currently ignored */
+               pw = the_authctxt->pw;
+               if (pw == NULL || !the_authctxt->valid)
+                       fatal("server_input_global_request: no/invalid user");
+               listen_address = packet_get_string(NULL);
                listen_port = (u_short)packet_get_int();
                debug("server_input_global_request: tcpip-forward listen %s port %d",
                    listen_address, listen_port);
 
                /* check permissions */
                if (!options.allow_tcp_forwarding ||
-                   no_port_forwarding_flag ||
-                   (listen_port < IPPORT_RESERVED && pw->pw_uid != 0)) {
+                   no_port_forwarding_flag
+#ifndef NO_IPPORT_RESERVED_CONCEPT
+                   || (listen_port < IPPORT_RESERVED && pw->pw_uid != 0)
+#endif
+                   ) {
                        success = 0;
                        packet_send_debug("Server has disabled port forwarding.");
                } else {
                        /* Start listening on the port */
-                       success = channel_request_forwarding(
-                           listen_address, listen_port,
-                           /*unspec host_to_connect*/ "<unspec host>",
-                           /*unspec port_to_connect*/ 0,
-                           options.gateway_ports, /*remote*/ 1);
+                       success = channel_setup_remote_fwd_listener(
+                           listen_address, listen_port, options.gateway_ports);
                }
                xfree(listen_address);
+       } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) {
+               char *cancel_address;
+               u_short cancel_port;
+
+               cancel_address = packet_get_string(NULL);
+               cancel_port = (u_short)packet_get_int();
+               debug("%s: cancel-tcpip-forward addr %s port %d", __func__,
+                   cancel_address, cancel_port);
+
+               success = channel_cancel_rport_listener(cancel_address,
+                   cancel_port);
        }
        if (want_reply) {
                packet_start(success ?
@@ -897,8 +1078,35 @@ server_input_global_request(int type, int plen, void *ctxt)
        }
        xfree(rtype);
 }
+static void
+server_input_channel_req(int type, u_int32_t seq, void *ctxt)
+{
+       Channel *c;
+       int id, reply, success = 0;
+       char *rtype;
 
-void
+       id = packet_get_int();
+       rtype = packet_get_string(NULL);
+       reply = packet_get_char();
+
+       debug("server_input_channel_req: channel %d request %s reply %d",
+           id, rtype, reply);
+
+       if ((c = channel_lookup(id)) == NULL)
+               packet_disconnect("server_input_channel_req: "
+                   "unknown channel %d", id);
+       if (c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN)
+               success = session_input_channel_req(c, rtype);
+       if (reply) {
+               packet_start(success ?
+                   SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
+               packet_put_int(c->remote_id);
+               packet_send();
+       }
+       xfree(rtype);
+}
+
+static void
 server_init_dispatch_20(void)
 {
        debug("server_init_dispatch_20");
@@ -910,11 +1118,17 @@ server_init_dispatch_20(void)
        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_REQUEST, &server_input_channel_req);
        dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
        dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request);
+       /* client_alive */
+       dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_keep_alive);
+       dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &server_input_keep_alive);
+       dispatch_set(SSH2_MSG_REQUEST_FAILURE, &server_input_keep_alive);
+       /* rekeying */
+       dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
 }
-void
+static void
 server_init_dispatch_13(void)
 {
        debug("server_init_dispatch_13");
@@ -929,7 +1143,7 @@ server_init_dispatch_13(void)
        dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
        dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
 }
-void
+static void
 server_init_dispatch_15(void)
 {
        server_init_dispatch_13();
@@ -937,7 +1151,7 @@ server_init_dispatch_15(void)
        dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
        dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose);
 }
-void
+static void
 server_init_dispatch(void)
 {
        if (compat20)
This page took 0.077993 seconds and 4 git commands to generate.