]> andersk Git - gssapi-openssh.git/blobdiff - openssh/serverloop.c
merged OpenSSH 5.2p1 to trunk
[gssapi-openssh.git] / openssh / serverloop.c
index b17547180b208ca8835cc0bc1dae1ec358861527..49e4c79dd9bac42bd3b1934fbd65cfa3b630638f 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: serverloop.c,v 1.145 2006/10/11 12:38:03 markus Exp $ */
+/* $OpenBSD: serverloop.c,v 1.157 2009/02/12 03:16:01 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -56,6 +56,7 @@
 #include <unistd.h>
 #include <stdarg.h>
 
+#include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "packet.h"
 #include "buffer.h"
@@ -92,10 +93,10 @@ static int fdin;            /* Descriptor for stdin (for writing) */
 static int fdout;              /* Descriptor for stdout (for reading);
                                   May be same number as fdin. */
 static int fderr;              /* Descriptor for stderr.  May be -1. */
-static long stdin_bytes = 0;   /* Number of bytes written to stdin. */
-static long stdout_bytes = 0;  /* Number of stdout bytes sent to client. */
-static long stderr_bytes = 0;  /* Number of stderr bytes sent to client. */
-static long fdout_bytes = 0;   /* Number of stdout bytes read from program. */
+static u_long stdin_bytes = 0; /* Number of bytes written to stdin. */
+static u_long stdout_bytes = 0;        /* Number of stdout bytes sent to client. */
+static u_long stderr_bytes = 0;        /* Number of stderr bytes sent to client. */
+static u_long fdout_bytes = 0; /* Number of stdout bytes read from program. */
 static int stdin_eof = 0;      /* EOF message received from client. */
 static int fdout_eof = 0;      /* EOF encountered reading from fdout. */
 static int fderr_eof = 0;      /* EOF encountered readung from fderr. */
@@ -104,7 +105,7 @@ static int connection_in;   /* Connection to client (input). */
 static int connection_out;     /* Connection to client (output). */
 static int connection_closed = 0;      /* Connection to client closed. */
 static u_int buffer_high;      /* "Soft" max buffer size. */
-static int client_alive_timeouts = 0;
+static int no_more_sessions = 0; /* Disallow further sessions. */
 
 /*
  * This SIGCHLD kludge is used to detect when the child exits.  The server
@@ -119,6 +120,20 @@ static volatile sig_atomic_t received_sigterm = 0;
 /* prototypes */
 static void server_init_dispatch(void);
 
+/*
+ * Returns current time in seconds from Jan 1, 1970 with the maximum
+ * available resolution.
+ */
+
+static double
+get_current_time(void)
+{
+       struct timeval tv;
+       gettimeofday(&tv, NULL);
+       return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
+}
+
+
 /*
  * we write to this pipe if a SIGCHLD is caught in order to avoid
  * the race between select() and child_terminated
@@ -248,7 +263,7 @@ 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) {
+       if (++keep_alive_timeouts > options.client_alive_count_max) {
                logit("Timeout, client not responding.");
                cleanup_exit(255);
        }
@@ -280,6 +295,7 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
        struct timeval tv, *tvp;
        int ret;
        int client_alive_scheduled = 0;
+       int program_alive_scheduled = 0;
 
        /*
         * if using client_alive, set the max timeout accordingly,
@@ -317,6 +333,7 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
                 * the client, try to get some more data from the program.
                 */
                if (packet_not_very_much_data_to_write()) {
+                       program_alive_scheduled = child_terminated;
                        if (!fdout_eof)
                                FD_SET(fdout, *readsetp);
                        if (!fderr_eof)
@@ -362,8 +379,16 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
                memset(*writesetp, 0, *nallocp);
                if (errno != EINTR)
                        error("select: %.100s", strerror(errno));
-       } else if (ret == 0 && client_alive_scheduled)
-               client_alive_check();
+       } else {
+               if (ret == 0 && client_alive_scheduled)
+                       client_alive_check();
+               if (!compat20 && program_alive_scheduled && fdin_is_tty) {
+                       if (!fdout_eof)
+                               FD_SET(fdout, *readsetp);
+                       if (!fderr_eof)
+                               FD_SET(fderr, *readsetp);
+               }
+       }
 
        notify_done(*readsetp);
 }
