X-Git-Url: http://andersk.mit.edu/gitweb/gssapi-openssh.git/blobdiff_plain/3c0ef6268ff5921062694dbd2cbb80f558aa8d40..4509933ca13b41d35539f3dd9cbcda2d76660cfc:/openssh/serverloop.c diff --git a/openssh/serverloop.c b/openssh/serverloop.c index 0da805f..1331dd8 100644 --- a/openssh/serverloop.c +++ b/openssh/serverloop.c @@ -1,3 +1,4 @@ +/* $OpenBSD: serverloop.c,v 1.148 2008/02/22 20:44:02 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -35,31 +36,54 @@ */ #include "includes.h" -RCSID("$OpenBSD: serverloop.c,v 1.83 2001/11/09 18:59:23 markus Exp $"); + +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include #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" #include "ssh1.h" #include "ssh2.h" +#include "key.h" +#include "cipher.h" +#include "kex.h" +#include "hostfile.h" #include "auth.h" #include "session.h" #include "dispatch.h" #include "auth-options.h" #include "serverloop.h" #include "misc.h" -#include "kex.h" extern ServerOptions options; /* XXX */ extern Kex *xxx_kex; -static Authctxt *xxx_authctxt; +extern Authctxt *the_authctxt; +extern int use_privsep; static Buffer stdin_buffer; /* Buffer for stdin data. */ static Buffer stdout_buffer; /* Buffer for stdout data. */ @@ -68,10 +92,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. */ @@ -80,28 +104,99 @@ 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; /* * 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 volatile int child_terminated; /* The child has terminated. */ +static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */ + +/* Cleanup on signals (!use_privsep case only) */ +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 + */ +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; + } + notify_pipe[0] = -1; /* read end */ + notify_pipe[1] = -1; /* write end */ +} +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"); +} + +/*ARGSUSED*/ static void sigchld_handler(int sig) { int save_errno = errno; - debug("Received SIGCHLD."); child_terminated = 1; +#ifndef _UNICOS mysignal(SIGCHLD, sigchld_handler); +#endif + notify_parent(); errno = save_errno; } +/*ARGSUSED*/ +static void +sigterm_handler(int sig) +{ + received_sigterm = sig; +} + /* * Make packets from buffered stderr data, and buffer it for sending * to the client. @@ -109,7 +204,7 @@ sigchld_handler(int sig) 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 && @@ -138,7 +233,7 @@ make_packets_from_stderr_data(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 && @@ -163,20 +258,25 @@ make_packets_from_stdout_data(void) static void client_alive_check(void) { - int id; + 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."); + if (++keep_alive_timeouts > options.client_alive_count_max) { + logit("Timeout, client not responding."); + cleanup_exit(255); + } - id = channel_find_open(); - if (id == -1) - packet_disconnect("No open channels after timeout!"); /* - * send a bogus channel request with "wantreply", + * send a bogus global/channel request with "wantreply", * we should get back a failure */ - channel_request_start(id, "keepalive@openssh.com", 1); + 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(); } @@ -188,19 +288,20 @@ client_alive_check(void) */ static void wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, - int *nallocp, u_int max_time_milliseconds) + u_int *nallocp, u_int max_time_milliseconds) { 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, + * 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. + * analysis more difficult, but we're not doing it yet. */ if (compat20 && max_time_milliseconds == 0 && options.client_alive_interval) { @@ -230,6 +331,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) @@ -242,6 +344,7 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 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 @@ -265,8 +368,6 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, 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); @@ -276,8 +377,18 @@ 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); } /* @@ -285,7 +396,7 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, * in buffers and processed later. */ static void -process_input(fd_set * readset) +process_input(fd_set *readset) { int len; char buf[16384]; @@ -294,19 +405,23 @@ 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."); + verbose("Connection closed by %.100s", + get_remote_ipaddr()); connection_closed = 1; if (compat20) return; - fatal_cleanup(); + 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. */ packet_process_incoming(buf, len); + fdout_bytes += len; } } if (compat20) @@ -314,22 +429,37 @@ process_input(fd_set * readset) /* Read and buffer any available stdout data from the program. */ 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 && !child_terminated))) { /* do nothing */ +#ifndef PTY_ZEROREAD } else if (len <= 0) { +#else + } else if ((!isatty(fdout) && len <= 0) || + (isatty(fdout) && (len < 0 || (len == 0 && errno != 0)))) { +#endif fdout_eof = 1; } 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 && !child_terminated))) { /* do nothing */ +#ifndef PTY_ZEROREAD } else if (len <= 0) { +#else + } else if ((!isatty(fderr) && len <= 0) || + (isatty(fderr) && (len < 0 || (len == 0 && errno != 0)))) { +#endif fderr_eof = 1; } else { buffer_append(&stderr_buffer, buf, len); @@ -341,7 +471,7 @@ process_input(fd_set * readset) * Sends data from internal buffers to client program stdin. */ static void -process_output(fd_set * writeset) +process_output(fd_set *writeset) { struct termios tio; u_char *data; @@ -356,14 +486,10 @@ process_output(fd_set * writeset) 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. */ @@ -385,7 +511,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(); } /* @@ -434,7 +560,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 = 0, nalloc = 0; + 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. */ @@ -449,6 +576,12 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) child_terminated = 0; 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; fdout = fdout_arg; @@ -467,6 +600,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. */ @@ -493,7 +628,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; @@ -511,14 +646,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. */ @@ -572,11 +703,18 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) 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, &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); @@ -597,7 +735,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); @@ -622,12 +760,12 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) /* We no longer want our SIGCHLD handler to be called. */ mysignal(SIGCHLD, SIG_DFL); - wait_pid = waitpid(-1, &wait_status, child_terminated ? WNOHANG : 0); - if (wait_pid == -1) - packet_disconnect("wait: %.100s", strerror(errno)); - else if (wait_pid != pid) - error("Strange, wait returned pid %d, expected %d", - wait_pid, pid); + 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)) { @@ -646,8 +784,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); @@ -676,8 +813,11 @@ collect_children(void) sigaddset(&nset, SIGCHLD); sigprocmask(SIG_BLOCK, &nset, &oset); if (child_terminated) { - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) - session_close_by_pid(pid, status); + debug("Received SIGCHLD."); + 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); @@ -688,16 +828,26 @@ 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; 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); - xxx_authctxt = authctxt; + max_fd = MAX(max_fd, notify_pipe[0]); server_init_dispatch(); @@ -711,9 +861,21 @@ server_loop2(Authctxt *authctxt) 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) + if (!rekeying) { channel_after_select(readset, writeset); + if (packet_need_rekeying()) { + debug("need rekeying"); + xxx_kex->done = 0; + kex_send_kexinit(xxx_kex); + } + } process_input(readset); if (connection_closed) break; @@ -730,24 +892,28 @@ server_loop2(Authctxt *authctxt) channel_free_all(); /* free remaining sessions, e.g. remove wtmp entries */ - session_destroy_all(); + 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 -server_input_channel_failure(int type, int plen, void *ctxt) +server_input_keep_alive(int type, u_int32_t seq, void *ctxt) { - debug("Got CHANNEL_FAILURE for keepalive"); - /* + 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; + keep_alive_timeouts = 0; } - static void -server_input_stdin_data(int type, int plen, void *ctxt) +server_input_stdin_data(int type, u_int32_t seq, void *ctxt) { char *data; u_int data_len; @@ -757,14 +923,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); } static void -server_input_eof(int type, int plen, void *ctxt) +server_input_eof(int type, u_int32_t seq, void *ctxt) { /* * Eof from the client. The stdin descriptor to the @@ -772,26 +938,26 @@ 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; } static void -server_input_window_size(int type, int plen, void *ctxt) +server_input_window_size(int type, u_int32_t seq, void *ctxt) { - int row = packet_get_int(); - int col = packet_get_int(); - int xpixel = packet_get_int(); - int ypixel = packet_get_int(); + u_int row = packet_get_int(); + u_int col = packet_get_int(); + u_int xpixel = packet_get_int(); + u_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); } static Channel * -server_request_direct_tcpip(char *ctype) +server_request_direct_tcpip(void) { Channel *c; int sock; @@ -802,10 +968,10 @@ 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 */ sock = channel_connect_to(target, target_port); @@ -813,56 +979,101 @@ server_request_direct_tcpip(char *ctype) xfree(originator); if (sock < 0) return NULL; - c = channel_new(ctype, SSH_CHANNEL_CONNECTING, + if (options.hpn_disabled) + c = channel_new("direct-tcpip", SSH_CHANNEL_CONNECTING, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, - CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip"), 1); - if (c == NULL) { - error("server_request_direct_tcpip: channel_new failed"); - close(sock); + 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); + return c; +} + +static Channel * +server_request_tun(void) +{ + Channel *c = NULL; + int mode, tun; + int sock; + + mode = packet_get_int(); + switch (mode) { + case SSH_TUNMODE_POINTOPOINT: + case SSH_TUNMODE_ETHERNET: + break; + default: + packet_send_debug("Unsupported tunnel device mode."); + return NULL; + } + if ((options.permit_tun & mode) == 0) { + packet_send_debug("Server has rejected tunnel device " + "forwarding"); + return NULL; } + + tun = packet_get_int(); + if (forced_tun_device != -1) { + if (tun != SSH_TUNID_ANY && forced_tun_device != tun) + goto done; + tun = forced_tun_device; + } + sock = tun_open(tun, mode); + 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); + else + c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1, + options.hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); + c->datagram = 1; +#if defined(SSH_TUN_FILTER) + if (mode == SSH_TUNMODE_POINTOPOINT) + channel_register_filter(c->self, sys_tun_infilter, + sys_tun_outfilter); +#endif + + done: + if (c == NULL) + packet_send_debug("Failed to open the tunnel device."); return c; } static Channel * -server_request_session(char *ctype) +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. */ - c = channel_new(ctype, SSH_CHANNEL_LARVAL, + c = channel_new("session", SSH_CHANNEL_LARVAL, -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, - 0, xstrdup("server-session"), 1); - if (c == NULL) { - error("server_request_session: channel_new failed"); - return NULL; - } - if (session_open(xxx_authctxt, c->self) != 1) { + 0, "server-session", 1); + if ((options.tcp_rcv_buf_poll > 0) && (!options.hpn_disabled)) + c->dynamic_window = 1; + if (session_open(the_authctxt, c->self) != 1) { debug("session open failed, free channel %d", c->self); channel_free(c); return NULL; } - channel_register_callback(c->self, SSH2_MSG_CHANNEL_REQUEST, - session_input_channel_req, (void *)0); - channel_register_cleanup(c->self, session_close_by_channel); + channel_register_cleanup(c->self, session_close_by_channel, 0); return c; } static void -server_input_channel_open(int type, int plen, void *ctxt) +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(); @@ -873,9 +1084,11 @@ 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); @@ -905,7 +1118,7 @@ server_input_channel_open(int type, int plen, void *ctxt) } static void -server_input_global_request(int type, int plen, void *ctxt) +server_input_global_request(int type, u_int32_t seq, void *ctxt) { char *rtype; int want_reply; @@ -921,29 +1134,42 @@ server_input_global_request(int type, int plen, void *ctxt) 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 port_to_connect*/ 0, - options.gateway_ports, /*remote*/ 1); + success = channel_setup_remote_fwd_listener( + listen_address, listen_port, options.gateway_ports, + options.hpn_disabled, options.hpn_buffer_size); } 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); + xfree(cancel_address); } if (want_reply) { packet_start(success ? @@ -954,6 +1180,34 @@ 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; + + 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) { @@ -966,11 +1220,13 @@ 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_channel_failure); + 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); }