17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * Author: Tatu Ylonen <ylo@cs.hut.fi>
37c478bd9Sstevel@tonic-gate * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
47c478bd9Sstevel@tonic-gate * All rights reserved
57c478bd9Sstevel@tonic-gate * Server main loop for handling the interactive session.
67c478bd9Sstevel@tonic-gate *
77c478bd9Sstevel@tonic-gate * As far as I am concerned, the code I have written for this software
87c478bd9Sstevel@tonic-gate * can be used freely for any purpose. Any derived versions of this
97c478bd9Sstevel@tonic-gate * software must be clearly marked as such, and if the derived work is
107c478bd9Sstevel@tonic-gate * incompatible with the protocol description in the RFC file, it must be
117c478bd9Sstevel@tonic-gate * called by a name other than "ssh" or "Secure Shell".
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * SSH2 support by Markus Friedl.
147c478bd9Sstevel@tonic-gate * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
157c478bd9Sstevel@tonic-gate *
167c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without
177c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions
187c478bd9Sstevel@tonic-gate * are met:
197c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright
207c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer.
217c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright
227c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the
237c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution.
247c478bd9Sstevel@tonic-gate *
257c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
267c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
277c478bd9Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
287c478bd9Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
297c478bd9Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
307c478bd9Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
317c478bd9Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
327c478bd9Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
337c478bd9Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
347c478bd9Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
357c478bd9Sstevel@tonic-gate */
367c478bd9Sstevel@tonic-gate /*
376f786aceSNobutomo Nakano * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
387c478bd9Sstevel@tonic-gate * Use is subject to license terms.
397c478bd9Sstevel@tonic-gate */
407c478bd9Sstevel@tonic-gate
417c478bd9Sstevel@tonic-gate #include "includes.h"
427c478bd9Sstevel@tonic-gate RCSID("$OpenBSD: serverloop.c,v 1.104 2002/09/19 16:03:15 stevesk Exp $");
437c478bd9Sstevel@tonic-gate
447c478bd9Sstevel@tonic-gate #include "xmalloc.h"
457c478bd9Sstevel@tonic-gate #include "packet.h"
467c478bd9Sstevel@tonic-gate #include "buffer.h"
477c478bd9Sstevel@tonic-gate #include "log.h"
487c478bd9Sstevel@tonic-gate #include "servconf.h"
497c478bd9Sstevel@tonic-gate #include "canohost.h"
507c478bd9Sstevel@tonic-gate #include "sshpty.h"
517c478bd9Sstevel@tonic-gate #include "channels.h"
527c478bd9Sstevel@tonic-gate #include "compat.h"
537c478bd9Sstevel@tonic-gate #include "ssh1.h"
547c478bd9Sstevel@tonic-gate #include "ssh2.h"
557c478bd9Sstevel@tonic-gate #include "auth.h"
567c478bd9Sstevel@tonic-gate #include "session.h"
577c478bd9Sstevel@tonic-gate #include "dispatch.h"
587c478bd9Sstevel@tonic-gate #include "auth-options.h"
597c478bd9Sstevel@tonic-gate #include "serverloop.h"
607c478bd9Sstevel@tonic-gate #include "misc.h"
617c478bd9Sstevel@tonic-gate #include "kex.h"
627c478bd9Sstevel@tonic-gate
637c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP
647c478bd9Sstevel@tonic-gate #include "altprivsep.h"
657c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP*/
667c478bd9Sstevel@tonic-gate
677c478bd9Sstevel@tonic-gate extern ServerOptions options;
687c478bd9Sstevel@tonic-gate
697c478bd9Sstevel@tonic-gate /* XXX */
707c478bd9Sstevel@tonic-gate extern Kex *xxx_kex;
717c478bd9Sstevel@tonic-gate static Authctxt *xxx_authctxt;
727c478bd9Sstevel@tonic-gate
737c478bd9Sstevel@tonic-gate static Buffer stdin_buffer; /* Buffer for stdin data. */
747c478bd9Sstevel@tonic-gate static Buffer stdout_buffer; /* Buffer for stdout data. */
757c478bd9Sstevel@tonic-gate static Buffer stderr_buffer; /* Buffer for stderr data. */
767c478bd9Sstevel@tonic-gate static int fdin; /* Descriptor for stdin (for writing) */
777c478bd9Sstevel@tonic-gate static int fdout; /* Descriptor for stdout (for reading);
787c478bd9Sstevel@tonic-gate May be same number as fdin. */
797c478bd9Sstevel@tonic-gate static int fderr; /* Descriptor for stderr. May be -1. */
807c478bd9Sstevel@tonic-gate static long stdin_bytes = 0; /* Number of bytes written to stdin. */
817c478bd9Sstevel@tonic-gate static long stdout_bytes = 0; /* Number of stdout bytes sent to client. */
827c478bd9Sstevel@tonic-gate static long stderr_bytes = 0; /* Number of stderr bytes sent to client. */
837c478bd9Sstevel@tonic-gate static long fdout_bytes = 0; /* Number of stdout bytes read from program. */
847c478bd9Sstevel@tonic-gate static int stdin_eof = 0; /* EOF message received from client. */
857c478bd9Sstevel@tonic-gate static int fdout_eof = 0; /* EOF encountered reading from fdout. */
867c478bd9Sstevel@tonic-gate static int fderr_eof = 0; /* EOF encountered readung from fderr. */
877c478bd9Sstevel@tonic-gate static int fdin_is_tty = 0; /* fdin points to a tty. */
887c478bd9Sstevel@tonic-gate static int connection_in; /* Connection to client (input). */
897c478bd9Sstevel@tonic-gate static int connection_out; /* Connection to client (output). */
907c478bd9Sstevel@tonic-gate static int connection_closed = 0; /* Connection to client closed. */
917c478bd9Sstevel@tonic-gate static u_int buffer_high; /* "Soft" max buffer size. */
927c478bd9Sstevel@tonic-gate static int client_alive_timeouts = 0;
937c478bd9Sstevel@tonic-gate
947c478bd9Sstevel@tonic-gate /*
957c478bd9Sstevel@tonic-gate * This SIGCHLD kludge is used to detect when the child exits. The server
967c478bd9Sstevel@tonic-gate * will exit after that, as soon as forwarded connections have terminated.
977c478bd9Sstevel@tonic-gate */
987c478bd9Sstevel@tonic-gate
997c478bd9Sstevel@tonic-gate static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */
1007c478bd9Sstevel@tonic-gate
1017c478bd9Sstevel@tonic-gate /* prototypes */
1027c478bd9Sstevel@tonic-gate static void server_init_dispatch(void);
1037c478bd9Sstevel@tonic-gate
1047c478bd9Sstevel@tonic-gate /*
1057c478bd9Sstevel@tonic-gate * we write to this pipe if a SIGCHLD is caught in order to avoid
1067c478bd9Sstevel@tonic-gate * the race between select() and child_terminated
1077c478bd9Sstevel@tonic-gate */
1087c478bd9Sstevel@tonic-gate static int notify_pipe[2];
1097c478bd9Sstevel@tonic-gate static void
notify_setup(void)1107c478bd9Sstevel@tonic-gate notify_setup(void)
1117c478bd9Sstevel@tonic-gate {
1127c478bd9Sstevel@tonic-gate if (pipe(notify_pipe) < 0) {
1137c478bd9Sstevel@tonic-gate error("pipe(notify_pipe) failed %s", strerror(errno));
114b9aa66a7SJan Pechanec } else if ((fcntl(notify_pipe[0], F_SETFD, FD_CLOEXEC) == -1) ||
115b9aa66a7SJan Pechanec (fcntl(notify_pipe[1], F_SETFD, FD_CLOEXEC) == -1)) {
1167c478bd9Sstevel@tonic-gate error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno));
1177c478bd9Sstevel@tonic-gate (void) close(notify_pipe[0]);
1187c478bd9Sstevel@tonic-gate (void) close(notify_pipe[1]);
1197c478bd9Sstevel@tonic-gate } else {
1207c478bd9Sstevel@tonic-gate set_nonblock(notify_pipe[0]);
1217c478bd9Sstevel@tonic-gate set_nonblock(notify_pipe[1]);
1227c478bd9Sstevel@tonic-gate return;
1237c478bd9Sstevel@tonic-gate }
1247c478bd9Sstevel@tonic-gate notify_pipe[0] = -1; /* read end */
1257c478bd9Sstevel@tonic-gate notify_pipe[1] = -1; /* write end */
1267c478bd9Sstevel@tonic-gate }
1277c478bd9Sstevel@tonic-gate static void
notify_parent(void)1287c478bd9Sstevel@tonic-gate notify_parent(void)
1297c478bd9Sstevel@tonic-gate {
1307c478bd9Sstevel@tonic-gate if (notify_pipe[1] != -1)
1317c478bd9Sstevel@tonic-gate (void) write(notify_pipe[1], "", 1);
1327c478bd9Sstevel@tonic-gate }
1337c478bd9Sstevel@tonic-gate static void
notify_prepare(fd_set * readset)1347c478bd9Sstevel@tonic-gate notify_prepare(fd_set *readset)
1357c478bd9Sstevel@tonic-gate {
1367c478bd9Sstevel@tonic-gate if (notify_pipe[0] != -1)
1377c478bd9Sstevel@tonic-gate FD_SET(notify_pipe[0], readset);
1387c478bd9Sstevel@tonic-gate }
1397c478bd9Sstevel@tonic-gate static void
notify_done(fd_set * readset)1407c478bd9Sstevel@tonic-gate notify_done(fd_set *readset)
1417c478bd9Sstevel@tonic-gate {
1427c478bd9Sstevel@tonic-gate char c;
1437c478bd9Sstevel@tonic-gate
1447c478bd9Sstevel@tonic-gate if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset))
1457c478bd9Sstevel@tonic-gate while (read(notify_pipe[0], &c, 1) != -1)
1467c478bd9Sstevel@tonic-gate debug2("notify_done: reading");
1477c478bd9Sstevel@tonic-gate }
1487c478bd9Sstevel@tonic-gate
1497c478bd9Sstevel@tonic-gate static void
sigchld_handler(int sig)1507c478bd9Sstevel@tonic-gate sigchld_handler(int sig)
1517c478bd9Sstevel@tonic-gate {
1527c478bd9Sstevel@tonic-gate int save_errno = errno;
1537c478bd9Sstevel@tonic-gate debug("Received SIGCHLD.");
1547c478bd9Sstevel@tonic-gate child_terminated = 1;
1557c478bd9Sstevel@tonic-gate #ifndef _UNICOS
1567c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, sigchld_handler);
1577c478bd9Sstevel@tonic-gate #endif
1587c478bd9Sstevel@tonic-gate notify_parent();
1597c478bd9Sstevel@tonic-gate errno = save_errno;
1607c478bd9Sstevel@tonic-gate }
1617c478bd9Sstevel@tonic-gate
1627c478bd9Sstevel@tonic-gate /*
1637c478bd9Sstevel@tonic-gate * Make packets from buffered stderr data, and buffer it for sending
1647c478bd9Sstevel@tonic-gate * to the client.
1657c478bd9Sstevel@tonic-gate */
1667c478bd9Sstevel@tonic-gate static void
make_packets_from_stderr_data(void)1677c478bd9Sstevel@tonic-gate make_packets_from_stderr_data(void)
1687c478bd9Sstevel@tonic-gate {
1697c478bd9Sstevel@tonic-gate int len;
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate /* Send buffered stderr data to the client. */
1727c478bd9Sstevel@tonic-gate while (buffer_len(&stderr_buffer) > 0 &&
1737c478bd9Sstevel@tonic-gate packet_not_very_much_data_to_write()) {
1747c478bd9Sstevel@tonic-gate len = buffer_len(&stderr_buffer);
1757c478bd9Sstevel@tonic-gate if (packet_is_interactive()) {
1767c478bd9Sstevel@tonic-gate if (len > 512)
1777c478bd9Sstevel@tonic-gate len = 512;
1787c478bd9Sstevel@tonic-gate } else {
1797c478bd9Sstevel@tonic-gate /* Keep the packets at reasonable size. */
1807c478bd9Sstevel@tonic-gate if (len > packet_get_maxsize())
1817c478bd9Sstevel@tonic-gate len = packet_get_maxsize();
1827c478bd9Sstevel@tonic-gate }
1837c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDERR_DATA);
1847c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stderr_buffer), len);
1857c478bd9Sstevel@tonic-gate packet_send();
1867c478bd9Sstevel@tonic-gate buffer_consume(&stderr_buffer, len);
1877c478bd9Sstevel@tonic-gate stderr_bytes += len;
1887c478bd9Sstevel@tonic-gate }
1897c478bd9Sstevel@tonic-gate }
1907c478bd9Sstevel@tonic-gate
1917c478bd9Sstevel@tonic-gate /*
1927c478bd9Sstevel@tonic-gate * Make packets from buffered stdout data, and buffer it for sending to the
1937c478bd9Sstevel@tonic-gate * client.
1947c478bd9Sstevel@tonic-gate */
1957c478bd9Sstevel@tonic-gate static void
make_packets_from_stdout_data(void)1967c478bd9Sstevel@tonic-gate make_packets_from_stdout_data(void)
1977c478bd9Sstevel@tonic-gate {
1987c478bd9Sstevel@tonic-gate int len;
1997c478bd9Sstevel@tonic-gate
2007c478bd9Sstevel@tonic-gate /* Send buffered stdout data to the client. */
2017c478bd9Sstevel@tonic-gate while (buffer_len(&stdout_buffer) > 0 &&
2027c478bd9Sstevel@tonic-gate packet_not_very_much_data_to_write()) {
2037c478bd9Sstevel@tonic-gate len = buffer_len(&stdout_buffer);
2047c478bd9Sstevel@tonic-gate if (packet_is_interactive()) {
2057c478bd9Sstevel@tonic-gate if (len > 512)
2067c478bd9Sstevel@tonic-gate len = 512;
2077c478bd9Sstevel@tonic-gate } else {
2087c478bd9Sstevel@tonic-gate /* Keep the packets at reasonable size. */
2097c478bd9Sstevel@tonic-gate if (len > packet_get_maxsize())
2107c478bd9Sstevel@tonic-gate len = packet_get_maxsize();
2117c478bd9Sstevel@tonic-gate }
2127c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDOUT_DATA);
2137c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stdout_buffer), len);
2147c478bd9Sstevel@tonic-gate packet_send();
2157c478bd9Sstevel@tonic-gate buffer_consume(&stdout_buffer, len);
2167c478bd9Sstevel@tonic-gate stdout_bytes += len;
2177c478bd9Sstevel@tonic-gate }
2187c478bd9Sstevel@tonic-gate }
2197c478bd9Sstevel@tonic-gate
2207c478bd9Sstevel@tonic-gate static void
client_alive_check(void)2217c478bd9Sstevel@tonic-gate client_alive_check(void)
2227c478bd9Sstevel@tonic-gate {
2237c478bd9Sstevel@tonic-gate static int had_channel = 0;
2247c478bd9Sstevel@tonic-gate int id;
2257c478bd9Sstevel@tonic-gate
2267c478bd9Sstevel@tonic-gate id = channel_find_open();
2277c478bd9Sstevel@tonic-gate if (id == -1) {
2287c478bd9Sstevel@tonic-gate if (!had_channel)
2297c478bd9Sstevel@tonic-gate return;
2307c478bd9Sstevel@tonic-gate packet_disconnect("No open channels after timeout!");
2317c478bd9Sstevel@tonic-gate }
2327c478bd9Sstevel@tonic-gate had_channel = 1;
2337c478bd9Sstevel@tonic-gate
2347c478bd9Sstevel@tonic-gate /* timeout, check to see how many we have had */
2357c478bd9Sstevel@tonic-gate if (++client_alive_timeouts > options.client_alive_count_max)
2367c478bd9Sstevel@tonic-gate packet_disconnect("Timeout, your session not responding.");
2377c478bd9Sstevel@tonic-gate
2387c478bd9Sstevel@tonic-gate /*
2397c478bd9Sstevel@tonic-gate * send a bogus channel request with "wantreply",
2407c478bd9Sstevel@tonic-gate * we should get back a failure
2417c478bd9Sstevel@tonic-gate */
2427c478bd9Sstevel@tonic-gate channel_request_start(id, "keepalive@openssh.com", 1);
2437c478bd9Sstevel@tonic-gate packet_send();
2447c478bd9Sstevel@tonic-gate }
2457c478bd9Sstevel@tonic-gate
2467c478bd9Sstevel@tonic-gate /*
2477c478bd9Sstevel@tonic-gate * Sleep in select() until we can do something. This will initialize the
2487c478bd9Sstevel@tonic-gate * select masks. Upon return, the masks will indicate which descriptors
2497c478bd9Sstevel@tonic-gate * have data or can accept data. Optionally, a maximum time can be specified
2507c478bd9Sstevel@tonic-gate * for the duration of the wait (0 = infinite).
2517c478bd9Sstevel@tonic-gate */
2527c478bd9Sstevel@tonic-gate static void
wait_until_can_do_something(fd_set ** readsetp,fd_set ** writesetp,int * maxfdp,int * nallocp,u_int max_time_milliseconds)2537c478bd9Sstevel@tonic-gate wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
2547c478bd9Sstevel@tonic-gate int *nallocp, u_int max_time_milliseconds)
2557c478bd9Sstevel@tonic-gate {
2567c478bd9Sstevel@tonic-gate struct timeval tv, *tvp;
2577c478bd9Sstevel@tonic-gate int ret;
2587c478bd9Sstevel@tonic-gate int client_alive_scheduled = 0;
2597c478bd9Sstevel@tonic-gate
2607c478bd9Sstevel@tonic-gate /*
2617c478bd9Sstevel@tonic-gate * if using client_alive, set the max timeout accordingly,
2627c478bd9Sstevel@tonic-gate * and indicate that this particular timeout was for client
2637c478bd9Sstevel@tonic-gate * alive by setting the client_alive_scheduled flag.
2647c478bd9Sstevel@tonic-gate *
2657c478bd9Sstevel@tonic-gate * this could be randomized somewhat to make traffic
2667c478bd9Sstevel@tonic-gate * analysis more difficult, but we're not doing it yet.
2677c478bd9Sstevel@tonic-gate */
2687c478bd9Sstevel@tonic-gate if (compat20 &&
2697c478bd9Sstevel@tonic-gate max_time_milliseconds == 0 && options.client_alive_interval) {
2707c478bd9Sstevel@tonic-gate client_alive_scheduled = 1;
2717c478bd9Sstevel@tonic-gate max_time_milliseconds = options.client_alive_interval * 1000;
2727c478bd9Sstevel@tonic-gate }
2737c478bd9Sstevel@tonic-gate
2747c478bd9Sstevel@tonic-gate /* Allocate and update select() masks for channel descriptors. */
2757c478bd9Sstevel@tonic-gate channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 0);
2767c478bd9Sstevel@tonic-gate
2777c478bd9Sstevel@tonic-gate if (compat20) {
2787c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP
2797c478bd9Sstevel@tonic-gate int pipe_fd;
2807c478bd9Sstevel@tonic-gate
2817c478bd9Sstevel@tonic-gate if ((pipe_fd = altprivsep_get_pipe_fd()) != -1) {
2827c478bd9Sstevel@tonic-gate *maxfdp = MAX(*maxfdp, pipe_fd);
2837c478bd9Sstevel@tonic-gate FD_SET(altprivsep_get_pipe_fd(), *readsetp);
2847c478bd9Sstevel@tonic-gate }
2857c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */
2867c478bd9Sstevel@tonic-gate #if 0
2877c478bd9Sstevel@tonic-gate /* wrong: bad condition XXX */
2887c478bd9Sstevel@tonic-gate if (channel_not_very_much_buffered_data())
2897c478bd9Sstevel@tonic-gate #endif
2907c478bd9Sstevel@tonic-gate FD_SET(connection_in, *readsetp);
2917c478bd9Sstevel@tonic-gate } else {
2927c478bd9Sstevel@tonic-gate /*
2937c478bd9Sstevel@tonic-gate * Read packets from the client unless we have too much
2947c478bd9Sstevel@tonic-gate * buffered stdin or channel data.
2957c478bd9Sstevel@tonic-gate */
2967c478bd9Sstevel@tonic-gate if (buffer_len(&stdin_buffer) < buffer_high &&
2977c478bd9Sstevel@tonic-gate channel_not_very_much_buffered_data())
2987c478bd9Sstevel@tonic-gate FD_SET(connection_in, *readsetp);
2997c478bd9Sstevel@tonic-gate /*
3007c478bd9Sstevel@tonic-gate * If there is not too much data already buffered going to
3017c478bd9Sstevel@tonic-gate * the client, try to get some more data from the program.
3027c478bd9Sstevel@tonic-gate */
3037c478bd9Sstevel@tonic-gate if (packet_not_very_much_data_to_write()) {
3047c478bd9Sstevel@tonic-gate if (!fdout_eof)
3057c478bd9Sstevel@tonic-gate FD_SET(fdout, *readsetp);
3067c478bd9Sstevel@tonic-gate if (!fderr_eof)
3077c478bd9Sstevel@tonic-gate FD_SET(fderr, *readsetp);
3087c478bd9Sstevel@tonic-gate }
3097c478bd9Sstevel@tonic-gate /*
3107c478bd9Sstevel@tonic-gate * If we have buffered data, try to write some of that data
3117c478bd9Sstevel@tonic-gate * to the program.
3127c478bd9Sstevel@tonic-gate */
3137c478bd9Sstevel@tonic-gate if (fdin != -1 && buffer_len(&stdin_buffer) > 0)
3147c478bd9Sstevel@tonic-gate FD_SET(fdin, *writesetp);
3157c478bd9Sstevel@tonic-gate }
3167c478bd9Sstevel@tonic-gate notify_prepare(*readsetp);
3177c478bd9Sstevel@tonic-gate
3187c478bd9Sstevel@tonic-gate /*
3197c478bd9Sstevel@tonic-gate * If we have buffered packet data going to the client, mark that
3207c478bd9Sstevel@tonic-gate * descriptor.
3217c478bd9Sstevel@tonic-gate */
3227c478bd9Sstevel@tonic-gate if (packet_have_data_to_write())
3237c478bd9Sstevel@tonic-gate FD_SET(connection_out, *writesetp);
3247c478bd9Sstevel@tonic-gate
3257c478bd9Sstevel@tonic-gate /*
3267c478bd9Sstevel@tonic-gate * If child has terminated and there is enough buffer space to read
3277c478bd9Sstevel@tonic-gate * from it, then read as much as is available and exit.
3287c478bd9Sstevel@tonic-gate */
3297c478bd9Sstevel@tonic-gate if (child_terminated && packet_not_very_much_data_to_write())
3307c478bd9Sstevel@tonic-gate if (max_time_milliseconds == 0 || client_alive_scheduled)
3317c478bd9Sstevel@tonic-gate max_time_milliseconds = 100;
3327c478bd9Sstevel@tonic-gate
3337c478bd9Sstevel@tonic-gate if (max_time_milliseconds == 0)
3347c478bd9Sstevel@tonic-gate tvp = NULL;
3357c478bd9Sstevel@tonic-gate else {
3367c478bd9Sstevel@tonic-gate tv.tv_sec = max_time_milliseconds / 1000;
3377c478bd9Sstevel@tonic-gate tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
3387c478bd9Sstevel@tonic-gate tvp = &tv;
3397c478bd9Sstevel@tonic-gate }
3407c478bd9Sstevel@tonic-gate
3417c478bd9Sstevel@tonic-gate /* Wait for something to happen, or the timeout to expire. */
3427c478bd9Sstevel@tonic-gate ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
3437c478bd9Sstevel@tonic-gate
3447c478bd9Sstevel@tonic-gate if (ret == -1) {
3457c478bd9Sstevel@tonic-gate memset(*readsetp, 0, *nallocp);
3467c478bd9Sstevel@tonic-gate memset(*writesetp, 0, *nallocp);
3477c478bd9Sstevel@tonic-gate if (errno != EINTR)
3487c478bd9Sstevel@tonic-gate error("select: %.100s", strerror(errno));
3497c478bd9Sstevel@tonic-gate } else if (ret == 0 && client_alive_scheduled)
3507c478bd9Sstevel@tonic-gate client_alive_check();
3517c478bd9Sstevel@tonic-gate
3527c478bd9Sstevel@tonic-gate notify_done(*readsetp);
3537c478bd9Sstevel@tonic-gate }
3547c478bd9Sstevel@tonic-gate
3557c478bd9Sstevel@tonic-gate /*
3567c478bd9Sstevel@tonic-gate * Processes input from the client and the program. Input data is stored
3577c478bd9Sstevel@tonic-gate * in buffers and processed later.
3587c478bd9Sstevel@tonic-gate */
3597c478bd9Sstevel@tonic-gate static void
process_input(fd_set * readset)3607c478bd9Sstevel@tonic-gate process_input(fd_set * readset)
3617c478bd9Sstevel@tonic-gate {
3627c478bd9Sstevel@tonic-gate int len;
3637c478bd9Sstevel@tonic-gate char buf[16384];
3647c478bd9Sstevel@tonic-gate
3657c478bd9Sstevel@tonic-gate /* Read and buffer any input data from the client. */
3667c478bd9Sstevel@tonic-gate if (FD_ISSET(connection_in, readset)) {
3677c478bd9Sstevel@tonic-gate len = read(connection_in, buf, sizeof(buf));
3687c478bd9Sstevel@tonic-gate if (len == 0) {
369cd7d5fafSJan Pechanec if (packet_is_monitor()) {
370cd7d5fafSJan Pechanec debug("child closed the communication pipe");
371cd7d5fafSJan Pechanec } else {
3727c478bd9Sstevel@tonic-gate verbose("Connection closed by %.100s",
3737c478bd9Sstevel@tonic-gate get_remote_ipaddr());
374cd7d5fafSJan Pechanec }
3757c478bd9Sstevel@tonic-gate connection_closed = 1;
3767c478bd9Sstevel@tonic-gate if (compat20)
3777c478bd9Sstevel@tonic-gate return;
3787c478bd9Sstevel@tonic-gate fatal_cleanup();
3797c478bd9Sstevel@tonic-gate } else if (len < 0) {
3807c478bd9Sstevel@tonic-gate if (errno != EINTR && errno != EAGAIN) {
3817c478bd9Sstevel@tonic-gate verbose("Read error from remote host "
3827c478bd9Sstevel@tonic-gate "%.100s: %.100s",
3837c478bd9Sstevel@tonic-gate get_remote_ipaddr(), strerror(errno));
3847c478bd9Sstevel@tonic-gate fatal_cleanup();
3857c478bd9Sstevel@tonic-gate }
3867c478bd9Sstevel@tonic-gate } else {
3877c478bd9Sstevel@tonic-gate /* Buffer any received data. */
3887c478bd9Sstevel@tonic-gate packet_process_incoming(buf, len);
3897c478bd9Sstevel@tonic-gate }
3907c478bd9Sstevel@tonic-gate }
3917c478bd9Sstevel@tonic-gate if (compat20)
3927c478bd9Sstevel@tonic-gate return;
3937c478bd9Sstevel@tonic-gate
3947c478bd9Sstevel@tonic-gate /* Read and buffer any available stdout data from the program. */
3957c478bd9Sstevel@tonic-gate if (!fdout_eof && FD_ISSET(fdout, readset)) {
3967c478bd9Sstevel@tonic-gate len = read(fdout, buf, sizeof(buf));
3977c478bd9Sstevel@tonic-gate if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
3987c478bd9Sstevel@tonic-gate /* EMPTY */
3997c478bd9Sstevel@tonic-gate } else if (len <= 0) {
4007c478bd9Sstevel@tonic-gate fdout_eof = 1;
4017c478bd9Sstevel@tonic-gate } else {
4027c478bd9Sstevel@tonic-gate buffer_append(&stdout_buffer, buf, len);
4037c478bd9Sstevel@tonic-gate fdout_bytes += len;
4047c478bd9Sstevel@tonic-gate }
4057c478bd9Sstevel@tonic-gate }
4067c478bd9Sstevel@tonic-gate /* Read and buffer any available stderr data from the program. */
4077c478bd9Sstevel@tonic-gate if (!fderr_eof && FD_ISSET(fderr, readset)) {
4087c478bd9Sstevel@tonic-gate len = read(fderr, buf, sizeof(buf));
4097c478bd9Sstevel@tonic-gate if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
4107c478bd9Sstevel@tonic-gate /* EMPTY */
4117c478bd9Sstevel@tonic-gate } else if (len <= 0) {
4127c478bd9Sstevel@tonic-gate fderr_eof = 1;
4137c478bd9Sstevel@tonic-gate } else {
4147c478bd9Sstevel@tonic-gate buffer_append(&stderr_buffer, buf, len);
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate }
4177c478bd9Sstevel@tonic-gate }
4187c478bd9Sstevel@tonic-gate
4197c478bd9Sstevel@tonic-gate /*
4207c478bd9Sstevel@tonic-gate * Sends data from internal buffers to client program stdin.
4217c478bd9Sstevel@tonic-gate */
4227c478bd9Sstevel@tonic-gate static void
process_output(fd_set * writeset)4237c478bd9Sstevel@tonic-gate process_output(fd_set * writeset)
4247c478bd9Sstevel@tonic-gate {
4257c478bd9Sstevel@tonic-gate struct termios tio;
4267c478bd9Sstevel@tonic-gate u_char *data;
4277c478bd9Sstevel@tonic-gate u_int dlen;
4287c478bd9Sstevel@tonic-gate int len;
4297c478bd9Sstevel@tonic-gate
4307c478bd9Sstevel@tonic-gate /* Write buffered data to program stdin. */
4317c478bd9Sstevel@tonic-gate if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) {
4327c478bd9Sstevel@tonic-gate data = buffer_ptr(&stdin_buffer);
4337c478bd9Sstevel@tonic-gate dlen = buffer_len(&stdin_buffer);
4347c478bd9Sstevel@tonic-gate len = write(fdin, data, dlen);
4357c478bd9Sstevel@tonic-gate if (len < 0 && (errno == EINTR || errno == EAGAIN)) {
4367c478bd9Sstevel@tonic-gate /* EMPTY */
4377c478bd9Sstevel@tonic-gate } else if (len <= 0) {
4387c478bd9Sstevel@tonic-gate if (fdin != fdout)
4397c478bd9Sstevel@tonic-gate (void) close(fdin);
4407c478bd9Sstevel@tonic-gate else
4417c478bd9Sstevel@tonic-gate (void) shutdown(fdin, SHUT_WR); /* We will no longer send. */
4427c478bd9Sstevel@tonic-gate fdin = -1;
4437c478bd9Sstevel@tonic-gate } else {
4447c478bd9Sstevel@tonic-gate /* Successful write. */
4457c478bd9Sstevel@tonic-gate if (fdin_is_tty && dlen >= 1 && data[0] != '\r' &&
4467c478bd9Sstevel@tonic-gate tcgetattr(fdin, &tio) == 0 &&
4477c478bd9Sstevel@tonic-gate !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
4487c478bd9Sstevel@tonic-gate /*
4497c478bd9Sstevel@tonic-gate * Simulate echo to reduce the impact of
4507c478bd9Sstevel@tonic-gate * traffic analysis
4517c478bd9Sstevel@tonic-gate */
4527c478bd9Sstevel@tonic-gate packet_send_ignore(len);
4537c478bd9Sstevel@tonic-gate packet_send();
4547c478bd9Sstevel@tonic-gate }
4557c478bd9Sstevel@tonic-gate /* Consume the data from the buffer. */
4567c478bd9Sstevel@tonic-gate buffer_consume(&stdin_buffer, len);
4577c478bd9Sstevel@tonic-gate /* Update the count of bytes written to the program. */
4587c478bd9Sstevel@tonic-gate stdin_bytes += len;
4597c478bd9Sstevel@tonic-gate }
4607c478bd9Sstevel@tonic-gate }
4617c478bd9Sstevel@tonic-gate /* Send any buffered packet data to the client. */
4627c478bd9Sstevel@tonic-gate if (FD_ISSET(connection_out, writeset))
4637c478bd9Sstevel@tonic-gate packet_write_poll();
4647c478bd9Sstevel@tonic-gate }
4657c478bd9Sstevel@tonic-gate
4667c478bd9Sstevel@tonic-gate /*
4677c478bd9Sstevel@tonic-gate * Wait until all buffered output has been sent to the client.
4687c478bd9Sstevel@tonic-gate * This is used when the program terminates.
4697c478bd9Sstevel@tonic-gate */
4707c478bd9Sstevel@tonic-gate static void
drain_output(void)4717c478bd9Sstevel@tonic-gate drain_output(void)
4727c478bd9Sstevel@tonic-gate {
4737c478bd9Sstevel@tonic-gate /* Send any buffered stdout data to the client. */
4747c478bd9Sstevel@tonic-gate if (buffer_len(&stdout_buffer) > 0) {
4757c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDOUT_DATA);
4767c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stdout_buffer),
4777c478bd9Sstevel@tonic-gate buffer_len(&stdout_buffer));
4787c478bd9Sstevel@tonic-gate packet_send();
4797c478bd9Sstevel@tonic-gate /* Update the count of sent bytes. */
4807c478bd9Sstevel@tonic-gate stdout_bytes += buffer_len(&stdout_buffer);
4817c478bd9Sstevel@tonic-gate }
4827c478bd9Sstevel@tonic-gate /* Send any buffered stderr data to the client. */
4837c478bd9Sstevel@tonic-gate if (buffer_len(&stderr_buffer) > 0) {
4847c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_STDERR_DATA);
4857c478bd9Sstevel@tonic-gate packet_put_string(buffer_ptr(&stderr_buffer),
4867c478bd9Sstevel@tonic-gate buffer_len(&stderr_buffer));
4877c478bd9Sstevel@tonic-gate packet_send();
4887c478bd9Sstevel@tonic-gate /* Update the count of sent bytes. */
4897c478bd9Sstevel@tonic-gate stderr_bytes += buffer_len(&stderr_buffer);
4907c478bd9Sstevel@tonic-gate }
4917c478bd9Sstevel@tonic-gate /* Wait until all buffered data has been written to the client. */
4927c478bd9Sstevel@tonic-gate packet_write_wait();
4937c478bd9Sstevel@tonic-gate }
4947c478bd9Sstevel@tonic-gate
4957c478bd9Sstevel@tonic-gate static void
process_buffered_input_packets(void)4967c478bd9Sstevel@tonic-gate process_buffered_input_packets(void)
4977c478bd9Sstevel@tonic-gate {
4987c478bd9Sstevel@tonic-gate dispatch_run(DISPATCH_NONBLOCK, NULL, compat20 ? xxx_kex : NULL);
4997c478bd9Sstevel@tonic-gate }
5007c478bd9Sstevel@tonic-gate
5017c478bd9Sstevel@tonic-gate /*
5027c478bd9Sstevel@tonic-gate * Performs the interactive session. This handles data transmission between
5037c478bd9Sstevel@tonic-gate * the client and the program. Note that the notion of stdin, stdout, and
5047c478bd9Sstevel@tonic-gate * stderr in this function is sort of reversed: this function writes to
5057c478bd9Sstevel@tonic-gate * stdin (of the child program), and reads from stdout and stderr (of the
5067c478bd9Sstevel@tonic-gate * child program).
5077c478bd9Sstevel@tonic-gate */
5087c478bd9Sstevel@tonic-gate void
server_loop(pid_t pid,int fdin_arg,int fdout_arg,int fderr_arg)5097c478bd9Sstevel@tonic-gate server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
5107c478bd9Sstevel@tonic-gate {
5117c478bd9Sstevel@tonic-gate fd_set *readset = NULL, *writeset = NULL;
5127c478bd9Sstevel@tonic-gate int max_fd = 0, nalloc = 0;
5137c478bd9Sstevel@tonic-gate int wait_status; /* Status returned by wait(). */
5147c478bd9Sstevel@tonic-gate pid_t wait_pid; /* pid returned by wait(). */
5157c478bd9Sstevel@tonic-gate int waiting_termination = 0; /* Have displayed waiting close message. */
5167c478bd9Sstevel@tonic-gate u_int max_time_milliseconds;
5177c478bd9Sstevel@tonic-gate u_int previous_stdout_buffer_bytes;
5187c478bd9Sstevel@tonic-gate u_int stdout_buffer_bytes;
5197c478bd9Sstevel@tonic-gate int type;
5207c478bd9Sstevel@tonic-gate
5217c478bd9Sstevel@tonic-gate debug("Entering interactive session.");
5227c478bd9Sstevel@tonic-gate
5237c478bd9Sstevel@tonic-gate /* Initialize the SIGCHLD kludge. */
5247c478bd9Sstevel@tonic-gate child_terminated = 0;
5257c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, sigchld_handler);
5267c478bd9Sstevel@tonic-gate
5277c478bd9Sstevel@tonic-gate /* Initialize our global variables. */
5287c478bd9Sstevel@tonic-gate fdin = fdin_arg;
5297c478bd9Sstevel@tonic-gate fdout = fdout_arg;
5307c478bd9Sstevel@tonic-gate fderr = fderr_arg;
5317c478bd9Sstevel@tonic-gate
5327c478bd9Sstevel@tonic-gate /* nonblocking IO */
5337c478bd9Sstevel@tonic-gate set_nonblock(fdin);
5347c478bd9Sstevel@tonic-gate set_nonblock(fdout);
5357c478bd9Sstevel@tonic-gate /* we don't have stderr for interactive terminal sessions, see below */
5367c478bd9Sstevel@tonic-gate if (fderr != -1)
5377c478bd9Sstevel@tonic-gate set_nonblock(fderr);
5387c478bd9Sstevel@tonic-gate
5397c478bd9Sstevel@tonic-gate if (!(datafellows & SSH_BUG_IGNOREMSG) && isatty(fdin))
5407c478bd9Sstevel@tonic-gate fdin_is_tty = 1;
5417c478bd9Sstevel@tonic-gate
5427c478bd9Sstevel@tonic-gate connection_in = packet_get_connection_in();
5437c478bd9Sstevel@tonic-gate connection_out = packet_get_connection_out();
5447c478bd9Sstevel@tonic-gate
5457c478bd9Sstevel@tonic-gate notify_setup();
5467c478bd9Sstevel@tonic-gate
5477c478bd9Sstevel@tonic-gate previous_stdout_buffer_bytes = 0;
5487c478bd9Sstevel@tonic-gate
5497c478bd9Sstevel@tonic-gate /* Set approximate I/O buffer size. */
5507c478bd9Sstevel@tonic-gate if (packet_is_interactive())
5517c478bd9Sstevel@tonic-gate buffer_high = 4096;
5527c478bd9Sstevel@tonic-gate else
5537c478bd9Sstevel@tonic-gate buffer_high = 64 * 1024;
5547c478bd9Sstevel@tonic-gate
5557c478bd9Sstevel@tonic-gate #if 0
5567c478bd9Sstevel@tonic-gate /* Initialize max_fd to the maximum of the known file descriptors. */
5577c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out);
5587c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdin);
5597c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdout);
5607c478bd9Sstevel@tonic-gate if (fderr != -1)
5617c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fderr);
5627c478bd9Sstevel@tonic-gate #endif
5637c478bd9Sstevel@tonic-gate
5647c478bd9Sstevel@tonic-gate /* Initialize Initialize buffers. */
5657c478bd9Sstevel@tonic-gate buffer_init(&stdin_buffer);
5667c478bd9Sstevel@tonic-gate buffer_init(&stdout_buffer);
5677c478bd9Sstevel@tonic-gate buffer_init(&stderr_buffer);
5687c478bd9Sstevel@tonic-gate
5697c478bd9Sstevel@tonic-gate /*
5707c478bd9Sstevel@tonic-gate * If we have no separate fderr (which is the case when we have a pty
5717c478bd9Sstevel@tonic-gate * - there we cannot make difference between data sent to stdout and
5727c478bd9Sstevel@tonic-gate * stderr), indicate that we have seen an EOF from stderr. This way
5737c478bd9Sstevel@tonic-gate * we don\'t need to check the descriptor everywhere.
5747c478bd9Sstevel@tonic-gate */
5757c478bd9Sstevel@tonic-gate if (fderr == -1)
5767c478bd9Sstevel@tonic-gate fderr_eof = 1;
5777c478bd9Sstevel@tonic-gate
5787c478bd9Sstevel@tonic-gate server_init_dispatch();
5797c478bd9Sstevel@tonic-gate
5807c478bd9Sstevel@tonic-gate /* Main loop of the server for the interactive session mode. */
5817c478bd9Sstevel@tonic-gate for (;;) {
5827c478bd9Sstevel@tonic-gate
5837c478bd9Sstevel@tonic-gate /* Process buffered packets from the client. */
5847c478bd9Sstevel@tonic-gate process_buffered_input_packets();
5857c478bd9Sstevel@tonic-gate
5867c478bd9Sstevel@tonic-gate /*
5877c478bd9Sstevel@tonic-gate * If we have received eof, and there is no more pending
5887c478bd9Sstevel@tonic-gate * input data, cause a real eof by closing fdin.
5897c478bd9Sstevel@tonic-gate */
5907c478bd9Sstevel@tonic-gate if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) {
5917c478bd9Sstevel@tonic-gate if (fdin != fdout)
5927c478bd9Sstevel@tonic-gate (void) close(fdin);
5937c478bd9Sstevel@tonic-gate else
5947c478bd9Sstevel@tonic-gate (void) shutdown(fdin, SHUT_WR); /* We will no longer send. */
5957c478bd9Sstevel@tonic-gate fdin = -1;
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate /* Make packets from buffered stderr data to send to the client. */
5987c478bd9Sstevel@tonic-gate make_packets_from_stderr_data();
5997c478bd9Sstevel@tonic-gate
6007c478bd9Sstevel@tonic-gate /*
6017c478bd9Sstevel@tonic-gate * Make packets from buffered stdout data to send to the
6027c478bd9Sstevel@tonic-gate * client. If there is very little to send, this arranges to
6037c478bd9Sstevel@tonic-gate * not send them now, but to wait a short while to see if we
6047c478bd9Sstevel@tonic-gate * are getting more data. This is necessary, as some systems
6057c478bd9Sstevel@tonic-gate * wake up readers from a pty after each separate character.
6067c478bd9Sstevel@tonic-gate */
6077c478bd9Sstevel@tonic-gate max_time_milliseconds = 0;
6087c478bd9Sstevel@tonic-gate stdout_buffer_bytes = buffer_len(&stdout_buffer);
6097c478bd9Sstevel@tonic-gate if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 &&
6107c478bd9Sstevel@tonic-gate stdout_buffer_bytes != previous_stdout_buffer_bytes) {
6117c478bd9Sstevel@tonic-gate /* try again after a while */
6127c478bd9Sstevel@tonic-gate max_time_milliseconds = 10;
6137c478bd9Sstevel@tonic-gate } else {
6147c478bd9Sstevel@tonic-gate /* Send it now. */
6157c478bd9Sstevel@tonic-gate make_packets_from_stdout_data();
6167c478bd9Sstevel@tonic-gate }
6177c478bd9Sstevel@tonic-gate previous_stdout_buffer_bytes = buffer_len(&stdout_buffer);
6187c478bd9Sstevel@tonic-gate
6197c478bd9Sstevel@tonic-gate /* Send channel data to the client. */
6207c478bd9Sstevel@tonic-gate if (packet_not_very_much_data_to_write())
6217c478bd9Sstevel@tonic-gate channel_output_poll();
6227c478bd9Sstevel@tonic-gate
6237c478bd9Sstevel@tonic-gate /*
6247c478bd9Sstevel@tonic-gate * Bail out of the loop if the program has closed its output
6257c478bd9Sstevel@tonic-gate * descriptors, and we have no more data to send to the
6267c478bd9Sstevel@tonic-gate * client, and there is no pending buffered data.
6277c478bd9Sstevel@tonic-gate */
6287c478bd9Sstevel@tonic-gate if (fdout_eof && fderr_eof && !packet_have_data_to_write() &&
6297c478bd9Sstevel@tonic-gate buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) {
6307c478bd9Sstevel@tonic-gate if (!channel_still_open())
6317c478bd9Sstevel@tonic-gate break;
6327c478bd9Sstevel@tonic-gate if (!waiting_termination) {
6337c478bd9Sstevel@tonic-gate const char *s = "Waiting for forwarded connections to terminate...\r\n";
6347c478bd9Sstevel@tonic-gate char *cp;
6357c478bd9Sstevel@tonic-gate waiting_termination = 1;
6367c478bd9Sstevel@tonic-gate buffer_append(&stderr_buffer, s, strlen(s));
6377c478bd9Sstevel@tonic-gate
6387c478bd9Sstevel@tonic-gate /* Display list of open channels. */
6397c478bd9Sstevel@tonic-gate cp = channel_open_message();
6407c478bd9Sstevel@tonic-gate buffer_append(&stderr_buffer, cp, strlen(cp));
6417c478bd9Sstevel@tonic-gate xfree(cp);
6427c478bd9Sstevel@tonic-gate }
6437c478bd9Sstevel@tonic-gate }
6447c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out);
6457c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdin);
6467c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fdout);
6477c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, fderr);
6487c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, notify_pipe[0]);
6497c478bd9Sstevel@tonic-gate
6507c478bd9Sstevel@tonic-gate /* Sleep in select() until we can do something. */
6517c478bd9Sstevel@tonic-gate wait_until_can_do_something(&readset, &writeset, &max_fd,
6527c478bd9Sstevel@tonic-gate &nalloc, max_time_milliseconds);
6537c478bd9Sstevel@tonic-gate
6547c478bd9Sstevel@tonic-gate /* Process any channel events. */
6557c478bd9Sstevel@tonic-gate channel_after_select(readset, writeset);
6567c478bd9Sstevel@tonic-gate
6577c478bd9Sstevel@tonic-gate /* Process input from the client and from program stdout/stderr. */
6587c478bd9Sstevel@tonic-gate process_input(readset);
6597c478bd9Sstevel@tonic-gate
6607c478bd9Sstevel@tonic-gate /* Process output to the client and to program stdin. */
6617c478bd9Sstevel@tonic-gate process_output(writeset);
6627c478bd9Sstevel@tonic-gate }
6637c478bd9Sstevel@tonic-gate if (readset)
6647c478bd9Sstevel@tonic-gate xfree(readset);
6657c478bd9Sstevel@tonic-gate if (writeset)
6667c478bd9Sstevel@tonic-gate xfree(writeset);
6677c478bd9Sstevel@tonic-gate
6687c478bd9Sstevel@tonic-gate /* Cleanup and termination code. */
6697c478bd9Sstevel@tonic-gate
6707c478bd9Sstevel@tonic-gate /* Wait until all output has been sent to the client. */
6717c478bd9Sstevel@tonic-gate drain_output();
6727c478bd9Sstevel@tonic-gate
6737c478bd9Sstevel@tonic-gate debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.",
6747c478bd9Sstevel@tonic-gate stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
6757c478bd9Sstevel@tonic-gate
6767c478bd9Sstevel@tonic-gate /* Free and clear the buffers. */
6777c478bd9Sstevel@tonic-gate buffer_free(&stdin_buffer);
6787c478bd9Sstevel@tonic-gate buffer_free(&stdout_buffer);
6797c478bd9Sstevel@tonic-gate buffer_free(&stderr_buffer);
6807c478bd9Sstevel@tonic-gate
6817c478bd9Sstevel@tonic-gate /* Close the file descriptors. */
6827c478bd9Sstevel@tonic-gate if (fdout != -1)
6837c478bd9Sstevel@tonic-gate (void) close(fdout);
6847c478bd9Sstevel@tonic-gate fdout = -1;
6857c478bd9Sstevel@tonic-gate fdout_eof = 1;
6867c478bd9Sstevel@tonic-gate if (fderr != -1)
6877c478bd9Sstevel@tonic-gate (void) close(fderr);
6887c478bd9Sstevel@tonic-gate fderr = -1;
6897c478bd9Sstevel@tonic-gate fderr_eof = 1;
6907c478bd9Sstevel@tonic-gate if (fdin != -1)
6917c478bd9Sstevel@tonic-gate (void) close(fdin);
6927c478bd9Sstevel@tonic-gate fdin = -1;
6937c478bd9Sstevel@tonic-gate
6947c478bd9Sstevel@tonic-gate channel_free_all();
6957c478bd9Sstevel@tonic-gate
6967c478bd9Sstevel@tonic-gate /* We no longer want our SIGCHLD handler to be called. */
6977c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, SIG_DFL);
6987c478bd9Sstevel@tonic-gate
6997c478bd9Sstevel@tonic-gate while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0)
7007c478bd9Sstevel@tonic-gate if (errno != EINTR)
7017c478bd9Sstevel@tonic-gate packet_disconnect("wait: %.100s", strerror(errno));
7027c478bd9Sstevel@tonic-gate if (wait_pid != pid)
7037c478bd9Sstevel@tonic-gate error("Strange, wait returned pid %ld, expected %ld",
7047c478bd9Sstevel@tonic-gate (long)wait_pid, (long)pid);
7057c478bd9Sstevel@tonic-gate
7067c478bd9Sstevel@tonic-gate /* Check if it exited normally. */
7077c478bd9Sstevel@tonic-gate if (WIFEXITED(wait_status)) {
7087c478bd9Sstevel@tonic-gate /* Yes, normal exit. Get exit status and send it to the client. */
7097c478bd9Sstevel@tonic-gate debug("Command exited with status %d.", WEXITSTATUS(wait_status));
7107c478bd9Sstevel@tonic-gate packet_start(SSH_SMSG_EXITSTATUS);
7117c478bd9Sstevel@tonic-gate packet_put_int(WEXITSTATUS(wait_status));
7127c478bd9Sstevel@tonic-gate packet_send();
7137c478bd9Sstevel@tonic-gate packet_write_wait();
7147c478bd9Sstevel@tonic-gate
7157c478bd9Sstevel@tonic-gate /*
7167c478bd9Sstevel@tonic-gate * Wait for exit confirmation. Note that there might be
7177c478bd9Sstevel@tonic-gate * other packets coming before it; however, the program has
7187c478bd9Sstevel@tonic-gate * already died so we just ignore them. The client is
7197c478bd9Sstevel@tonic-gate * supposed to respond with the confirmation when it receives
7207c478bd9Sstevel@tonic-gate * the exit status.
7217c478bd9Sstevel@tonic-gate */
7227c478bd9Sstevel@tonic-gate do {
7237c478bd9Sstevel@tonic-gate type = packet_read();
7247c478bd9Sstevel@tonic-gate }
7257c478bd9Sstevel@tonic-gate while (type != SSH_CMSG_EXIT_CONFIRMATION);
7267c478bd9Sstevel@tonic-gate
7277c478bd9Sstevel@tonic-gate debug("Received exit confirmation.");
7287c478bd9Sstevel@tonic-gate return;
7297c478bd9Sstevel@tonic-gate }
7307c478bd9Sstevel@tonic-gate /* Check if the program terminated due to a signal. */
7317c478bd9Sstevel@tonic-gate if (WIFSIGNALED(wait_status))
7327c478bd9Sstevel@tonic-gate packet_disconnect("Command terminated on signal %d.",
7337c478bd9Sstevel@tonic-gate WTERMSIG(wait_status));
7347c478bd9Sstevel@tonic-gate
7357c478bd9Sstevel@tonic-gate /* Some weird exit cause. Just exit. */
7367c478bd9Sstevel@tonic-gate packet_disconnect("wait returned status %04x.", wait_status);
7377c478bd9Sstevel@tonic-gate /* NOTREACHED */
7387c478bd9Sstevel@tonic-gate }
7397c478bd9Sstevel@tonic-gate
7407c478bd9Sstevel@tonic-gate static void
collect_children(void)7417c478bd9Sstevel@tonic-gate collect_children(void)
7427c478bd9Sstevel@tonic-gate {
7437c478bd9Sstevel@tonic-gate pid_t pid;
7447c478bd9Sstevel@tonic-gate sigset_t oset, nset;
7457c478bd9Sstevel@tonic-gate int status;
7467c478bd9Sstevel@tonic-gate
7477c478bd9Sstevel@tonic-gate /* block SIGCHLD while we check for dead children */
7487c478bd9Sstevel@tonic-gate (void) sigemptyset(&nset);
7497c478bd9Sstevel@tonic-gate (void) sigaddset(&nset, SIGCHLD);
7507c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &nset, &oset);
7517c478bd9Sstevel@tonic-gate if (child_terminated) {
7527c478bd9Sstevel@tonic-gate while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
7537c478bd9Sstevel@tonic-gate (pid < 0 && errno == EINTR))
7547c478bd9Sstevel@tonic-gate if (pid > 0)
7557c478bd9Sstevel@tonic-gate session_close_by_pid(pid, status);
7567c478bd9Sstevel@tonic-gate child_terminated = 0;
7577c478bd9Sstevel@tonic-gate }
7587c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &oset, NULL);
7597c478bd9Sstevel@tonic-gate }
7607c478bd9Sstevel@tonic-gate
7617c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP
7627c478bd9Sstevel@tonic-gate /*
7637c478bd9Sstevel@tonic-gate * For ALTPRIVSEP the wait_until_can_do_something function is very
7647c478bd9Sstevel@tonic-gate * simple: select() on the read side of the pipe, and if there's packets
7657c478bd9Sstevel@tonic-gate * to send, on the write side, and on the read side of the SIGCHLD
7667c478bd9Sstevel@tonic-gate * handler pipe. That's it.
7677c478bd9Sstevel@tonic-gate */
7687c478bd9Sstevel@tonic-gate static void
aps_wait_until_can_do_something(fd_set ** readsetp,fd_set ** writesetp,int * maxfdp,int * nallocp,u_int max_time_milliseconds)7697c478bd9Sstevel@tonic-gate aps_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
7707c478bd9Sstevel@tonic-gate int *maxfdp, int *nallocp, u_int max_time_milliseconds)
7717c478bd9Sstevel@tonic-gate {
7727c478bd9Sstevel@tonic-gate int ret;
7737c478bd9Sstevel@tonic-gate
7747c478bd9Sstevel@tonic-gate /*
7757c478bd9Sstevel@tonic-gate * Use channel_prepare_select() to make the fd sets.
7767c478bd9Sstevel@tonic-gate *
7777c478bd9Sstevel@tonic-gate * This is cheating, really, since because the last argument in
7787c478bd9Sstevel@tonic-gate * this call is '1' nothing related to channels will be done --
7797c478bd9Sstevel@tonic-gate * we're using this function only to callocate the fd sets.
7807c478bd9Sstevel@tonic-gate */
7817c478bd9Sstevel@tonic-gate channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 1);
7827c478bd9Sstevel@tonic-gate
7837c478bd9Sstevel@tonic-gate if ((connection_in = packet_get_connection_in()) >= 0 &&
7847c478bd9Sstevel@tonic-gate !connection_closed)
7857c478bd9Sstevel@tonic-gate FD_SET(connection_in, *readsetp);
7867c478bd9Sstevel@tonic-gate
7877c478bd9Sstevel@tonic-gate notify_prepare(*readsetp);
7887c478bd9Sstevel@tonic-gate
7897c478bd9Sstevel@tonic-gate if ((connection_out = packet_get_connection_out()) >= 0 &&
7907c478bd9Sstevel@tonic-gate packet_have_data_to_write() && !connection_closed)
7917c478bd9Sstevel@tonic-gate FD_SET(connection_out, *writesetp);
7927c478bd9Sstevel@tonic-gate
7937c478bd9Sstevel@tonic-gate /* Wait for something to happen, or the timeout to expire. */
7947c478bd9Sstevel@tonic-gate ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL);
7957c478bd9Sstevel@tonic-gate
7967c478bd9Sstevel@tonic-gate if (ret == -1) {
7977c478bd9Sstevel@tonic-gate memset(*readsetp, 0, *nallocp);
7987c478bd9Sstevel@tonic-gate memset(*writesetp, 0, *nallocp);
7997c478bd9Sstevel@tonic-gate if (errno != EINTR)
8007c478bd9Sstevel@tonic-gate error("select: %.100s", strerror(errno));
8017c478bd9Sstevel@tonic-gate }
8027c478bd9Sstevel@tonic-gate
8037c478bd9Sstevel@tonic-gate notify_done(*readsetp);
8047c478bd9Sstevel@tonic-gate }
8057c478bd9Sstevel@tonic-gate
8067c478bd9Sstevel@tonic-gate /*
8077c478bd9Sstevel@tonic-gate * Slightly different than collect_children, aps_collect_child() has
8087c478bd9Sstevel@tonic-gate * only the unprivileged sshd to wait for, no sessions, no channells,
8097c478bd9Sstevel@tonic-gate * just one process.
8107c478bd9Sstevel@tonic-gate */
8117c478bd9Sstevel@tonic-gate static int
aps_collect_child(pid_t child)8127c478bd9Sstevel@tonic-gate aps_collect_child(pid_t child)
8137c478bd9Sstevel@tonic-gate {
8147c478bd9Sstevel@tonic-gate pid_t pid;
8157c478bd9Sstevel@tonic-gate sigset_t oset, nset;
8167c478bd9Sstevel@tonic-gate int status;
8177c478bd9Sstevel@tonic-gate
8187c478bd9Sstevel@tonic-gate /* block SIGCHLD while we check for dead children */
8197c478bd9Sstevel@tonic-gate (void) sigemptyset(&nset);
8207c478bd9Sstevel@tonic-gate (void) sigaddset(&nset, SIGCHLD);
8217c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &nset, &oset);
8227c478bd9Sstevel@tonic-gate if (child_terminated) {
8237c478bd9Sstevel@tonic-gate while ((pid = waitpid(child, &status, WNOHANG)) > 0 ||
8247c478bd9Sstevel@tonic-gate (pid < 0 && errno == EINTR))
8257c478bd9Sstevel@tonic-gate if (pid == child) {
8267c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &oset, NULL);
8277c478bd9Sstevel@tonic-gate return (1);
8287c478bd9Sstevel@tonic-gate }
8297c478bd9Sstevel@tonic-gate child_terminated = 0;
8307c478bd9Sstevel@tonic-gate }
8317c478bd9Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &oset, NULL);
8327c478bd9Sstevel@tonic-gate return (0);
8337c478bd9Sstevel@tonic-gate }
8347c478bd9Sstevel@tonic-gate
8357c478bd9Sstevel@tonic-gate static int killed = 0;
8367c478bd9Sstevel@tonic-gate
8377c478bd9Sstevel@tonic-gate static void
aps_monitor_kill_handler(int sig)8387c478bd9Sstevel@tonic-gate aps_monitor_kill_handler(int sig)
8397c478bd9Sstevel@tonic-gate {
8407c478bd9Sstevel@tonic-gate int save_errno = errno;
8417c478bd9Sstevel@tonic-gate killed = 1;
8427c478bd9Sstevel@tonic-gate notify_parent();
8437c478bd9Sstevel@tonic-gate mysignal(sig, aps_monitor_kill_handler);
8447c478bd9Sstevel@tonic-gate errno = save_errno;
8457c478bd9Sstevel@tonic-gate }
8467c478bd9Sstevel@tonic-gate
8477c478bd9Sstevel@tonic-gate static void
aps_monitor_sigchld_handler(int sig)8487c478bd9Sstevel@tonic-gate aps_monitor_sigchld_handler(int sig)
8497c478bd9Sstevel@tonic-gate {
8507c478bd9Sstevel@tonic-gate int save_errno = errno;
8517c478bd9Sstevel@tonic-gate debug("Monitor received SIGCHLD.");
8527c478bd9Sstevel@tonic-gate child_terminated = 1;
8537c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, aps_monitor_sigchld_handler);
8547c478bd9Sstevel@tonic-gate notify_parent();
8557c478bd9Sstevel@tonic-gate errno = save_errno;
8567c478bd9Sstevel@tonic-gate }
8577c478bd9Sstevel@tonic-gate
8587c478bd9Sstevel@tonic-gate void
aps_monitor_loop(Authctxt * authctxt,pid_t child_pid)859cd7d5fafSJan Pechanec aps_monitor_loop(Authctxt *authctxt, pid_t child_pid)
8607c478bd9Sstevel@tonic-gate {
8617c478bd9Sstevel@tonic-gate fd_set *readset = NULL, *writeset = NULL;
8627c478bd9Sstevel@tonic-gate int max_fd, nalloc = 0;
8637c478bd9Sstevel@tonic-gate
8647c478bd9Sstevel@tonic-gate debug("Entering monitor loop.");
8657c478bd9Sstevel@tonic-gate
8667c478bd9Sstevel@tonic-gate /*
8677c478bd9Sstevel@tonic-gate * Awful hack follows: fake compat20 == 1 to cause process_input()
8687c478bd9Sstevel@tonic-gate * and process_output() to behave as they would for SSHv2 because that's
8697c478bd9Sstevel@tonic-gate * the behaviour we need in SSHv2.
8707c478bd9Sstevel@tonic-gate *
8717c478bd9Sstevel@tonic-gate * This same hack is done in packet.c
8727c478bd9Sstevel@tonic-gate */
8737c478bd9Sstevel@tonic-gate compat20 = 1; /* causes process_input/output() to ignore stdio */
8747c478bd9Sstevel@tonic-gate
8757c478bd9Sstevel@tonic-gate mysignal(SIGHUP, aps_monitor_kill_handler);
8767c478bd9Sstevel@tonic-gate mysignal(SIGINT, aps_monitor_kill_handler);
8777c478bd9Sstevel@tonic-gate mysignal(SIGTERM, aps_monitor_kill_handler);
8787c478bd9Sstevel@tonic-gate
8797c478bd9Sstevel@tonic-gate child_terminated = 0;
8807c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, aps_monitor_sigchld_handler);
8817c478bd9Sstevel@tonic-gate
8827c478bd9Sstevel@tonic-gate connection_in = packet_get_connection_in();
8837c478bd9Sstevel@tonic-gate connection_out = packet_get_connection_out();
8847c478bd9Sstevel@tonic-gate
8857c478bd9Sstevel@tonic-gate notify_setup();
8867c478bd9Sstevel@tonic-gate
8877c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out);
8887c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, notify_pipe[0]);
8897c478bd9Sstevel@tonic-gate
8907c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
8917c478bd9Sstevel@tonic-gate dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_MAX,
8927c478bd9Sstevel@tonic-gate &dispatch_protocol_error);
8937c478bd9Sstevel@tonic-gate dispatch_set(SSH2_PRIV_MSG_ALTPRIVSEP, &aps_input_altpriv_msg);
8947c478bd9Sstevel@tonic-gate
8957c478bd9Sstevel@tonic-gate for (;;) {
8967c478bd9Sstevel@tonic-gate process_buffered_input_packets();
8977c478bd9Sstevel@tonic-gate
8987c478bd9Sstevel@tonic-gate aps_wait_until_can_do_something(&readset, &writeset, &max_fd,
8997c478bd9Sstevel@tonic-gate &nalloc, 0);
9007c478bd9Sstevel@tonic-gate
9017c478bd9Sstevel@tonic-gate if (aps_collect_child(child_pid))
9027c478bd9Sstevel@tonic-gate break;
9037c478bd9Sstevel@tonic-gate
9047c478bd9Sstevel@tonic-gate if (killed) {
9057c478bd9Sstevel@tonic-gate /* fatal cleanups will kill child, audit logout */
9067c478bd9Sstevel@tonic-gate log("Monitor killed; exiting");
9077c478bd9Sstevel@tonic-gate fatal_cleanup();
9087c478bd9Sstevel@tonic-gate }
9097c478bd9Sstevel@tonic-gate
9107c478bd9Sstevel@tonic-gate /*
9117c478bd9Sstevel@tonic-gate * Unlike server_loop2() we don't care if connection_closed
9127c478bd9Sstevel@tonic-gate * since we still want to wait for the monitor's child.
9137c478bd9Sstevel@tonic-gate */
9147c478bd9Sstevel@tonic-gate process_input(readset);
9157c478bd9Sstevel@tonic-gate process_output(writeset);
9167c478bd9Sstevel@tonic-gate }
9177c478bd9Sstevel@tonic-gate
9187c478bd9Sstevel@tonic-gate packet_close();
9197c478bd9Sstevel@tonic-gate }
9207c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */
9217c478bd9Sstevel@tonic-gate
922cd7d5fafSJan Pechanec /*
923cd7d5fafSJan Pechanec * This server loop is for unprivileged child only. Our monitor runs its own
924cd7d5fafSJan Pechanec * aps_monitor_loop() funtion.
925cd7d5fafSJan Pechanec */
9267c478bd9Sstevel@tonic-gate void
server_loop2(Authctxt * authctxt)9277c478bd9Sstevel@tonic-gate server_loop2(Authctxt *authctxt)
9287c478bd9Sstevel@tonic-gate {
9297c478bd9Sstevel@tonic-gate fd_set *readset = NULL, *writeset = NULL;
9307c478bd9Sstevel@tonic-gate int rekeying = 0, max_fd, nalloc = 0;
9317c478bd9Sstevel@tonic-gate
9327c478bd9Sstevel@tonic-gate debug("Entering interactive session for SSH2.");
9337c478bd9Sstevel@tonic-gate
9347c478bd9Sstevel@tonic-gate mysignal(SIGCHLD, sigchld_handler);
9357c478bd9Sstevel@tonic-gate child_terminated = 0;
9367c478bd9Sstevel@tonic-gate connection_in = packet_get_connection_in();
9377c478bd9Sstevel@tonic-gate connection_out = packet_get_connection_out();
9387c478bd9Sstevel@tonic-gate
9397c478bd9Sstevel@tonic-gate notify_setup();
9407c478bd9Sstevel@tonic-gate
9417c478bd9Sstevel@tonic-gate max_fd = MAX(connection_in, connection_out);
9427c478bd9Sstevel@tonic-gate max_fd = MAX(max_fd, notify_pipe[0]);
9437c478bd9Sstevel@tonic-gate
9447c478bd9Sstevel@tonic-gate xxx_authctxt = authctxt;
9457c478bd9Sstevel@tonic-gate
9467c478bd9Sstevel@tonic-gate server_init_dispatch();
9477c478bd9Sstevel@tonic-gate
9487c478bd9Sstevel@tonic-gate for (;;) {
9497c478bd9Sstevel@tonic-gate process_buffered_input_packets();
9507c478bd9Sstevel@tonic-gate
9517c478bd9Sstevel@tonic-gate rekeying = (xxx_kex != NULL && !xxx_kex->done);
9527c478bd9Sstevel@tonic-gate
9537c478bd9Sstevel@tonic-gate if (!rekeying && packet_not_very_much_data_to_write())
9547c478bd9Sstevel@tonic-gate channel_output_poll();
9557c478bd9Sstevel@tonic-gate wait_until_can_do_something(&readset, &writeset, &max_fd,
9567c478bd9Sstevel@tonic-gate &nalloc, 0);
9577c478bd9Sstevel@tonic-gate
9587c478bd9Sstevel@tonic-gate collect_children();
9597c478bd9Sstevel@tonic-gate
9609a8058b5Sjp161948 if (!rekeying) {
9617c478bd9Sstevel@tonic-gate channel_after_select(readset, writeset);
9629a8058b5Sjp161948 if (packet_need_rekeying()) {
963dbe3f931Sjp161948 debug("rekey limit reached, need rekeying");
9649a8058b5Sjp161948 xxx_kex->done = 0;
965dbe3f931Sjp161948 debug("poking the monitor to start "
966dbe3f931Sjp161948 "key re-exchange");
967dbe3f931Sjp161948 altprivsep_start_rekex();
9689a8058b5Sjp161948 }
9699a8058b5Sjp161948 }
9707c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP
9717c478bd9Sstevel@tonic-gate else
972dbe3f931Sjp161948 altprivsep_process_input(readset);
9737c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */
9747c478bd9Sstevel@tonic-gate
9757c478bd9Sstevel@tonic-gate process_input(readset);
9767c478bd9Sstevel@tonic-gate if (connection_closed)
9777c478bd9Sstevel@tonic-gate break;
9787c478bd9Sstevel@tonic-gate process_output(writeset);
9797c478bd9Sstevel@tonic-gate }
9807c478bd9Sstevel@tonic-gate collect_children();
9817c478bd9Sstevel@tonic-gate
9827c478bd9Sstevel@tonic-gate if (readset)
9837c478bd9Sstevel@tonic-gate xfree(readset);
9847c478bd9Sstevel@tonic-gate if (writeset)
9857c478bd9Sstevel@tonic-gate xfree(writeset);
9867c478bd9Sstevel@tonic-gate
9877c478bd9Sstevel@tonic-gate /* free all channels, no more reads and writes */
9887c478bd9Sstevel@tonic-gate channel_free_all();
9897c478bd9Sstevel@tonic-gate
9907c478bd9Sstevel@tonic-gate /* free remaining sessions, e.g. remove wtmp entries */
9917c478bd9Sstevel@tonic-gate session_destroy_all(NULL);
9927c478bd9Sstevel@tonic-gate }
9937c478bd9Sstevel@tonic-gate
9947c478bd9Sstevel@tonic-gate static void
server_input_channel_failure(int type,u_int32_t seq,void * ctxt)9957c478bd9Sstevel@tonic-gate server_input_channel_failure(int type, u_int32_t seq, void *ctxt)
9967c478bd9Sstevel@tonic-gate {
9977c478bd9Sstevel@tonic-gate debug("Got CHANNEL_FAILURE for keepalive");
9987c478bd9Sstevel@tonic-gate /*
9997c478bd9Sstevel@tonic-gate * reset timeout, since we got a sane answer from the client.
10007c478bd9Sstevel@tonic-gate * even if this was generated by something other than
10017c478bd9Sstevel@tonic-gate * the bogus CHANNEL_REQUEST we send for keepalives.
10027c478bd9Sstevel@tonic-gate */
10037c478bd9Sstevel@tonic-gate client_alive_timeouts = 0;
10047c478bd9Sstevel@tonic-gate }
10057c478bd9Sstevel@tonic-gate
10067c478bd9Sstevel@tonic-gate static void
server_input_stdin_data(int type,u_int32_t seq,void * ctxt)10077c478bd9Sstevel@tonic-gate server_input_stdin_data(int type, u_int32_t seq, void *ctxt)
10087c478bd9Sstevel@tonic-gate {
10097c478bd9Sstevel@tonic-gate char *data;
10107c478bd9Sstevel@tonic-gate u_int data_len;
10117c478bd9Sstevel@tonic-gate
10127c478bd9Sstevel@tonic-gate /* Stdin data from the client. Append it to the buffer. */
10137c478bd9Sstevel@tonic-gate /* Ignore any data if the client has closed stdin. */
10147c478bd9Sstevel@tonic-gate if (fdin == -1)
10157c478bd9Sstevel@tonic-gate return;
10167c478bd9Sstevel@tonic-gate data = packet_get_string(&data_len);
10177c478bd9Sstevel@tonic-gate packet_check_eom();
10187c478bd9Sstevel@tonic-gate buffer_append(&stdin_buffer, data, data_len);
10197c478bd9Sstevel@tonic-gate memset(data, 0, data_len);
10207c478bd9Sstevel@tonic-gate xfree(data);
10217c478bd9Sstevel@tonic-gate }
10227c478bd9Sstevel@tonic-gate
10237c478bd9Sstevel@tonic-gate static void
server_input_eof(int type,u_int32_t seq,void * ctxt)10247c478bd9Sstevel@tonic-gate server_input_eof(int type, u_int32_t seq, void *ctxt)
10257c478bd9Sstevel@tonic-gate {
10267c478bd9Sstevel@tonic-gate /*
10277c478bd9Sstevel@tonic-gate * Eof from the client. The stdin descriptor to the
10287c478bd9Sstevel@tonic-gate * program will be closed when all buffered data has
10297c478bd9Sstevel@tonic-gate * drained.
10307c478bd9Sstevel@tonic-gate */
10317c478bd9Sstevel@tonic-gate debug("EOF received for stdin.");
10327c478bd9Sstevel@tonic-gate packet_check_eom();
10337c478bd9Sstevel@tonic-gate stdin_eof = 1;
10347c478bd9Sstevel@tonic-gate }
10357c478bd9Sstevel@tonic-gate
10367c478bd9Sstevel@tonic-gate static void
server_input_window_size(int type,u_int32_t seq,void * ctxt)10377c478bd9Sstevel@tonic-gate server_input_window_size(int type, u_int32_t seq, void *ctxt)
10387c478bd9Sstevel@tonic-gate {
10397c478bd9Sstevel@tonic-gate int row = packet_get_int();
10407c478bd9Sstevel@tonic-gate int col = packet_get_int();
10417c478bd9Sstevel@tonic-gate int xpixel = packet_get_int();
10427c478bd9Sstevel@tonic-gate int ypixel = packet_get_int();
10437c478bd9Sstevel@tonic-gate
10447c478bd9Sstevel@tonic-gate debug("Window change received.");
10457c478bd9Sstevel@tonic-gate packet_check_eom();
10467c478bd9Sstevel@tonic-gate if (fdin != -1)
10477c478bd9Sstevel@tonic-gate pty_change_window_size(fdin, row, col, xpixel, ypixel);
10487c478bd9Sstevel@tonic-gate }
10497c478bd9Sstevel@tonic-gate
10507c478bd9Sstevel@tonic-gate static Channel *
server_request_direct_tcpip(char * ctype)10517c478bd9Sstevel@tonic-gate server_request_direct_tcpip(char *ctype)
10527c478bd9Sstevel@tonic-gate {
10537c478bd9Sstevel@tonic-gate Channel *c;
10547c478bd9Sstevel@tonic-gate int sock;
10557c478bd9Sstevel@tonic-gate char *target, *originator;
10567c478bd9Sstevel@tonic-gate int target_port, originator_port;
10577c478bd9Sstevel@tonic-gate
10587c478bd9Sstevel@tonic-gate target = packet_get_string(NULL);
10597c478bd9Sstevel@tonic-gate target_port = packet_get_int();
10607c478bd9Sstevel@tonic-gate originator = packet_get_string(NULL);
10617c478bd9Sstevel@tonic-gate originator_port = packet_get_int();
10627c478bd9Sstevel@tonic-gate packet_check_eom();
10637c478bd9Sstevel@tonic-gate
10647c478bd9Sstevel@tonic-gate debug("server_request_direct_tcpip: originator %s port %d, target %s port %d",
10657c478bd9Sstevel@tonic-gate originator, originator_port, target, target_port);
10667c478bd9Sstevel@tonic-gate
10677c478bd9Sstevel@tonic-gate /* XXX check permission */
10687c478bd9Sstevel@tonic-gate sock = channel_connect_to(target, target_port);
10697c478bd9Sstevel@tonic-gate
10707c478bd9Sstevel@tonic-gate xfree(target);
10717c478bd9Sstevel@tonic-gate xfree(originator);
10727c478bd9Sstevel@tonic-gate if (sock < 0)
10737c478bd9Sstevel@tonic-gate return NULL;
10747c478bd9Sstevel@tonic-gate c = channel_new(ctype, SSH_CHANNEL_CONNECTING,
10757c478bd9Sstevel@tonic-gate sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT,
10767c478bd9Sstevel@tonic-gate CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip"), 1);
10777c478bd9Sstevel@tonic-gate return c;
10787c478bd9Sstevel@tonic-gate }
10797c478bd9Sstevel@tonic-gate
10807c478bd9Sstevel@tonic-gate static Channel *
server_request_session(char * ctype)10817c478bd9Sstevel@tonic-gate server_request_session(char *ctype)
10827c478bd9Sstevel@tonic-gate {
10837c478bd9Sstevel@tonic-gate Channel *c;
10847c478bd9Sstevel@tonic-gate
10857c478bd9Sstevel@tonic-gate debug("input_session_request");
10867c478bd9Sstevel@tonic-gate packet_check_eom();
10877c478bd9Sstevel@tonic-gate /*
10887c478bd9Sstevel@tonic-gate * A server session has no fd to read or write until a
10897c478bd9Sstevel@tonic-gate * CHANNEL_REQUEST for a shell is made, so we set the type to
10907c478bd9Sstevel@tonic-gate * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all
10917c478bd9Sstevel@tonic-gate * CHANNEL_REQUEST messages is registered.
10927c478bd9Sstevel@tonic-gate */
10937c478bd9Sstevel@tonic-gate c = channel_new(ctype, SSH_CHANNEL_LARVAL,
10947c478bd9Sstevel@tonic-gate -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT,
10957c478bd9Sstevel@tonic-gate 0, xstrdup("server-session"), 1);
10967c478bd9Sstevel@tonic-gate if (session_open(xxx_authctxt, c->self) != 1) {
10977c478bd9Sstevel@tonic-gate debug("session open failed, free channel %d", c->self);
10987c478bd9Sstevel@tonic-gate channel_free(c);
10997c478bd9Sstevel@tonic-gate return NULL;
11007c478bd9Sstevel@tonic-gate }
11017c478bd9Sstevel@tonic-gate channel_register_cleanup(c->self, session_close_by_channel);
11027c478bd9Sstevel@tonic-gate return c;
11037c478bd9Sstevel@tonic-gate }
11047c478bd9Sstevel@tonic-gate
11057c478bd9Sstevel@tonic-gate static void
server_input_channel_open(int type,u_int32_t seq,void * ctxt)11067c478bd9Sstevel@tonic-gate server_input_channel_open(int type, u_int32_t seq, void *ctxt)
11077c478bd9Sstevel@tonic-gate {
11087c478bd9Sstevel@tonic-gate Channel *c = NULL;
11097c478bd9Sstevel@tonic-gate char *ctype;
11107c478bd9Sstevel@tonic-gate int rchan;
11117c478bd9Sstevel@tonic-gate u_int rmaxpack, rwindow, len;
11127c478bd9Sstevel@tonic-gate
11137c478bd9Sstevel@tonic-gate ctype = packet_get_string(&len);
11147c478bd9Sstevel@tonic-gate rchan = packet_get_int();
11157c478bd9Sstevel@tonic-gate rwindow = packet_get_int();
11167c478bd9Sstevel@tonic-gate rmaxpack = packet_get_int();
11177c478bd9Sstevel@tonic-gate
11187c478bd9Sstevel@tonic-gate debug("server_input_channel_open: ctype %s rchan %d win %d max %d",
11197c478bd9Sstevel@tonic-gate ctype, rchan, rwindow, rmaxpack);
11207c478bd9Sstevel@tonic-gate
11217c478bd9Sstevel@tonic-gate if (strcmp(ctype, "session") == 0) {
11227c478bd9Sstevel@tonic-gate c = server_request_session(ctype);
11237c478bd9Sstevel@tonic-gate } else if (strcmp(ctype, "direct-tcpip") == 0) {
11247c478bd9Sstevel@tonic-gate c = server_request_direct_tcpip(ctype);
11257c478bd9Sstevel@tonic-gate }
11267c478bd9Sstevel@tonic-gate if (c != NULL) {
11277c478bd9Sstevel@tonic-gate debug("server_input_channel_open: confirm %s", ctype);
11287c478bd9Sstevel@tonic-gate c->remote_id = rchan;
11297c478bd9Sstevel@tonic-gate c->remote_window = rwindow;
11307c478bd9Sstevel@tonic-gate c->remote_maxpacket = rmaxpack;
11317c478bd9Sstevel@tonic-gate if (c->type != SSH_CHANNEL_CONNECTING) {
11327c478bd9Sstevel@tonic-gate packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
11337c478bd9Sstevel@tonic-gate packet_put_int(c->remote_id);
11347c478bd9Sstevel@tonic-gate packet_put_int(c->self);
11357c478bd9Sstevel@tonic-gate packet_put_int(c->local_window);
11367c478bd9Sstevel@tonic-gate packet_put_int(c->local_maxpacket);
11377c478bd9Sstevel@tonic-gate packet_send();
11387c478bd9Sstevel@tonic-gate }
11397c478bd9Sstevel@tonic-gate } else {
11407c478bd9Sstevel@tonic-gate debug("server_input_channel_open: failure %s", ctype);
11417c478bd9Sstevel@tonic-gate packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
11427c478bd9Sstevel@tonic-gate packet_put_int(rchan);
11437c478bd9Sstevel@tonic-gate packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
11447c478bd9Sstevel@tonic-gate if (!(datafellows & SSH_BUG_OPENFAILURE)) {
11456f786aceSNobutomo Nakano packet_put_utf8_cstring("open failed");
11467c478bd9Sstevel@tonic-gate packet_put_cstring("");
11477c478bd9Sstevel@tonic-gate }
11487c478bd9Sstevel@tonic-gate packet_send();
11497c478bd9Sstevel@tonic-gate }
11507c478bd9Sstevel@tonic-gate xfree(ctype);
11517c478bd9Sstevel@tonic-gate }
11527c478bd9Sstevel@tonic-gate
11537c478bd9Sstevel@tonic-gate static void
server_input_global_request(int type,u_int32_t seq,void * ctxt)11547c478bd9Sstevel@tonic-gate server_input_global_request(int type, u_int32_t seq, void *ctxt)
11557c478bd9Sstevel@tonic-gate {
11567c478bd9Sstevel@tonic-gate char *rtype;
11577c478bd9Sstevel@tonic-gate int want_reply;
11587c478bd9Sstevel@tonic-gate int success = 0;
11597c478bd9Sstevel@tonic-gate
11607c478bd9Sstevel@tonic-gate rtype = packet_get_string(NULL);
11617c478bd9Sstevel@tonic-gate want_reply = packet_get_char();
11627c478bd9Sstevel@tonic-gate debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply);
11637c478bd9Sstevel@tonic-gate
11647c478bd9Sstevel@tonic-gate /* -R style forwarding */
11657c478bd9Sstevel@tonic-gate if (strcmp(rtype, "tcpip-forward") == 0) {
11667c478bd9Sstevel@tonic-gate struct passwd *pw;
11677c478bd9Sstevel@tonic-gate char *listen_address;
11687c478bd9Sstevel@tonic-gate u_short listen_port;
11697c478bd9Sstevel@tonic-gate
11707c478bd9Sstevel@tonic-gate pw = auth_get_user();
11717c478bd9Sstevel@tonic-gate if (pw == NULL)
11727c478bd9Sstevel@tonic-gate fatal("server_input_global_request: no user");
11737c478bd9Sstevel@tonic-gate listen_address = packet_get_string(NULL); /* XXX currently ignored */
11747c478bd9Sstevel@tonic-gate listen_port = (u_short)packet_get_int();
11757c478bd9Sstevel@tonic-gate debug("server_input_global_request: tcpip-forward listen %s port %d",
11767c478bd9Sstevel@tonic-gate listen_address, listen_port);
11777c478bd9Sstevel@tonic-gate
11787c478bd9Sstevel@tonic-gate /* check permissions */
11797c478bd9Sstevel@tonic-gate if (!options.allow_tcp_forwarding ||
11807c478bd9Sstevel@tonic-gate no_port_forwarding_flag
11817c478bd9Sstevel@tonic-gate #ifndef NO_IPPORT_RESERVED_CONCEPT
11827c478bd9Sstevel@tonic-gate || (listen_port < IPPORT_RESERVED && pw->pw_uid != 0)
11837c478bd9Sstevel@tonic-gate #endif
11847c478bd9Sstevel@tonic-gate ) {
11857c478bd9Sstevel@tonic-gate success = 0;
11867c478bd9Sstevel@tonic-gate packet_send_debug("Server has disabled port forwarding.");
11877c478bd9Sstevel@tonic-gate } else {
11887c478bd9Sstevel@tonic-gate /* Start listening on the port */
11897c478bd9Sstevel@tonic-gate success = channel_setup_remote_fwd_listener(
11907c478bd9Sstevel@tonic-gate listen_address, listen_port, options.gateway_ports);
11917c478bd9Sstevel@tonic-gate }
11927c478bd9Sstevel@tonic-gate xfree(listen_address);
11939b03ea0fSjp161948 } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) {
11949b03ea0fSjp161948 char *cancel_address;
11959b03ea0fSjp161948 u_short cancel_port;
11967c478bd9Sstevel@tonic-gate
11979b03ea0fSjp161948 cancel_address = packet_get_string(NULL);
11989b03ea0fSjp161948 cancel_port = (u_short)packet_get_int();
11999b03ea0fSjp161948 debug("%s: cancel-tcpip-forward addr %s port %d", __func__,
12009b03ea0fSjp161948 cancel_address, cancel_port);
12019b03ea0fSjp161948
12029b03ea0fSjp161948 success = channel_cancel_rport_listener(cancel_address,
12039b03ea0fSjp161948 cancel_port);
12049b03ea0fSjp161948 xfree(cancel_address);
12059b03ea0fSjp161948 }
12067c478bd9Sstevel@tonic-gate if (want_reply) {
12077c478bd9Sstevel@tonic-gate packet_start(success ?
12087c478bd9Sstevel@tonic-gate SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
12097c478bd9Sstevel@tonic-gate packet_send();
12107c478bd9Sstevel@tonic-gate packet_write_wait();
12117c478bd9Sstevel@tonic-gate }
12127c478bd9Sstevel@tonic-gate xfree(rtype);
12137c478bd9Sstevel@tonic-gate }
12149b03ea0fSjp161948
12157c478bd9Sstevel@tonic-gate static void
server_input_channel_req(int type,u_int32_t seq,void * ctxt)12167c478bd9Sstevel@tonic-gate server_input_channel_req(int type, u_int32_t seq, void *ctxt)
12177c478bd9Sstevel@tonic-gate {
12187c478bd9Sstevel@tonic-gate Channel *c;
12197c478bd9Sstevel@tonic-gate int id, reply, success = 0;
12207c478bd9Sstevel@tonic-gate char *rtype;
12217c478bd9Sstevel@tonic-gate
12227c478bd9Sstevel@tonic-gate id = packet_get_int();
12237c478bd9Sstevel@tonic-gate rtype = packet_get_string(NULL);
12247c478bd9Sstevel@tonic-gate reply = packet_get_char();
12257c478bd9Sstevel@tonic-gate
12267c478bd9Sstevel@tonic-gate debug("server_input_channel_req: channel %d request %s reply %d",
12277c478bd9Sstevel@tonic-gate id, rtype, reply);
12287c478bd9Sstevel@tonic-gate
12297c478bd9Sstevel@tonic-gate if ((c = channel_lookup(id)) == NULL)
12307c478bd9Sstevel@tonic-gate packet_disconnect("server_input_channel_req: "
12317c478bd9Sstevel@tonic-gate "unknown channel %d", id);
1232*371387faSAdam Stevko if (!strcmp(rtype, "eow@openssh.com")) {
1233*371387faSAdam Stevko packet_check_eom();
1234*371387faSAdam Stevko chan_rcvd_eow(c);
1235*371387faSAdam Stevko } else if (c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN)
12367c478bd9Sstevel@tonic-gate success = session_input_channel_req(c, rtype);
12377c478bd9Sstevel@tonic-gate if (reply) {
12387c478bd9Sstevel@tonic-gate packet_start(success ?
12397c478bd9Sstevel@tonic-gate SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
12407c478bd9Sstevel@tonic-gate packet_put_int(c->remote_id);
12417c478bd9Sstevel@tonic-gate packet_send();
12427c478bd9Sstevel@tonic-gate }
12437c478bd9Sstevel@tonic-gate xfree(rtype);
12447c478bd9Sstevel@tonic-gate }
12457c478bd9Sstevel@tonic-gate
12467c478bd9Sstevel@tonic-gate static void
server_init_dispatch_20(void)12477c478bd9Sstevel@tonic-gate server_init_dispatch_20(void)
12487c478bd9Sstevel@tonic-gate {
12497c478bd9Sstevel@tonic-gate debug("server_init_dispatch_20");
12507c478bd9Sstevel@tonic-gate dispatch_init(&dispatch_protocol_error);
12517c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
12527c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
12537c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
12547c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
12557c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open);
12567c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
12577c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
12587c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &server_input_channel_req);
12597c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
12607c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request);
12617c478bd9Sstevel@tonic-gate /* client_alive */
12627c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_channel_failure);
12637c478bd9Sstevel@tonic-gate /* rekeying */
12647c478bd9Sstevel@tonic-gate
12657c478bd9Sstevel@tonic-gate #ifdef ALTPRIVSEP
12667c478bd9Sstevel@tonic-gate /* unprivileged sshd has a kex packet handler that must not be reset */
12677c478bd9Sstevel@tonic-gate debug3("server_init_dispatch_20 -- should we dispatch_set(KEXINIT) here? %d && !%d",
12687c478bd9Sstevel@tonic-gate packet_is_server(), packet_is_monitor());
12697c478bd9Sstevel@tonic-gate if (packet_is_server() && !packet_is_monitor()) {
12707c478bd9Sstevel@tonic-gate debug3("server_init_dispatch_20 -- skipping dispatch_set(KEXINIT) in unpriv proc");
12717c478bd9Sstevel@tonic-gate dispatch_range(SSH2_MSG_KEXINIT, SSH2_MSG_TRANSPORT_MAX,
12727c478bd9Sstevel@tonic-gate &altprivsep_rekey);
12737c478bd9Sstevel@tonic-gate return;
12747c478bd9Sstevel@tonic-gate }
12757c478bd9Sstevel@tonic-gate #endif /* ALTPRIVSEP */
12767c478bd9Sstevel@tonic-gate dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
12777c478bd9Sstevel@tonic-gate }
12787c478bd9Sstevel@tonic-gate static void
server_init_dispatch_13(void)12797c478bd9Sstevel@tonic-gate server_init_dispatch_13(void)
12807c478bd9Sstevel@tonic-gate {
12817c478bd9Sstevel@tonic-gate debug("server_init_dispatch_13");
12827c478bd9Sstevel@tonic-gate dispatch_init(NULL);
12837c478bd9Sstevel@tonic-gate dispatch_set(SSH_CMSG_EOF, &server_input_eof);
12847c478bd9Sstevel@tonic-gate dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data);
12857c478bd9Sstevel@tonic-gate dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size);
12867c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
12877c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
12887c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
12897c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
12907c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
12917c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
12927c478bd9Sstevel@tonic-gate }
12937c478bd9Sstevel@tonic-gate static void
server_init_dispatch_15(void)12947c478bd9Sstevel@tonic-gate server_init_dispatch_15(void)
12957c478bd9Sstevel@tonic-gate {
12967c478bd9Sstevel@tonic-gate server_init_dispatch_13();
12977c478bd9Sstevel@tonic-gate debug("server_init_dispatch_15");
12987c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
12997c478bd9Sstevel@tonic-gate dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose);
13007c478bd9Sstevel@tonic-gate }
13017c478bd9Sstevel@tonic-gate static void
server_init_dispatch(void)13027c478bd9Sstevel@tonic-gate server_init_dispatch(void)
13037c478bd9Sstevel@tonic-gate {
13047c478bd9Sstevel@tonic-gate if (compat20)
13057c478bd9Sstevel@tonic-gate server_init_dispatch_20();
13067c478bd9Sstevel@tonic-gate else if (compat13)
13077c478bd9Sstevel@tonic-gate server_init_dispatch_13();
13087c478bd9Sstevel@tonic-gate else
13097c478bd9Sstevel@tonic-gate server_init_dispatch_15();
13107c478bd9Sstevel@tonic-gate }
1311