@@ -389,7 +414,8 @@ process_input(fd_set *readset)
                                return;
                        cleanup_exit(255);
                } else if (len < 0) {
-                       if (errno != EINTR && errno != EAGAIN) {
+                       if (errno != EINTR && errno != EAGAIN &&
+                           errno != EWOULDBLOCK) {
                                verbose("Read error from remote host "
                                    "%.100s: %.100s",
                                    get_remote_ipaddr(), strerror(errno));
@@ -398,6 +424,7 @@ process_input(fd_set *readset)
                } else {
                        /* Buffer any received data. */
                        packet_process_incoming(buf, len);
+                       fdout_bytes += len;
                }
        }
        if (compat20)
@@ -407,7 +434,8 @@ process_input(fd_set *readset)
        if (!fdout_eof && FD_ISSET(fdout, readset)) {
                errno = 0;
                len = read(fdout, buf, sizeof(buf));
-               if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
+               if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
+                   errno == EWOULDBLOCK) && !child_terminated))) {
                        /* do nothing */
 #ifndef PTY_ZEROREAD
                } else if (len <= 0) {
@@ -419,13 +447,15 @@ process_input(fd_set *readset)
                } else {
                        buffer_append(&stdout_buffer, buf, len);
                        fdout_bytes += len;
+                       debug ("FD out now: %ld", fdout_bytes);
                }
        }
        /* Read and buffer any available stderr data from the program. */
        if (!fderr_eof && FD_ISSET(fderr, readset)) {
                errno = 0;
                len = read(fderr, buf, sizeof(buf));
-               if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
+               if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
+                   errno == EWOULDBLOCK) && !child_terminated))) {
                        /* do nothing */
 #ifndef PTY_ZEROREAD
                } else if (len <= 0) {
@@ -456,7 +486,8 @@ process_output(fd_set *writeset)
                data = buffer_ptr(&stdin_buffer);
                dlen = buffer_len(&stdin_buffer);
                len = write(fdin, data, dlen);
-               if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
+               if (len < 0 &&
+                   (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
                        /* do nothing */
                } else if (len <= 0) {
                        if (fdin != fdout)
@@ -484,7 +515,7 @@ process_output(fd_set *writeset)
        }
        /* Send any buffered packet data to the client. */
        if (FD_ISSET(connection_out, writeset))
-               packet_write_poll();
+               stdin_bytes += packet_write_poll();
 }
 
 /*
@@ -801,8 +832,10 @@ server_loop2(Authctxt *authctxt)
 {
        fd_set *readset = NULL, *writeset = NULL;
        int rekeying = 0, max_fd, nalloc = 0;
+       double start_time, total_time;
 
        debug("Entering interactive session for SSH2.");
+       start_time = get_current_time();
 
        mysignal(SIGCHLD, sigchld_handler);
        child_terminated = 0;
@@ -864,6 +897,11 @@ server_loop2(Authctxt *authctxt)
 
        /* free remaining sessions, e.g. remove wtmp entries */
        session_destroy_all(NULL);
+       total_time = get_current_time() - start_time;
+       logit("SSH: Server;LType: Throughput;Remote: %s-%d;IN: %lu;OUT: %lu;Duration: %.1f;tPut_in: %.1f;tPut_out: %.1f",
+             get_remote_ipaddr(), get_remote_port(),
+             stdin_bytes, fdout_bytes, total_time, stdin_bytes / total_time, 
+             fdout_bytes / total_time);
 }
 
 static void
@@ -875,7 +913,7 @@ server_input_keep_alive(int type, u_int32_t seq, void *ctxt)
         * even if this was generated by something other than
         * the bogus CHANNEL_REQUEST we send for keepalives.
         */
-       client_alive_timeouts = 0;
+       keep_alive_timeouts = 0;
 }
 
 static void
@@ -926,9 +964,8 @@ static Channel *
 server_request_direct_tcpip(void)
 {
        Channel *c;
-       int sock;
        char *target, *originator;
-       int target_port, originator_port;
+       u_short target_port, originator_port;
 
        target = packet_get_string(NULL);
        target_port = packet_get_int();
@@ -936,23 +973,16 @@ server_request_direct_tcpip(void)
        originator_port = packet_get_int();
        packet_check_eom();
 
-       debug("server_request_direct_tcpip: originator %s port %d, target %s port %d",
-           originator, originator_port, target, target_port);
+       debug("server_request_direct_tcpip: originator %s port %d, target %s "
+           "port %d", originator, originator_port, target, target_port);
 
        /* XXX check permission */
-       sock = channel_connect_to(target, target_port);
-       xfree(target);
+       c = channel_connect_to(target, target_port,
+           "direct-tcpip", "direct-tcpip");
+
        xfree(originator);
-       if (sock < 0)
-               return NULL;
-       if (options.hpn_disabled)
-               c = channel_new("direct-tcpip", SSH_CHANNEL_CONNECTING,
-                   sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT,
-                   CHAN_TCP_PACKET_DEFAULT, 0, "direct-tcpip", 1);
-       else
-               c = channel_new("direct-tcpip", SSH_CHANNEL_CONNECTING,
-                   sock, sock, -1, options.hpn_buffer_size,
-                   CHAN_TCP_PACKET_DEFAULT, 0, "direct-tcpip", 1);
+       xfree(target);
+
        return c;
 }
 
