summaryrefslogtreecommitdiff
path: root/serverloop.c
diff options
context:
space:
mode:
Diffstat (limited to 'serverloop.c')
-rw-r--r--serverloop.c1242
1 files changed, 403 insertions, 839 deletions
diff --git a/serverloop.c b/serverloop.c
index 306ac36b..340b19a5 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: serverloop.c,v 1.178 2015/02/20 22:17:21 djm Exp $ */
+/* $OpenBSD: serverloop.c,v 1.222 2020/01/30 07:21:38 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -37,7 +37,6 @@
#include "includes.h"
-#include <sys/param.h> /* MIN MAX */
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
@@ -50,6 +49,7 @@
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
+#include <limits.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
@@ -59,7 +59,7 @@
#include "openbsd-compat/sys-queue.h"
#include "xmalloc.h"
#include "packet.h"
-#include "buffer.h"
+#include "sshbuf.h"
#include "log.h"
#include "misc.h"
#include "servconf.h"
@@ -67,9 +67,8 @@
#include "sshpty.h"
#include "channels.h"
#include "compat.h"
-#include "ssh1.h"
#include "ssh2.h"
-#include "key.h"
+#include "sshkey.h"
#include "cipher.h"
#include "kex.h"
#include "hostfile.h"
@@ -78,34 +77,15 @@
#include "dispatch.h"
#include "auth-options.h"
#include "serverloop.h"
-#include "roaming.h"
#include "ssherr.h"
extern ServerOptions options;
/* XXX */
extern Authctxt *the_authctxt;
+extern struct sshauthopt *auth_opts;
extern int use_privsep;
-static Buffer stdin_buffer; /* Buffer for stdin data. */
-static Buffer stdout_buffer; /* Buffer for stdout data. */
-static Buffer stderr_buffer; /* Buffer for stderr data. */
-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 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. */
-static int fdin_is_tty = 0; /* fdin points to a tty. */
-static int connection_in; /* Connection to client (input). */
-static int connection_out; /* Connection to client (output). */
-static int connection_closed = 0; /* Connection to client closed. */
-static u_int buffer_high; /* "Soft" max buffer size. */
static int no_more_sessions = 0; /* Disallow further sessions. */
/*
@@ -119,7 +99,21 @@ static volatile sig_atomic_t child_terminated = 0; /* The child has terminated.
static volatile sig_atomic_t received_sigterm = 0;
/* prototypes */
-static void server_init_dispatch(void);
+static void server_init_dispatch(struct ssh *);
+
+/* requested tunnel forwarding interface(s), shared with session.c */
+char *tun_fwd_ifnames = NULL;
+
+/* returns 1 if bind to specified port by specified user is permitted */
+static int
+bind_permitted(int port, uid_t uid)
+{
+ if (use_privsep)
+ return 1; /* allow system to decide */
+ if (port < IPPORT_RESERVED && uid != 0)
+ return 0;
+ return 1;
+}
/*
* we write to this pipe if a SIGCHLD is caught in order to avoid
@@ -129,7 +123,7 @@ static int notify_pipe[2];
static void
notify_setup(void)
{
- if (pipe(notify_pipe) < 0) {
+ if (pipe(notify_pipe) == -1) {
error("pipe(notify_pipe) failed %s", strerror(errno));
} else if ((fcntl(notify_pipe[0], F_SETFD, FD_CLOEXEC) == -1) ||
(fcntl(notify_pipe[1], F_SETFD, FD_CLOEXEC) == -1)) {
@@ -163,7 +157,7 @@ notify_done(fd_set *readset)
if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset))
while (read(notify_pipe[0], &c, 1) != -1)
- debug2("notify_done: reading");
+ debug2("%s: reading", __func__);
}
/*ARGSUSED*/
@@ -172,9 +166,6 @@ sigchld_handler(int sig)
{
int save_errno = errno;
child_terminated = 1;
-#ifndef _UNICOS
- mysignal(SIGCHLD, sigchld_handler);
-#endif
notify_parent();
errno = save_errno;
}
@@ -186,72 +177,18 @@ sigterm_handler(int sig)
received_sigterm = sig;
}
-/*
- * Make packets from buffered stderr data, and buffer it for sending
- * to the client.
- */
-static void
-make_packets_from_stderr_data(void)
-{
- u_int len;
-
- /* Send buffered stderr data to the client. */
- while (buffer_len(&stderr_buffer) > 0 &&
- packet_not_very_much_data_to_write()) {
- len = buffer_len(&stderr_buffer);
- if (packet_is_interactive()) {
- if (len > 512)
- len = 512;
- } else {
- /* Keep the packets at reasonable size. */
- if (len > packet_get_maxsize())
- len = packet_get_maxsize();
- }
- packet_start(SSH_SMSG_STDERR_DATA);
- packet_put_string(buffer_ptr(&stderr_buffer), len);
- packet_send();
- buffer_consume(&stderr_buffer, len);
- stderr_bytes += len;
- }
-}
-
-/*
- * Make packets from buffered stdout data, and buffer it for sending to the
- * client.
- */
static void
-make_packets_from_stdout_data(void)
+client_alive_check(struct ssh *ssh)
{
- u_int len;
-
- /* Send buffered stdout data to the client. */
- while (buffer_len(&stdout_buffer) > 0 &&
- packet_not_very_much_data_to_write()) {
- len = buffer_len(&stdout_buffer);
- if (packet_is_interactive()) {
- if (len > 512)
- len = 512;
- } else {
- /* Keep the packets at reasonable size. */
- if (len > packet_get_maxsize())
- len = packet_get_maxsize();
- }
- packet_start(SSH_SMSG_STDOUT_DATA);
- packet_put_string(buffer_ptr(&stdout_buffer), len);
- packet_send();
- buffer_consume(&stdout_buffer, len);
- stdout_bytes += len;
- }
-}
-
-static void
-client_alive_check(void)
-{
- int channel_id;
+ char remote_id[512];
+ int r, channel_id;
/* timeout, check to see how many we have had */
- if (packet_inc_alive_timeouts() > options.client_alive_count_max) {
- logit("Timeout, client not responding.");
+ if (options.client_alive_count_max > 0 &&
+ ssh_packet_inc_alive_timeouts(ssh) >
+ options.client_alive_count_max) {
+ sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
+ logit("Timeout, client not responding from %s", remote_id);
cleanup_exit(255);
}
@@ -259,14 +196,18 @@ client_alive_check(void)
* send a bogus global/channel request with "wantreply",
* we should get back a failure
*/
- if ((channel_id = channel_find_open()) == -1) {
- packet_start(SSH2_MSG_GLOBAL_REQUEST);
- packet_put_cstring("keepalive@openssh.com");
- packet_put_char(1); /* boolean: want reply */
+ if ((channel_id = channel_find_open(ssh)) == -1) {
+ if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
+ (r = sshpkt_put_cstring(ssh, "keepalive@openssh.com"))
+ != 0 ||
+ (r = sshpkt_put_u8(ssh, 1)) != 0) /* boolean: want reply */
+ fatal("%s: %s", __func__, ssh_err(r));
} else {
- channel_request_start(channel_id, "keepalive@openssh.com", 1);
+ channel_request_start(ssh, channel_id,
+ "keepalive@openssh.com", 1);
}
- packet_send();
+ if ((r = sshpkt_send(ssh)) != 0)
+ fatal("%s: %s", __func__, ssh_err(r));
}
/*
@@ -276,22 +217,25 @@ client_alive_check(void)
* for the duration of the wait (0 = infinite).
*/
static void
-wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
- u_int *nallocp, u_int64_t max_time_milliseconds)
+wait_until_can_do_something(struct ssh *ssh,
+ int connection_in, int connection_out,
+ fd_set **readsetp, fd_set **writesetp, int *maxfdp,
+ u_int *nallocp, u_int64_t max_time_ms)
{
struct timeval tv, *tvp;
int ret;
time_t minwait_secs = 0;
int client_alive_scheduled = 0;
- int program_alive_scheduled = 0;
+ /* time we last heard from the client OR sent a keepalive */
+ static time_t last_client_time;
/* Allocate and update select() masks for channel descriptors. */
- channel_prepare_select(readsetp, writesetp, maxfdp, nallocp,
- &minwait_secs, 0);
+ channel_prepare_select(ssh, readsetp, writesetp, maxfdp,
+ nallocp, &minwait_secs);
+ /* XXX need proper deadline system for rekey/client alive */
if (minwait_secs != 0)
- max_time_milliseconds = MIN(max_time_milliseconds,
- (u_int)minwait_secs * 1000);
+ max_time_ms = MINIMUM(max_time_ms, (u_int)minwait_secs * 1000);
/*
* if using client_alive, set the max timeout accordingly,
@@ -301,67 +245,43 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
* this could be randomized somewhat to make traffic
* analysis more difficult, but we're not doing it yet.
*/
- if (compat20 &&
- max_time_milliseconds == 0 && options.client_alive_interval) {
- client_alive_scheduled = 1;
- max_time_milliseconds =
- (u_int64_t)options.client_alive_interval * 1000;
+ if (options.client_alive_interval) {
+ uint64_t keepalive_ms =
+ (uint64_t)options.client_alive_interval * 1000;
+
+ if (max_time_ms == 0 || max_time_ms > keepalive_ms) {
+ max_time_ms = keepalive_ms;
+ client_alive_scheduled = 1;
+ }
}
- if (compat20) {
#if 0
- /* wrong: bad condition XXX */
- if (channel_not_very_much_buffered_data())
+ /* wrong: bad condition XXX */
+ if (channel_not_very_much_buffered_data())
#endif
- FD_SET(connection_in, *readsetp);
- } else {
- /*
- * Read packets from the client unless we have too much
- * buffered stdin or channel data.
- */
- if (buffer_len(&stdin_buffer) < buffer_high &&
- channel_not_very_much_buffered_data())
- FD_SET(connection_in, *readsetp);
- /*
- * If there is not too much data already buffered going to
- * the client, try to get some more data from the program.
- */
- if (packet_not_very_much_data_to_write()) {
- program_alive_scheduled = child_terminated;
- if (!fdout_eof)
- FD_SET(fdout, *readsetp);
- if (!fderr_eof)
- FD_SET(fderr, *readsetp);
- }
- /*
- * If we have buffered data, try to write some of that data
- * to the program.
- */
- if (fdin != -1 && buffer_len(&stdin_buffer) > 0)
- FD_SET(fdin, *writesetp);
- }
+ FD_SET(connection_in, *readsetp);
notify_prepare(*readsetp);
/*
* If we have buffered packet data going to the client, mark that
* descriptor.
*/
- if (packet_have_data_to_write())
+ if (ssh_packet_have_data_to_write(ssh))
FD_SET(connection_out, *writesetp);
/*
* If child has terminated and there is enough buffer space to read
* from it, then read as much as is available and exit.
*/
- if (child_terminated && packet_not_very_much_data_to_write())
- if (max_time_milliseconds == 0 || client_alive_scheduled)
- max_time_milliseconds = 100;
+ if (child_terminated && ssh_packet_not_very_much_data_to_write(ssh))
+ if (max_time_ms == 0 || client_alive_scheduled)
+ max_time_ms = 100;
- if (max_time_milliseconds == 0)
+ if (max_time_ms == 0)
tvp = NULL;
else {
- tv.tv_sec = max_time_milliseconds / 1000;
- tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
+ tv.tv_sec = max_time_ms / 1000;
+ tv.tv_usec = 1000 * (max_time_ms % 1000);
tvp = &tv;
}
@@ -373,14 +293,19 @@ 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();
- if (!compat20 && program_alive_scheduled && fdin_is_tty) {
- if (!fdout_eof)
- FD_SET(fdout, *readsetp);
- if (!fderr_eof)
- FD_SET(fderr, *readsetp);
+ } else if (client_alive_scheduled) {
+ time_t now = monotime();
+
+ /*
+ * If the select timed out, or returned for some other reason
+ * but we haven't heard from the client in time, send keepalive.
+ */
+ if (ret == 0 || (last_client_time != 0 && last_client_time +
+ options.client_alive_interval <= now)) {
+ client_alive_check(ssh);
+ last_client_time = now;
+ } else if (FD_ISSET(connection_in, *readsetp)) {
+ last_client_time = now;
}
}
@@ -391,415 +316,64 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
* Processes input from the client and the program. Input data is stored
* in buffers and processed later.
*/
-static void
-process_input(fd_set *readset)
+static int
+process_input(struct ssh *ssh, fd_set *readset, int connection_in)
{
- int len;
+ int r, len;
char buf[16384];
/* Read and buffer any input data from the client. */
if (FD_ISSET(connection_in, readset)) {
- int cont = 0;
- len = roaming_read(connection_in, buf, sizeof(buf), &cont);
+ len = read(connection_in, buf, sizeof(buf));
if (len == 0) {
- if (cont)
- return;
- verbose("Connection closed by %.100s",
- get_remote_ipaddr());
- connection_closed = 1;
- if (compat20)
- return;
- cleanup_exit(255);
- } else if (len < 0) {
+ verbose("Connection closed by %.100s port %d",
+ ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
+ return -1;
+ } else if (len == -1) {
if (errno != EINTR && errno != EAGAIN &&
errno != EWOULDBLOCK) {
verbose("Read error from remote host "
- "%.100s: %.100s",
- get_remote_ipaddr(), strerror(errno));
+ "%.100s port %d: %.100s",
+ ssh_remote_ipaddr(ssh),
+ ssh_remote_port(ssh), strerror(errno));
cleanup_exit(255);
}
} else {
/* Buffer any received data. */
- packet_process_incoming(buf, len);
- }
- }
- if (compat20)
- return;
-
- /* Read and buffer any available stdout data from the program. */
- if (!fdout_eof && FD_ISSET(fdout, readset)) {
- errno = 0;
- len = read(fdout, buf, sizeof(buf));
- if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
- errno == EWOULDBLOCK) && !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;
- }
- }
- /* 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 ||
- errno == EWOULDBLOCK) && !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);
+ if ((r = ssh_packet_process_incoming(ssh, buf, len))
+ != 0)
+ fatal("%s: ssh_packet_process_incoming: %s",
+ __func__, ssh_err(r));
}
}
+ return 0;
}
/*
* Sends data from internal buffers to client program stdin.
*/
static void
-process_output(fd_set *writeset)
+process_output(struct ssh *ssh, fd_set *writeset, int connection_out)
{
- struct termios tio;
- u_char *data;
- u_int dlen;
- int len;
-
- /* Write buffered data to program stdin. */
- if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) {
- data = buffer_ptr(&stdin_buffer);
- dlen = buffer_len(&stdin_buffer);
- len = write(fdin, data, dlen);
- if (len < 0 &&
- (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
- /* do nothing */
- } else if (len <= 0) {
- if (fdin != fdout)
- close(fdin);
- else
- shutdown(fdin, SHUT_WR); /* We will no longer send. */
- fdin = -1;
- } else {
- /* Successful write. */
- if (fdin_is_tty && dlen >= 1 && data[0] != '\r' &&
- tcgetattr(fdin, &tio) == 0 &&
- !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
- /*
- * Simulate echo to reduce the impact of
- * traffic analysis
- */
- packet_send_ignore(len);
- packet_send();
- }
- /* Consume the data from the buffer. */
- buffer_consume(&stdin_buffer, len);
- /* Update the count of bytes written to the program. */
- stdin_bytes += len;
+ int r;
+
+ /* Send any buffered packet data to the client. */
+ if (FD_ISSET(connection_out, writeset)) {
+ if ((r = ssh_packet_write_poll(ssh)) != 0) {
+ sshpkt_fatal(ssh, r, "%s: ssh_packet_write_poll",
+ __func__);
}
}
- /* Send any buffered packet data to the client. */
- if (FD_ISSET(connection_out, writeset))
- packet_write_poll();
}
-/*
- * Wait until all buffered output has been sent to the client.
- * This is used when the program terminates.
- */
static void
-drain_output(void)
+process_buffered_input_packets(struct ssh *ssh)
{
- /* Send any buffered stdout data to the client. */
- if (buffer_len(&stdout_buffer) > 0) {
- packet_start(SSH_SMSG_STDOUT_DATA);
- packet_put_string(buffer_ptr(&stdout_buffer),
- buffer_len(&stdout_buffer));
- packet_send();
- /* Update the count of sent bytes. */
- stdout_bytes += buffer_len(&stdout_buffer);
- }
- /* Send any buffered stderr data to the client. */
- if (buffer_len(&stderr_buffer) > 0) {
- packet_start(SSH_SMSG_STDERR_DATA);
- packet_put_string(buffer_ptr(&stderr_buffer),
- buffer_len(&stderr_buffer));
- packet_send();
- /* Update the count of sent bytes. */
- stderr_bytes += buffer_len(&stderr_buffer);
- }
- /* Wait until all buffered data has been written to the client. */
- packet_write_wait();
+ ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, NULL);
}
static void
-process_buffered_input_packets(void)
-{
- dispatch_run(DISPATCH_NONBLOCK, NULL, active_state);
-}
-
-/*
- * Performs the interactive session. This handles data transmission between
- * the client and the program. Note that the notion of stdin, stdout, and
- * stderr in this function is sort of reversed: this function writes to
- * stdin (of the child program), and reads from stdout and stderr (of the
- * child program).
- */
-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;
- 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. */
- u_int64_t max_time_milliseconds;
- u_int previous_stdout_buffer_bytes;
- u_int stdout_buffer_bytes;
- int type;
-
- debug("Entering interactive session.");
-
- /* Initialize the SIGCHLD kludge. */
- 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;
- fderr = fderr_arg;
-
- /* nonblocking IO */
- set_nonblock(fdin);
- set_nonblock(fdout);
- /* we don't have stderr for interactive terminal sessions, see below */
- if (fderr != -1)
- set_nonblock(fderr);
-
- if (!(datafellows & SSH_BUG_IGNOREMSG) && isatty(fdin))
- fdin_is_tty = 1;
-
- 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. */
- if (packet_is_interactive())
- buffer_high = 4096;
- else
- buffer_high = 64 * 1024;
-
-#if 0
- /* Initialize max_fd to the maximum of the known file descriptors. */
- max_fd = MAX(connection_in, connection_out);
- max_fd = MAX(max_fd, fdin);
- max_fd = MAX(max_fd, fdout);
- if (fderr != -1)
- max_fd = MAX(max_fd, fderr);
-#endif
-
- /* Initialize Initialize buffers. */
- buffer_init(&stdin_buffer);
- buffer_init(&stdout_buffer);
- buffer_init(&stderr_buffer);
-
- /*
- * 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.
- */
- if (fderr == -1)
- fderr_eof = 1;
-
- server_init_dispatch();
-
- /* Main loop of the server for the interactive session mode. */
- for (;;) {
-
- /* Process buffered packets from the client. */
- process_buffered_input_packets();
-
- /*
- * If we have received eof, and there is no more pending
- * input data, cause a real eof by closing fdin.
- */
- if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) {
- if (fdin != fdout)
- close(fdin);
- else
- shutdown(fdin, SHUT_WR); /* We will no longer send. */
- fdin = -1;
- }
- /* Make packets from buffered stderr data to send to the client. */
- make_packets_from_stderr_data();
-
- /*
- * Make packets from buffered stdout data to send to the
- * client. If there is very little to send, this arranges to
- * not send them now, but to wait a short while to see if we
- * are getting more data. This is necessary, as some systems
- * wake up readers from a pty after each separate character.
- */
- max_time_milliseconds = 0;
- stdout_buffer_bytes = buffer_len(&stdout_buffer);
- if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 &&
- stdout_buffer_bytes != previous_stdout_buffer_bytes) {
- /* try again after a while */
- max_time_milliseconds = 10;
- } else {
- /* Send it now. */
- make_packets_from_stdout_data();
- }
- previous_stdout_buffer_bytes = buffer_len(&stdout_buffer);
-
- /* Send channel data to the client. */
- if (packet_not_very_much_data_to_write())
- channel_output_poll();
-
- /*
- * Bail out of the loop if the program has closed its output
- * descriptors, and we have no more data to send to the
- * client, and there is no pending buffered data.
- */
- if (fdout_eof && fderr_eof && !packet_have_data_to_write() &&
- buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) {
- if (!channel_still_open())
- break;
- if (!waiting_termination) {
- const char *s = "Waiting for forwarded connections to terminate...\r\n";
- char *cp;
- waiting_termination = 1;
- buffer_append(&stderr_buffer, s, strlen(s));
-
- /* Display list of open channels. */
- cp = channel_open_message();
- buffer_append(&stderr_buffer, cp, strlen(cp));
- free(cp);
- }
- }
- max_fd = MAX(connection_in, connection_out);
- max_fd = MAX(max_fd, fdin);
- max_fd = MAX(max_fd, fdout);
- max_fd = MAX(max_fd, fderr);
- max_fd = MAX(max_fd, notify_pipe[0]);
-
- /* Sleep in select() until we can do something. */
- wait_until_can_do_something(&readset, &writeset, &max_fd,
- &nalloc, max_time_milliseconds);
-
- if (received_sigterm) {
- logit("Exiting on signal %d", (int)received_sigterm);
- /* Clean up sessions, utmp, etc. */
- cleanup_exit(255);
- }
-
- /* Process any channel events. */
- channel_after_select(readset, writeset);
-
- /* Process input from the client and from program stdout/stderr. */
- process_input(readset);
-
- /* Process output to the client and to program stdin. */
- process_output(writeset);
- }
- free(readset);
- free(writeset);
-
- /* Cleanup and termination code. */
-
- /* Wait until all output has been sent to the client. */
- 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);
-
- /* Free and clear the buffers. */
- buffer_free(&stdin_buffer);
- buffer_free(&stdout_buffer);
- buffer_free(&stderr_buffer);
-
- /* Close the file descriptors. */
- if (fdout != -1)
- close(fdout);
- fdout = -1;
- fdout_eof = 1;
- if (fderr != -1)
- close(fderr);
- fderr = -1;
- fderr_eof = 1;
- if (fdin != -1)
- close(fdin);
- fdin = -1;
-
- channel_free_all();
-
- /* We no longer want our SIGCHLD handler to be called. */
- mysignal(SIGCHLD, SIG_DFL);
-
- while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0)
- if (errno != EINTR)
- packet_disconnect("wait: %.100s", strerror(errno));
- if (wait_pid != pid)
- error("Strange, wait returned pid %ld, expected %ld",
- (long)wait_pid, (long)pid);
-
- /* Check if it exited normally. */
- if (WIFEXITED(wait_status)) {
- /* Yes, normal exit. Get exit status and send it to the client. */
- debug("Command exited with status %d.", WEXITSTATUS(wait_status));
- packet_start(SSH_SMSG_EXITSTATUS);
- packet_put_int(WEXITSTATUS(wait_status));
- packet_send();
- packet_write_wait();
-
- /*
- * Wait for exit confirmation. Note that there might be
- * other packets coming before it; however, the program has
- * already died so we just ignore them. The client is
- * supposed to respond with the confirmation when it receives
- * the exit status.
- */
- do {
- type = packet_read();
- }
- while (type != SSH_CMSG_EXIT_CONFIRMATION);
-
- debug("Received exit confirmation.");
- return;
- }
- /* Check if the program terminated due to a signal. */
- if (WIFSIGNALED(wait_status))
- packet_disconnect("Command terminated on signal %d.",
- WTERMSIG(wait_status));
-
- /* Some weird exit cause. Just exit. */
- packet_disconnect("wait returned status %04x.", wait_status);
- /* NOTREACHED */
-}
-
-static void
-collect_children(void)
+collect_children(struct ssh *ssh)
{
pid_t pid;
sigset_t oset, nset;
@@ -812,56 +386,58 @@ collect_children(void)
if (child_terminated) {
debug("Received SIGCHLD.");
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
- (pid < 0 && errno == EINTR))
+ (pid == -1 && errno == EINTR))
if (pid > 0)
- session_close_by_pid(pid, status);
+ session_close_by_pid(ssh, pid, status);
child_terminated = 0;
}
sigprocmask(SIG_SETMASK, &oset, NULL);
}
void
-server_loop2(Authctxt *authctxt)
+server_loop2(struct ssh *ssh, Authctxt *authctxt)
{
fd_set *readset = NULL, *writeset = NULL;
- int rekeying = 0, max_fd;
- u_int nalloc = 0;
+ int max_fd;
+ u_int nalloc = 0, connection_in, connection_out;
u_int64_t rekey_timeout_ms = 0;
debug("Entering interactive session for SSH2.");
- mysignal(SIGCHLD, sigchld_handler);
+ ssh_signal(SIGCHLD, sigchld_handler);
child_terminated = 0;
- connection_in = packet_get_connection_in();
- connection_out = packet_get_connection_out();
+ connection_in = ssh_packet_get_connection_in(ssh);
+ connection_out = ssh_packet_get_connection_out(ssh);
if (!use_privsep) {
- signal(SIGTERM, sigterm_handler);
- signal(SIGINT, sigterm_handler);
- signal(SIGQUIT, sigterm_handler);
+ ssh_signal(SIGTERM, sigterm_handler);
+ ssh_signal(SIGINT, sigterm_handler);
+ ssh_signal(SIGQUIT, sigterm_handler);
}
notify_setup();
- max_fd = MAX(connection_in, connection_out);
- max_fd = MAX(max_fd, notify_pipe[0]);
+ max_fd = MAXIMUM(connection_in, connection_out);
+ max_fd = MAXIMUM(max_fd, notify_pipe[0]);
- server_init_dispatch();
+ server_init_dispatch(ssh);
for (;;) {
- process_buffered_input_packets();
-
- rekeying = (active_state->kex != NULL && !active_state->kex->done);
-
- if (!rekeying && packet_not_very_much_data_to_write())
- channel_output_poll();
- if (options.rekey_interval > 0 && compat20 && !rekeying)
- rekey_timeout_ms = packet_get_rekey_timeout() * 1000;
- else
+ process_buffered_input_packets(ssh);
+
+ if (!ssh_packet_is_rekeying(ssh) &&
+ ssh_packet_not_very_much_data_to_write(ssh))
+ channel_output_poll(ssh);
+ if (options.rekey_interval > 0 &&
+ !ssh_packet_is_rekeying(ssh)) {
+ rekey_timeout_ms = ssh_packet_get_rekey_timeout(ssh) *
+ 1000;
+ } else {
rekey_timeout_ms = 0;
+ }
- wait_until_can_do_something(&readset, &writeset, &max_fd,
- &nalloc, rekey_timeout_ms);
+ wait_until_can_do_something(ssh, connection_in, connection_out,
+ &readset, &writeset, &max_fd, &nalloc, rekey_timeout_ms);
if (received_sigterm) {
logit("Exiting on signal %d", (int)received_sigterm);
@@ -869,34 +445,27 @@ server_loop2(Authctxt *authctxt)
cleanup_exit(255);
}
- collect_children();
- if (!rekeying) {
- channel_after_select(readset, writeset);
- if (packet_need_rekeying()) {
- debug("need rekeying");
- active_state->kex->done = 0;
- kex_send_kexinit(active_state);
- }
- }
- process_input(readset);
- if (connection_closed)
+ collect_children(ssh);
+ if (!ssh_packet_is_rekeying(ssh))
+ channel_after_select(ssh, readset, writeset);
+ if (process_input(ssh, readset, connection_in) < 0)
break;
- process_output(writeset);
+ process_output(ssh, writeset, connection_out);
}
- collect_children();
+ collect_children(ssh);
free(readset);
free(writeset);
/* free all channels, no more reads and writes */
- channel_free_all();
+ channel_free_all(ssh);
/* free remaining sessions, e.g. remove wtmp entries */
- session_destroy_all(NULL);
+ session_destroy_all(ssh, NULL);
}
static int
-server_input_keep_alive(int type, u_int32_t seq, void *ctxt)
+server_input_keep_alive(int type, u_int32_t seq, struct ssh *ssh)
{
debug("Got %d/%u for keepalive", type, seq);
/*
@@ -904,109 +473,88 @@ server_input_keep_alive(int type, u_int32_t seq, void *ctxt)
* even if this was generated by something other than
* the bogus CHANNEL_REQUEST we send for keepalives.
*/
- packet_set_alive_timeouts(0);
- return 0;
-}
-
-static int
-server_input_stdin_data(int type, u_int32_t seq, void *ctxt)
-{
- char *data;
- u_int data_len;
-
- /* Stdin data from the client. Append it to the buffer. */
- /* Ignore any data if the client has closed stdin. */
- if (fdin == -1)
- return 0;
- data = packet_get_string(&data_len);
- packet_check_eom();
- buffer_append(&stdin_buffer, data, data_len);
- explicit_bzero(data, data_len);
- free(data);
- return 0;
-}
-
-static int
-server_input_eof(int type, u_int32_t seq, void *ctxt)
-{
- /*
- * Eof from the client. The stdin descriptor to the
- * program will be closed when all buffered data has
- * drained.
- */
- debug("EOF received for stdin.");
- packet_check_eom();
- stdin_eof = 1;
- return 0;
-}
-
-static int
-server_input_window_size(int type, u_int32_t seq, void *ctxt)
-{
- 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_check_eom();
- if (fdin != -1)
- pty_change_window_size(fdin, row, col, xpixel, ypixel);
+ ssh_packet_set_alive_timeouts(ssh, 0);
return 0;
}
static Channel *
-server_request_direct_tcpip(void)
+server_request_direct_tcpip(struct ssh *ssh, int *reason, const char **errmsg)
{
Channel *c = NULL;
- char *target, *originator;
- u_short target_port, originator_port;
-
- target = packet_get_string(NULL);
- target_port = packet_get_int();
- originator = packet_get_string(NULL);
- originator_port = packet_get_int();
- packet_check_eom();
-
- debug("server_request_direct_tcpip: originator %s port %d, target %s "
- "port %d", originator, originator_port, target, target_port);
+ char *target = NULL, *originator = NULL;
+ u_int target_port = 0, originator_port = 0;
+ int r;
+
+ if ((r = sshpkt_get_cstring(ssh, &target, NULL)) != 0 ||
+ (r = sshpkt_get_u32(ssh, &target_port)) != 0 ||
+ (r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 ||
+ (r = sshpkt_get_u32(ssh, &originator_port)) != 0 ||
+ (r = sshpkt_get_end(ssh)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
+ if (target_port > 0xFFFF) {
+ error("%s: invalid target port", __func__);
+ *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
+ goto out;
+ }
+ if (originator_port > 0xFFFF) {
+ error("%s: invalid originator port", __func__);
+ *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
+ goto out;
+ }
+
+ debug("%s: originator %s port %u, target %s port %u", __func__,
+ originator, originator_port, target, target_port);
/* XXX fine grained permissions */
if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 &&
- !no_port_forwarding_flag) {
- c = channel_connect_to_port(target, target_port,
- "direct-tcpip", "direct-tcpip");
+ auth_opts->permit_port_forwarding_flag &&
+ !options.disable_forwarding) {
+ c = channel_connect_to_port(ssh, target, target_port,
+ "direct-tcpip", "direct-tcpip", reason, errmsg);
} else {
logit("refused local port forward: "
"originator %s port %d, target %s port %d",
originator, originator_port, target, target_port);
+ if (reason != NULL)
+ *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
}
+ out:
free(originator);
free(target);
-
return c;
}
static Channel *
-server_request_direct_streamlocal(void)
+server_request_direct_streamlocal(struct ssh *ssh)
{
Channel *c = NULL;
- char *target, *originator;
- u_short originator_port;
+ char *target = NULL, *originator = NULL;
+ u_int originator_port = 0;
+ struct passwd *pw = the_authctxt->pw;
+ int r;
+
+ if (pw == NULL || !the_authctxt->valid)
+ fatal("%s: no/invalid user", __func__);
- target = packet_get_string(NULL);
- originator = packet_get_string(NULL);
- originator_port = packet_get_int();
- packet_check_eom();
+ if ((r = sshpkt_get_cstring(ssh, &target, NULL)) != 0 ||
+ (r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 ||
+ (r = sshpkt_get_u32(ssh, &originator_port)) != 0 ||
+ (r = sshpkt_get_end(ssh)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
+ if (originator_port > 0xFFFF) {
+ error("%s: invalid originator port", __func__);
+ goto out;
+ }
- debug("server_request_direct_streamlocal: originator %s port %d, target %s",
+ debug("%s: originator %s port %d, target %s", __func__,
originator, originator_port, target);
/* XXX fine grained permissions */
if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 &&
- !no_port_forwarding_flag) {
- c = channel_connect_to_path(target,
+ auth_opts->permit_port_forwarding_flag &&
+ !options.disable_forwarding && (pw->pw_uid == 0 || use_privsep)) {
+ c = channel_connect_to_path(ssh, target,
"direct-streamlocal@openssh.com", "direct-streamlocal");
} else {
logit("refused streamlocal port forward: "
@@ -1014,69 +562,94 @@ server_request_direct_streamlocal(void)
originator, originator_port, target);
}
+out:
free(originator);
free(target);
-
return c;
}
static Channel *
-server_request_tun(void)
+server_request_tun(struct ssh *ssh)
{
Channel *c = NULL;
- int mode, tun;
- int sock;
+ u_int mode, tun;
+ int r, sock;
+ char *tmp, *ifname = NULL;
- mode = packet_get_int();
+ if ((r = sshpkt_get_u32(ssh, &mode)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse mode", __func__);
switch (mode) {
case SSH_TUNMODE_POINTOPOINT:
case SSH_TUNMODE_ETHERNET:
break;
default:
- packet_send_debug("Unsupported tunnel device mode.");
+ ssh_packet_send_debug(ssh, "Unsupported tunnel device mode.");
return NULL;
}
if ((options.permit_tun & mode) == 0) {
- packet_send_debug("Server has rejected tunnel device "
+ ssh_packet_send_debug(ssh, "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)
+ if ((r = sshpkt_get_u32(ssh, &tun)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse device", __func__);
+ if (tun > INT_MAX) {
+ debug("%s: invalid tun", __func__);
+ goto done;
+ }
+ if (auth_opts->force_tun_device != -1) {
+ if (tun != SSH_TUNID_ANY &&
+ auth_opts->force_tun_device != (int)tun)
goto done;
- tun = forced_tun_device;
+ tun = auth_opts->force_tun_device;
}
- sock = tun_open(tun, mode);
+ sock = tun_open(tun, mode, &ifname);
if (sock < 0)
goto done;
- c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
+ debug("Tunnel forwarding using interface %s", ifname);
+
+ c = channel_new(ssh, "tun", SSH_CHANNEL_OPEN, sock, sock, -1,
CHAN_TCP_WINDOW_DEFAULT, 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,
+ channel_register_filter(ssh, c->self, sys_tun_infilter,
sys_tun_outfilter, NULL, NULL);
#endif
+ /*
+ * Update the list of names exposed to the session
+ * XXX remove these if the tunnels are closed (won't matter
+ * much if they are already in the environment though)
+ */
+ tmp = tun_fwd_ifnames;
+ xasprintf(&tun_fwd_ifnames, "%s%s%s",
+ tun_fwd_ifnames == NULL ? "" : tun_fwd_ifnames,
+ tun_fwd_ifnames == NULL ? "" : ",",
+ ifname);
+ free(tmp);
+ free(ifname);
+
done:
if (c == NULL)
- packet_send_debug("Failed to open the tunnel device.");
+ ssh_packet_send_debug(ssh, "Failed to open the tunnel device.");
return c;
}
static Channel *
-server_request_session(void)
+server_request_session(struct ssh *ssh)
{
Channel *c;
+ int r;
debug("input_session_request");
- packet_check_eom();
+ if ((r = sshpkt_get_end(ssh)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
if (no_more_sessions) {
- packet_disconnect("Possible attack: attempt to open a session "
- "after additional sessions disabled");
+ ssh_packet_disconnect(ssh, "Possible attack: attempt to open a "
+ "session after additional sessions disabled");
}
/*
@@ -1085,79 +658,84 @@ server_request_session(void)
* SSH_CHANNEL_LARVAL. Additionally, a callback for handling all
* CHANNEL_REQUEST messages is registered.
*/
- c = channel_new("session", SSH_CHANNEL_LARVAL,
+ c = channel_new(ssh, "session", SSH_CHANNEL_LARVAL,
-1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT,
0, "server-session", 1);
if (session_open(the_authctxt, c->self) != 1) {
debug("session open failed, free channel %d", c->self);
- channel_free(c);
+ channel_free(ssh, c);
return NULL;
}
- channel_register_cleanup(c->self, session_close_by_channel, 0);
+ channel_register_cleanup(ssh, c->self, session_close_by_channel, 0);
return c;
}
static int
-server_input_channel_open(int type, u_int32_t seq, void *ctxt)
+server_input_channel_open(int type, u_int32_t seq, struct ssh *ssh)
{
Channel *c = NULL;
- char *ctype;
- int rchan;
- u_int rmaxpack, rwindow, len;
-
- ctype = packet_get_string(&len);
- rchan = packet_get_int();
- rwindow = packet_get_int();
- rmaxpack = packet_get_int();
-
- debug("server_input_channel_open: ctype %s rchan %d win %d max %d",
+ char *ctype = NULL;
+ const char *errmsg = NULL;
+ int r, reason = SSH2_OPEN_CONNECT_FAILED;
+ u_int rchan = 0, rmaxpack = 0, rwindow = 0;
+
+ if ((r = sshpkt_get_cstring(ssh, &ctype, NULL)) != 0 ||
+ (r = sshpkt_get_u32(ssh, &rchan)) != 0 ||
+ (r = sshpkt_get_u32(ssh, &rwindow)) != 0 ||
+ (r = sshpkt_get_u32(ssh, &rmaxpack)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
+ debug("%s: ctype %s rchan %u win %u max %u", __func__,
ctype, rchan, rwindow, rmaxpack);
if (strcmp(ctype, "session") == 0) {
- c = server_request_session();
+ c = server_request_session(ssh);
} else if (strcmp(ctype, "direct-tcpip") == 0) {
- c = server_request_direct_tcpip();
+ c = server_request_direct_tcpip(ssh, &reason, &errmsg);
} else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) {
- c = server_request_direct_streamlocal();
+ c = server_request_direct_streamlocal(ssh);
} else if (strcmp(ctype, "tun@openssh.com") == 0) {
- c = server_request_tun();
+ c = server_request_tun(ssh);
}
if (c != NULL) {
- debug("server_input_channel_open: confirm %s", ctype);
+ debug("%s: confirm %s", __func__, ctype);
c->remote_id = rchan;
+ c->have_remote_id = 1;
c->remote_window = rwindow;
c->remote_maxpacket = rmaxpack;
if (c->type != SSH_CHANNEL_CONNECTING) {
- packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
- packet_put_int(c->remote_id);
- packet_put_int(c->self);
- packet_put_int(c->local_window);
- packet_put_int(c->local_maxpacket);
- packet_send();
+ if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
+ (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
+ (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
+ (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
+ (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 ||
+ (r = sshpkt_send(ssh)) != 0) {
+ sshpkt_fatal(ssh, r,
+ "%s: send open confirm", __func__);
+ }
}
} else {
- debug("server_input_channel_open: failure %s", ctype);
- packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
- packet_put_int(rchan);
- packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
- if (!(datafellows & SSH_BUG_OPENFAILURE)) {
- packet_put_cstring("open failed");
- packet_put_cstring("");
+ debug("%s: failure %s", __func__, ctype);
+ if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 ||
+ (r = sshpkt_put_u32(ssh, rchan)) != 0 ||
+ (r = sshpkt_put_u32(ssh, reason)) != 0 ||
+ (r = sshpkt_put_cstring(ssh, errmsg ? errmsg : "open failed")) != 0 ||
+ (r = sshpkt_put_cstring(ssh, "")) != 0 ||
+ (r = sshpkt_send(ssh)) != 0) {
+ sshpkt_fatal(ssh, r,
+ "%s: send open failure", __func__);
}
- packet_send();
}
free(ctype);
return 0;
}
static int
-server_input_hostkeys_prove(struct sshbuf **respp)
+server_input_hostkeys_prove(struct ssh *ssh, struct sshbuf **respp)
{
- struct ssh *ssh = active_state; /* XXX */
struct sshbuf *resp = NULL;
struct sshbuf *sigbuf = NULL;
struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL;
- int r, ndx, success = 0;
+ int r, ndx, kexsigtype, use_kexsigtype, success = 0;
const u_char *blob;
u_char *sig = 0;
size_t blen, slen;
@@ -1165,6 +743,8 @@ server_input_hostkeys_prove(struct sshbuf **respp)
if ((resp = sshbuf_new()) == NULL || (sigbuf = sshbuf_new()) == NULL)
fatal("%s: sshbuf_new", __func__);
+ kexsigtype = sshkey_type_plain(
+ sshkey_type_from_name(ssh->kex->hostkey_alg));
while (ssh_packet_remaining(ssh) > 0) {
sshkey_free(key);
key = NULL;
@@ -1195,13 +775,20 @@ server_input_hostkeys_prove(struct sshbuf **respp)
sshbuf_reset(sigbuf);
free(sig);
sig = NULL;
+ /*
+ * For RSA keys, prefer to use the signature type negotiated
+ * during KEX to the default (SHA1).
+ */
+ use_kexsigtype = kexsigtype == KEY_RSA &&
+ sshkey_type_plain(key->type) == KEY_RSA;
if ((r = sshbuf_put_cstring(sigbuf,
"hostkeys-prove-00@openssh.com")) != 0 ||
(r = sshbuf_put_string(sigbuf,
ssh->kex->session_id, ssh->kex->session_id_len)) != 0 ||
(r = sshkey_puts(key, sigbuf)) != 0 ||
- (r = ssh->kex->sign(key_prv, key_pub, &sig, &slen,
- sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), 0)) != 0 ||
+ (r = ssh->kex->sign(ssh, key_prv, key_pub, &sig, &slen,
+ sshbuf_ptr(sigbuf), sshbuf_len(sigbuf),
+ use_kexsigtype ? ssh->kex->hostkey_alg : NULL)) != 0 ||
(r = sshbuf_put_string(resp, sig, slen)) != 0) {
error("%s: couldn't prepare signature: %s",
__func__, ssh_err(r));
@@ -1221,198 +808,175 @@ server_input_hostkeys_prove(struct sshbuf **respp)
}
static int
-server_input_global_request(int type, u_int32_t seq, void *ctxt)
+server_input_global_request(int type, u_int32_t seq, struct ssh *ssh)
{
- char *rtype;
- int want_reply;
+ char *rtype = NULL;
+ u_char want_reply = 0;
int r, success = 0, allocated_listen_port = 0;
+ u_int port = 0;
struct sshbuf *resp = NULL;
+ struct passwd *pw = the_authctxt->pw;
+ struct Forward fwd;
+
+ memset(&fwd, 0, sizeof(fwd));
+ if (pw == NULL || !the_authctxt->valid)
+ fatal("%s: no/invalid user", __func__);
- rtype = packet_get_string(NULL);
- want_reply = packet_get_char();
- debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply);
+ if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 ||
+ (r = sshpkt_get_u8(ssh, &want_reply)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
+ debug("%s: rtype %s want_reply %d", __func__, rtype, want_reply);
/* -R style forwarding */
if (strcmp(rtype, "tcpip-forward") == 0) {
- struct passwd *pw;
- struct Forward fwd;
-
- pw = the_authctxt->pw;
- if (pw == NULL || !the_authctxt->valid)
- fatal("server_input_global_request: no/invalid user");
- memset(&fwd, 0, sizeof(fwd));
- fwd.listen_host = packet_get_string(NULL);
- fwd.listen_port = (u_short)packet_get_int();
- debug("server_input_global_request: tcpip-forward listen %s port %d",
- fwd.listen_host, fwd.listen_port);
-
+ if ((r = sshpkt_get_cstring(ssh, &fwd.listen_host, NULL)) != 0 ||
+ (r = sshpkt_get_u32(ssh, &port)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse tcpip-forward", __func__);
+ debug("%s: tcpip-forward listen %s port %u", __func__,
+ fwd.listen_host, port);
+ if (port <= INT_MAX)
+ fwd.listen_port = (int)port;
/* check permissions */
- if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 ||
- no_port_forwarding_flag ||
- (!want_reply && fwd.listen_port == 0)
-#ifndef NO_IPPORT_RESERVED_CONCEPT
- || (fwd.listen_port != 0 && fwd.listen_port < IPPORT_RESERVED &&
- pw->pw_uid != 0)
-#endif
- ) {
+ if (port > INT_MAX ||
+ (options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 ||
+ !auth_opts->permit_port_forwarding_flag ||
+ options.disable_forwarding ||
+ (!want_reply && fwd.listen_port == 0) ||
+ (fwd.listen_port != 0 &&
+ !bind_permitted(fwd.listen_port, pw->pw_uid))) {
success = 0;
- packet_send_debug("Server has disabled port forwarding.");
+ ssh_packet_send_debug(ssh, "Server has disabled port forwarding.");
} else {
/* Start listening on the port */
- success = channel_setup_remote_fwd_listener(&fwd,
+ success = channel_setup_remote_fwd_listener(ssh, &fwd,
&allocated_listen_port, &options.fwd_opts);
}
- free(fwd.listen_host);
if ((resp = sshbuf_new()) == NULL)
fatal("%s: sshbuf_new", __func__);
- if ((r = sshbuf_put_u32(resp, allocated_listen_port)) != 0)
+ if (allocated_listen_port != 0 &&
+ (r = sshbuf_put_u32(resp, allocated_listen_port)) != 0)
fatal("%s: sshbuf_put_u32: %s", __func__, ssh_err(r));
} else if (strcmp(rtype, "cancel-tcpip-forward") == 0) {
- struct Forward fwd;
+ if ((r = sshpkt_get_cstring(ssh, &fwd.listen_host, NULL)) != 0 ||
+ (r = sshpkt_get_u32(ssh, &port)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse cancel-tcpip-forward", __func__);
- memset(&fwd, 0, sizeof(fwd));
- fwd.listen_host = packet_get_string(NULL);
- fwd.listen_port = (u_short)packet_get_int();
debug("%s: cancel-tcpip-forward addr %s port %d", __func__,
- fwd.listen_host, fwd.listen_port);
-
- success = channel_cancel_rport_listener(&fwd);
- free(fwd.listen_host);
+ fwd.listen_host, port);
+ if (port <= INT_MAX) {
+ fwd.listen_port = (int)port;
+ success = channel_cancel_rport_listener(ssh, &fwd);
+ }
} else if (strcmp(rtype, "streamlocal-forward@openssh.com") == 0) {
- struct Forward fwd;
-
- memset(&fwd, 0, sizeof(fwd));
- fwd.listen_path = packet_get_string(NULL);
- debug("server_input_global_request: streamlocal-forward listen path %s",
+ if ((r = sshpkt_get_cstring(ssh, &fwd.listen_path, NULL)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse streamlocal-forward@openssh.com", __func__);
+ debug("%s: streamlocal-forward listen path %s", __func__,
fwd.listen_path);
/* check permissions */
if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0
- || no_port_forwarding_flag) {
+ || !auth_opts->permit_port_forwarding_flag ||
+ options.disable_forwarding ||
+ (pw->pw_uid != 0 && !use_privsep)) {
success = 0;
- packet_send_debug("Server has disabled port forwarding.");
+ ssh_packet_send_debug(ssh, "Server has disabled "
+ "streamlocal forwarding.");
} else {
/* Start listening on the socket */
- success = channel_setup_remote_fwd_listener(
+ success = channel_setup_remote_fwd_listener(ssh,
&fwd, NULL, &options.fwd_opts);
}
- free(fwd.listen_path);
} else if (strcmp(rtype, "cancel-streamlocal-forward@openssh.com") == 0) {
- struct Forward fwd;
-
- memset(&fwd, 0, sizeof(fwd));
- fwd.listen_path = packet_get_string(NULL);
+ if ((r = sshpkt_get_cstring(ssh, &fwd.listen_path, NULL)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse cancel-streamlocal-forward@openssh.com", __func__);
debug("%s: cancel-streamlocal-forward path %s", __func__,
fwd.listen_path);
- success = channel_cancel_rport_listener(&fwd);
- free(fwd.listen_path);
+ success = channel_cancel_rport_listener(ssh, &fwd);
} else if (strcmp(rtype, "no-more-sessions@openssh.com") == 0) {
no_more_sessions = 1;
success = 1;
} else if (strcmp(rtype, "hostkeys-prove-00@openssh.com") == 0) {
- success = server_input_hostkeys_prove(&resp);
+ success = server_input_hostkeys_prove(ssh, &resp);
}
+ /* XXX sshpkt_get_end() */
if (want_reply) {
- packet_start(success ?
- SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);
- if (success && resp != NULL)
- ssh_packet_put_raw(active_state, sshbuf_ptr(resp),
- sshbuf_len(resp));
- packet_send();
- packet_write_wait();
- }
+ if ((r = sshpkt_start(ssh, success ?
+ SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE)) != 0 ||
+ (success && resp != NULL && (r = sshpkt_putb(ssh, resp)) != 0) ||
+ (r = sshpkt_send(ssh)) != 0 ||
+ (r = ssh_packet_write_wait(ssh)) != 0)
+ sshpkt_fatal(ssh, r, "%s: send reply", __func__);
+ }
+ free(fwd.listen_host);
+ free(fwd.listen_path);
free(rtype);
sshbuf_free(resp);
return 0;
}
static int
-server_input_channel_req(int type, u_int32_t seq, void *ctxt)
+server_input_channel_req(int type, u_int32_t seq, struct ssh *ssh)
{
Channel *c;
- int id, reply, success = 0;
- char *rtype;
+ int r, success = 0;
+ char *rtype = NULL;
+ u_char want_reply = 0;
+ u_int id = 0;
- id = packet_get_int();
- rtype = packet_get_string(NULL);
- reply = packet_get_char();
+ if ((r = sshpkt_get_u32(ssh, &id)) != 0 ||
+ (r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 ||
+ (r = sshpkt_get_u8(ssh, &want_reply)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- debug("server_input_channel_req: channel %d request %s reply %d",
- id, rtype, reply);
+ debug("server_input_channel_req: channel %u request %s reply %d",
+ id, rtype, want_reply);
- if ((c = channel_lookup(id)) == NULL)
- packet_disconnect("server_input_channel_req: "
- "unknown channel %d", id);
+ if (id >= INT_MAX || (c = channel_lookup(ssh, (int)id)) == NULL) {
+ ssh_packet_disconnect(ssh, "%s: unknown channel %d",
+ __func__, id);
+ }
if (!strcmp(rtype, "eow@openssh.com")) {
- packet_check_eom();
- chan_rcvd_eow(c);
+ if ((r = sshpkt_get_end(ssh)) != 0)
+ sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
+ chan_rcvd_eow(ssh, c);
} else if ((c->type == SSH_CHANNEL_LARVAL ||
c->type == SSH_CHANNEL_OPEN) && strcmp(c->ctype, "session") == 0)
- success = session_input_channel_req(c, rtype);
- if (reply && !(c->flags & CHAN_CLOSE_SENT)) {
- packet_start(success ?
- SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE);
- packet_put_int(c->remote_id);
- packet_send();
+ success = session_input_channel_req(ssh, c, rtype);
+ if (want_reply && !(c->flags & CHAN_CLOSE_SENT)) {
+ if (!c->have_remote_id)
+ fatal("%s: channel %d: no remote_id",
+ __func__, c->self);
+ if ((r = sshpkt_start(ssh, success ?
+ SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 ||
+ (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
+ (r = sshpkt_send(ssh)) != 0)
+ sshpkt_fatal(ssh, r, "%s: send reply", __func__);
}
free(rtype);
return 0;
}
static void
-server_init_dispatch_20(void)
-{
- debug("server_init_dispatch_20");
- dispatch_init(&dispatch_protocol_error);
- dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
- dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data);
- dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
- dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
- dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open);
- dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
- dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
- dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &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);
+server_init_dispatch(struct ssh *ssh)
+{
+ debug("server_init_dispatch");
+ ssh_dispatch_init(ssh, &dispatch_protocol_error);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_DATA, &channel_input_data);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EOF, &channel_input_ieof);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_REQUEST, &server_input_channel_req);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
+ ssh_dispatch_set(ssh, SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request);
/* client_alive */
- dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &server_input_keep_alive);
- dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_keep_alive);
- dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &server_input_keep_alive);
- dispatch_set(SSH2_MSG_REQUEST_FAILURE, &server_input_keep_alive);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_SUCCESS, &server_input_keep_alive);
+ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_FAILURE, &server_input_keep_alive);
+ ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_SUCCESS, &server_input_keep_alive);
+ ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_FAILURE, &server_input_keep_alive);
/* rekeying */
- dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
-}
-static void
-server_init_dispatch_13(void)
-{
- debug("server_init_dispatch_13");
- dispatch_init(NULL);
- dispatch_set(SSH_CMSG_EOF, &server_input_eof);
- dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data);
- dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size);
- dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
- dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
- dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
- dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
- dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
- dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
-}
-static void
-server_init_dispatch_15(void)
-{
- server_init_dispatch_13();
- debug("server_init_dispatch_15");
- dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
- dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose);
-}
-static void
-server_init_dispatch(void)
-{
- if (compat20)
- server_init_dispatch_20();
- else if (compat13)
- server_init_dispatch_13();
- else
- server_init_dispatch_15();
+ ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
}