@@ -988,8 +1018,8 @@ server_request_tun(void)
        if (sock < 0)
                goto done;
        if (options.hpn_disabled)
-               c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
-                   CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
+       c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
+           CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
        else
                c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
                    options.hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
@@ -997,7 +1027,7 @@ server_request_tun(void)
 #if defined(SSH_TUN_FILTER)
        if (mode == SSH_TUNMODE_POINTOPOINT)
                channel_register_filter(c->self, sys_tun_infilter,
-                   sys_tun_outfilter);
+                   sys_tun_outfilter, NULL, NULL);
 #endif
 
  done:
@@ -1013,6 +1043,12 @@ server_request_session(void)
 
        debug("input_session_request");
        packet_check_eom();
+
+       if (no_more_sessions) {
+               packet_disconnect("Possible attack: attempt to open a session "
+                   "after additional sessions disabled");
+       }
+
        /*
         * 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
@@ -1022,7 +1058,7 @@ server_request_session(void)
        c = channel_new("session", SSH_CHANNEL_LARVAL,
            -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT,
            0, "server-session", 1);
-       if ((options.tcp_rcv_buf_poll > 0) && (!options.hpn_disabled))
+       if ((options.tcp_rcv_buf_poll) && (!options.hpn_disabled))
                c->dynamic_window = 1;
        if (session_open(the_authctxt, c->self) != 1) {
                debug("session open failed, free channel %d", c->self);
@@ -1088,7 +1124,7 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt)
 {
        char *rtype;
        int want_reply;
-       int success = 0;
+       int success = 0, allocated_listen_port = 0;
 
        rtype = packet_get_string(NULL);
        want_reply = packet_get_char();
@@ -1110,7 +1146,8 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt)
 
                /* check permissions */
                if (!options.allow_tcp_forwarding ||
-                   no_port_forwarding_flag
+                   no_port_forwarding_flag ||
+                   (!want_reply && listen_port == 0)
 #ifndef NO_IPPORT_RESERVED_CONCEPT
                    || (listen_port < IPPORT_RESERVED && pw->pw_uid != 0)
 #endif
@@ -1120,8 +1157,8 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt)
                } else {
                        /* Start listening on the port */
                        success = channel_setup_remote_fwd_listener(
-                           listen_address, listen_port, options.gateway_ports, 
-                           options.hpn_disabled, options.hpn_buffer_size);
+                           listen_address, listen_port,
+                           &allocated_listen_port, options.gateway_ports);
                }
                xfree(listen_address);
        } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) {
@@ -1136,10 +1173,15 @@ server_input_global_request(int type, u_int32_t seq, void *ctxt)
                success = channel_cancel_rport_listener(cancel_address,
                    cancel_port);
                xfree(cancel_address);
+       } else if (strcmp(rtype, "no-more-sessions@openssh.com") == 0) {
+               no_more_sessions = 1;
+               success = 1;
        }
        if (want_reply) {
                packet_start(success ?
                    SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
+               if (success && allocated_listen_port > 0)
+                       packet_put_int(allocated_listen_port);
                packet_send();
                packet_write_wait();
        }
@@ -1163,7 +1205,11 @@ server_input_channel_req(int type, u_int32_t seq, void *ctxt)
        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)
+       if (!strcmp(rtype, "eow@openssh.com")) {
+               packet_check_eom();
+               chan_rcvd_eow(c);
+       } else if ((c->type == SSH_CHANNEL_LARVAL ||
+           c->type == SSH_CHANNEL_OPEN) && strcmp(c->ctype, "session") == 0)
                success = session_input_channel_req(c, rtype);
        if (reply) {
                packet_start(success ?
@@ -1190,6 +1236,7 @@ server_init_dispatch_20(void)
        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_SUCCESS, &server_input_keep_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);
This page took 0.050339 seconds and 4 git commands to generate.