+/* $OpenBSD: channels.c,v 1.272 2008/01/19 23:02:40 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
*/
#include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.232 2006/01/30 12:22:22 reyk Exp $");
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include "xmalloc.h"
#include "ssh.h"
#include "ssh1.h"
#include "ssh2.h"
#include "packet.h"
-#include "xmalloc.h"
#include "log.h"
#include "misc.h"
+#include "buffer.h"
#include "channels.h"
#include "compat.h"
#include "canohost.h"
#include "key.h"
#include "authfd.h"
#include "pathnames.h"
-#include "bufaux.h"
/* -- channel core */
u_short listen_port; /* Remote side should listen port number. */
} ForwardPermission;
-/* List of all permitted host/port pairs to connect. */
+/* List of all permitted host/port pairs to connect by the user. */
static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
-/* Number of permitted host/port pairs in the array. */
+/* List of all permitted host/port pairs to connect by the admin. */
+static ForwardPermission permitted_adm_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
+
+/* Number of permitted host/port pairs in the array permitted by the user. */
static int num_permitted_opens = 0;
+
+/* Number of permitted host/port pair in the array permitted by the admin. */
+static int num_adm_permitted_opens = 0;
+
/*
* If this is true, all opens are permitted. This is the case on the server
* on which we have to trust the client anyway, and the user could do
* Fake X11 authentication data. This is what the server will be sending us;
* we should replace any occurrences of this by the real data.
*/
-static char *x11_fake_data = NULL;
+static u_char *x11_fake_data = NULL;
static u_int x11_fake_data_len;
if ((c = channel_by_id(id)) == NULL)
return (NULL);
- switch(c->type) {
+ switch (c->type) {
case SSH_CHANNEL_X11_OPEN:
case SSH_CHANNEL_LARVAL:
case SSH_CHANNEL_CONNECTING:
case SSH_CHANNEL_INPUT_DRAINING:
case SSH_CHANNEL_OUTPUT_DRAINING:
return (c);
- break;
}
logit("Non-public channel %d, type %d.", id, c->type);
return (NULL);
* Register filedescriptors for a channel, used when allocating a channel or
* when the channel consumer/producer is ready, e.g. shell exec'd
*/
-
static void
channel_register_fds(Channel *c, int rfd, int wfd, int efd,
int extusage, int nonblock)
* Allocate a new channel object and set its type and socket. This will cause
* remote_name to be freed.
*/
-
Channel *
channel_new(char *ctype, int type, int rfd, int wfd, int efd,
u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)
/* Do initial allocation if this is the first call. */
if (channels_alloc == 0) {
channels_alloc = 10;
- channels = xmalloc(channels_alloc * sizeof(Channel *));
+ channels = xcalloc(channels_alloc, sizeof(Channel *));
for (i = 0; i < channels_alloc; i++)
channels[i] = NULL;
}
if (channels_alloc > 10000)
fatal("channel_new: internal error: channels_alloc %d "
"too big.", channels_alloc);
- channels = xrealloc(channels,
- (channels_alloc + 10) * sizeof(Channel *));
+ channels = xrealloc(channels, channels_alloc + 10,
+ sizeof(Channel *));
channels_alloc += 10;
debug2("channel: expanding %d", channels_alloc);
for (i = found; i < channels_alloc; i++)
channels[i] = NULL;
}
/* Initialize and return new channel. */
- c = channels[found] = xmalloc(sizeof(Channel));
- memset(c, 0, sizeof(Channel));
+ c = channels[found] = xcalloc(1, sizeof(Channel));
buffer_init(&c->input);
buffer_init(&c->output);
buffer_init(&c->extended);
}
/* Close all channel fd/socket. */
-
static void
channel_close_fds(Channel *c)
{
}
/* Free the channel and close its fd/socket. */
-
void
channel_free(Channel *c)
{
* Closes the sockets/fds of all channels. This is used to close extra file
* descriptors after a fork.
*/
-
void
channel_close_all(void)
{
/*
* Stop listening to channels.
*/
-
void
channel_stop_listening(void)
{
* Returns true if no channel has too much buffered data, and false if one or
* more channel is overfull.
*/
-
int
channel_not_very_much_buffered_data(void)
{
}
/* Returns true if any channel is still open. */
-
int
channel_still_open(void)
{
}
/* Returns the id of an open channel suitable for keepaliving */
-
int
channel_find_open(void)
{
* suitable for sending to the client. The message contains crlf pairs for
* newlines.
*/
-
char *
channel_open_message(void)
{
packet_put_cstring(service);
packet_put_char(wantconfirm);
}
+
void
channel_register_confirm(int id, channel_callback_fn *fn, void *ctx)
{
c->confirm = fn;
c->confirm_ctx = ctx;
}
+
void
channel_register_cleanup(int id, channel_callback_fn *fn, int do_close)
{
c->detach_user = fn;
c->detach_close = do_close;
}
+
void
channel_cancel_cleanup(int id)
{
c->detach_user = NULL;
c->detach_close = 0;
}
+
void
channel_register_filter(int id, channel_infilter_fn *ifn,
channel_outfilter_fn *ofn)
* 'channel_post*': perform any appropriate operations for channels which
* have events pending.
*/
-typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset);
+typedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset);
chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
+/* ARGSUSED */
static void
-channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset)
{
FD_SET(c->sock, readset);
}
+/* ARGSUSED */
static void
-channel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset)
{
debug3("channel %d: waiting for connection", c->self);
FD_SET(c->sock, writeset);
}
static void
-channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset)
{
if (buffer_len(&c->input) < packet_get_maxsize())
FD_SET(c->sock, readset);
FD_SET(c->sock, writeset);
}
+int channel_tcpwinsz () {
+ u_int32_t tcpwinsz = 0;
+ socklen_t optsz = sizeof(tcpwinsz);
+ int ret = -1;
+
+ /* if we aren't on a socket return 128KB*/
+ if(!packet_connection_is_on_socket())
+ return(128*1024);
+ ret = getsockopt(packet_get_connection_in(),
+ SOL_SOCKET, SO_RCVBUF, &tcpwinsz, &optsz);
+ /* return no more than 64MB */
+ if ((ret == 0) && tcpwinsz > BUFFER_MAX_LEN_HPN)
+ tcpwinsz = BUFFER_MAX_LEN_HPN;
+ debug2("tcpwinsz: %d for connection: %d", tcpwinsz,
+ packet_get_connection_in());
+ return(tcpwinsz);
+}
+
static void
-channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset)
{
u_int limit = compat20 ? c->remote_window : packet_get_maxsize();
- /* check buffer limits */
- limit = MIN(limit, (BUFFER_MAX_HPN_LEN - BUFFER_MAX_CHUNK - CHAN_RBUF));
-
+ /* check buffer limits */
+ if ((!c->tcpwinsz) || (c->dynamic_window > 0))
+ c->tcpwinsz = channel_tcpwinsz();
+
+ limit = MIN(limit, 2 * c->tcpwinsz);
+
if (c->istate == CHAN_INPUT_OPEN &&
limit > 0 &&
- buffer_len(&c->input) < limit)
+ buffer_len(&c->input) < limit &&
+ buffer_check_alloc(&c->input, CHAN_RBUF))
FD_SET(c->rfd, readset);
if (c->ostate == CHAN_OUTPUT_OPEN ||
c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
FD_SET(c->ctl_fd, readset);
}
+/* ARGSUSED */
static void
-channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset)
{
if (buffer_len(&c->input) == 0) {
packet_start(SSH_MSG_CHANNEL_CLOSE);
}
}
+/* ARGSUSED */
static void
-channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset)
{
if (buffer_len(&c->output) == 0)
chan_mark_dead(c);
}
static void
-channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset)
{
int ret = x11_open_helper(&c->output);
}
static void
-channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset)
{
int ret = x11_open_helper(&c->output);
}
/* try to decode a socks4 header */
+/* ARGSUSED */
static int
-channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset)
+channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset)
{
char *p, *host;
u_int len, have, i, found;
s4_rsp.command = 90; /* cd: req granted */
s4_rsp.dest_port = 0; /* ignored */
s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */
- buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp));
+ buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp));
return 1;
}
#define SSH_SOCKS5_CONNECT 0x01
#define SSH_SOCKS5_SUCCESS 0x00
+/* ARGSUSED */
static int
-channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset)
+channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset)
{
struct {
u_int8_t version;
} s5_req, s5_rsp;
u_int16_t dest_port;
u_char *p, dest_addr[255+1];
- u_int have, i, found, nmethods, addrlen, af;
+ u_int have, need, i, found, nmethods, addrlen, af;
debug2("channel %d: decode socks5", c->self);
p = buffer_ptr(&c->input);
if (have < nmethods + 2)
return 0;
/* look for method: "NO AUTHENTICATION REQUIRED" */
- for (found = 0, i = 2 ; i < nmethods + 2; i++) {
- if (p[i] == SSH_SOCKS5_NOAUTH ) {
+ for (found = 0, i = 2; i < nmethods + 2; i++) {
+ if (p[i] == SSH_SOCKS5_NOAUTH) {
found = 1;
break;
}
debug2("channel %d: socks5 post auth", c->self);
if (have < sizeof(s5_req)+1)
return 0; /* need more */
- memcpy((char *)&s5_req, p, sizeof(s5_req));
+ memcpy(&s5_req, p, sizeof(s5_req));
if (s5_req.version != 0x05 ||
s5_req.command != SSH_SOCKS5_CONNECT ||
s5_req.reserved != 0x00) {
debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
return -1;
}
- if (have < 4 + addrlen + 2)
+ need = sizeof(s5_req) + addrlen + 2;
+ if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
+ need++;
+ if (have < need)
return 0;
buffer_consume(&c->input, sizeof(s5_req));
if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY;
dest_port = 0; /* ignored */
- buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp));
- buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr));
- buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port));
+ buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp));
+ buffer_append(&c->output, &dest_addr, sizeof(struct in_addr));
+ buffer_append(&c->output, &dest_port, sizeof(dest_port));
return 1;
}
/* dynamic port forwarding */
static void
-channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset)
{
u_char *p;
u_int have;
}
/* This is our fake X11 server socket. */
+/* ARGSUSED */
static void
-channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
{
Channel *nc;
struct sockaddr addr;
/*
* This socket is listening for connections to a forwarded TCP/IP port.
*/
+/* ARGSUSED */
static void
-channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
{
Channel *nc;
struct sockaddr addr;
* This is the authentication agent socket listening for connections from
* clients.
*/
+/* ARGSUSED */
static void
-channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
{
Channel *nc;
int newsock;
}
}
+/* ARGSUSED */
static void
-channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
{
int err = 0;
socklen_t sz = sizeof(err);
}
}
+/* ARGSUSED */
static int
-channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
+channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset)
{
char buf[CHAN_RBUF];
- int len;
+ int len, force;
- if (c->rfd != -1 &&
- FD_ISSET(c->rfd, readset)) {
+ force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
+ if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) {
+ errno = 0;
len = read(c->rfd, buf, sizeof(buf));
- if (len < 0 && (errno == EINTR || errno == EAGAIN))
+ if (len < 0 && (errno == EINTR || (errno == EAGAIN && !force)))
return 1;
+#ifndef PTY_ZEROREAD
if (len <= 0) {
+#else
+ if ((!c->isatty && len <= 0) ||
+ (c->isatty && (len < 0 || (len == 0 && errno != 0)))) {
+#endif
debug2("channel %d: read<=0 rfd %d len %d",
c->self, c->rfd, len);
if (c->type != SSH_CHANNEL_OPEN) {
}
return 1;
}
+
+/* ARGSUSED */
static int
-channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
+channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset)
{
struct termios tio;
u_char *data = NULL, *buf;
}
return 1;
}
+
static int
-channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset)
+channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset)
{
char buf[CHAN_RBUF];
int len;
c->local_consumed += len;
}
} else if (c->extended_usage == CHAN_EXTENDED_READ &&
- FD_ISSET(c->efd, readset)) {
+ (c->detach_close || FD_ISSET(c->efd, readset))) {
len = read(c->efd, buf, sizeof(buf));
debug2("channel %d: read %d from efd %d",
c->self, len, c->efd);
- if (len < 0 && (errno == EINTR || errno == EAGAIN))
+ if (len < 0 && (errno == EINTR ||
+ (errno == EAGAIN && !c->detach_close)))
return 1;
if (len <= 0) {
debug2("channel %d: closing read-efd %d",
}
return 1;
}
+
+/* ARGSUSED */
static int
-channel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset)
+channel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset)
{
char buf[16];
int len;
}
return 1;
}
+
static int
channel_check_window(Channel *c)
{
if (c->type == SSH_CHANNEL_OPEN &&
!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
- c->local_window < c->local_window_max/2 &&
+ ((c->local_window_max - c->local_window >
+ c->local_maxpacket*3) ||
+ c->local_window < c->local_window_max/2) &&
c->local_consumed > 0) {
- u_int32_t tcpwinsz = 0;
- socklen_t optsz = sizeof(tcpwinsz);
- int ret = -1;
- u_int32_t addition = 0;
- if (c->dynamic_window) {
- ret = getsockopt(packet_get_connection_in(),
- SOL_SOCKET, SO_RCVBUF, &tcpwinsz, &optsz);
- if ((ret == 0) && tcpwinsz > BUFFER_MAX_HPN_LEN)
- tcpwinsz = BUFFER_MAX_HPN_LEN;
- }
- if (c->dynamic_window && (ret == 0) &&
- (tcpwinsz > c->local_window_max)) {
- addition = tcpwinsz - c->local_window_max;
+ u_int addition = 0;
+ /* adjust max window size if we are in a dynamic environment */
+ if (c->dynamic_window && (c->tcpwinsz > c->local_window_max)) {
+ /* grow the window somewhat aggressively to maintain pressure */
+ addition = 1.5*(c->tcpwinsz - c->local_window_max);
c->local_window_max += addition;
}
packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
}
static void
-channel_post_open(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_open(Channel *c, fd_set *readset, fd_set *writeset)
{
if (c->delayed)
return;
channel_check_window(c);
}
+/* ARGSUSED */
static void
-channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset)
{
int len;
}
static void
-channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset)
+channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
{
static int did_init = 0;
u_int i;
channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
u_int *nallocp, int rekeying)
{
- u_int n, sz;
+ u_int n, sz, nfdset;
n = MAX(*maxfdp, channel_max_fd);
- sz = howmany(n+1, NFDBITS) * sizeof(fd_mask);
+ nfdset = howmany(n+1, NFDBITS);
+ /* Explicitly test here, because xrealloc isn't always called */
+ if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask))
+ fatal("channel_prepare_select: max_fd (%d) is too large", n);
+ sz = nfdset * sizeof(fd_mask);
+
/* perhaps check sz < nalloc/2 and shrink? */
if (*readsetp == NULL || sz > *nallocp) {
- *readsetp = xrealloc(*readsetp, sz);
- *writesetp = xrealloc(*writesetp, sz);
+ *readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask));
+ *writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask));
*nallocp = sz;
}
*maxfdp = n;
* events pending.
*/
void
-channel_after_select(fd_set * readset, fd_set * writeset)
+channel_after_select(fd_set *readset, fd_set *writeset)
{
channel_handler(channel_post, readset, writeset);
}
/* If there is data to send to the connection, enqueue some of it now. */
-
-void
+int
channel_output_poll(void)
{
Channel *c;
u_int i, len;
+ int packet_length = 0;
for (i = 0; i < channels_alloc; i++) {
c = channels[i];
packet_start(SSH2_MSG_CHANNEL_DATA);
packet_put_int(c->remote_id);
packet_put_string(data, dlen);
- packet_send();
+ packet_length = packet_send();
c->remote_window -= dlen + 4;
xfree(data);
}
SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA);
packet_put_int(c->remote_id);
packet_put_string(buffer_ptr(&c->input), len);
- packet_send();
+ packet_length = packet_send();
buffer_consume(&c->input, len);
c->remote_window -= len;
}
packet_put_int(c->remote_id);
packet_put_int(SSH2_EXTENDED_DATA_STDERR);
packet_put_string(buffer_ptr(&c->extended), len);
- packet_send();
+ packet_length = packet_send();
buffer_consume(&c->extended, len);
c->remote_window -= len;
debug2("channel %d: sent ext data %d", c->self, len);
}
}
+ return (packet_length);
}
/* -- protocol input */
+/* ARGSUSED */
void
channel_input_data(int type, u_int32_t seq, void *ctxt)
{
xfree(data);
}
+/* ARGSUSED */
void
channel_input_extended_data(int type, u_int32_t seq, void *ctxt)
{
xfree(data);
}
+/* ARGSUSED */
void
channel_input_ieof(int type, u_int32_t seq, void *ctxt)
{
}
+/* ARGSUSED */
void
channel_input_close(int type, u_int32_t seq, void *ctxt)
{
}
/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
+/* ARGSUSED */
void
channel_input_oclose(int type, u_int32_t seq, void *ctxt)
{
chan_rcvd_oclose(c);
}
+/* ARGSUSED */
void
channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt)
{
channel_free(c);
}
+/* ARGSUSED */
void
channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)
{
return "unknown reason";
}
+/* ARGSUSED */
void
channel_input_open_failure(int type, u_int32_t seq, void *ctxt)
{
channel_free(c);
}
+/* ARGSUSED */
void
channel_input_window_adjust(int type, u_int32_t seq, void *ctxt)
{
c->remote_window += adjust;
}
+/* ARGSUSED */
void
channel_input_port_open(int type, u_int32_t seq, void *ctxt)
{
static int
channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port,
- const char *host_to_connect, u_short port_to_connect, int gateway_ports)
+ const char *host_to_connect, u_short port_to_connect, int gateway_ports,
+ int hpn_disabled, int hpn_buffer_size)
{
Channel *c;
int sock, r, success = 0, wildcard = 0, is_client;
wildcard = 1;
} else if (gateway_ports || is_client) {
if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
- strcmp(listen_addr, "0.0.0.0") == 0) ||
+ strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
*listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
(!is_client && gateway_ports == 1))
wildcard = 1;
if (addr == NULL) {
/* This really shouldn't happen */
packet_disconnect("getaddrinfo: fatal error: %s",
- gai_strerror(r));
+ ssh_gai_strerror(r));
} else {
error("channel_setup_fwd_listener: "
- "getaddrinfo(%.64s): %s", addr, gai_strerror(r));
+ "getaddrinfo(%.64s): %s", addr,
+ ssh_gai_strerror(r));
}
return 0;
}
continue;
}
/* Allocate a channel number for the socket. */
+ /* explicitly test for hpn disabled option. if true use smaller window size */
+ if (hpn_disabled)
c = channel_new("port listener", type, sock, sock, -1,
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
0, "port listener", 1);
+ else
+ c = channel_new("port listener", type, sock, sock, -1,
+ hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT,
+ 0, "port listener", 1);
strlcpy(c->path, host, sizeof(c->path));
c->host_port = port_to_connect;
c->listening_port = listen_port;
/* protocol local port fwd, used by ssh (and sshd in v1) */
int
channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port,
- const char *host_to_connect, u_short port_to_connect, int gateway_ports)
+ const char *host_to_connect, u_short port_to_connect, int gateway_ports,
+ int hpn_disabled, int hpn_buffer_size)
{
return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
listen_host, listen_port, host_to_connect, port_to_connect,
- gateway_ports);
+ gateway_ports, hpn_disabled, hpn_buffer_size);
}
/* protocol v2 remote port fwd, used by sshd */
int
channel_setup_remote_fwd_listener(const char *listen_address,
- u_short listen_port, int gateway_ports)
+ u_short listen_port, int gateway_ports, int hpn_disabled, int hpn_buffer_size)
{
return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,
- listen_address, listen_port, NULL, 0, gateway_ports);
+ listen_address, listen_port, NULL, 0, gateway_ports,
+ hpn_disabled, hpn_buffer_size);
}
/*
* the secure channel to host:port from local side.
*/
-void
+int
channel_request_remote_forwarding(const char *listen_host, u_short listen_port,
const char *host_to_connect, u_short port_to_connect)
{
/* Send the forward request to the remote side. */
if (compat20) {
const char *address_to_bind;
- if (listen_host == NULL)
- address_to_bind = "localhost";
- else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0)
- address_to_bind = "";
- else
+ if (listen_host == NULL) {
+ if (datafellows & SSH_BUG_RFWD_ADDR)
+ address_to_bind = "127.0.0.1";
+ else
+ address_to_bind = "localhost";
+ } else if (*listen_host == '\0' ||
+ strcmp(listen_host, "*") == 0) {
+ if (datafellows & SSH_BUG_RFWD_ADDR)
+ address_to_bind = "0.0.0.0";
+ else
+ address_to_bind = "";
+ } else
address_to_bind = listen_host;
packet_start(SSH2_MSG_GLOBAL_REQUEST);
success = 1;
break;
case SSH_SMSG_FAILURE:
- logit("Warning: Server denied remote port forwarding.");
break;
default:
/* Unknown packet */
permitted_opens[num_permitted_opens].listen_port = listen_port;
num_permitted_opens++;
}
+ return (success ? 0 : -1);
}
/*
/*
* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates
* listening for the port, and sends back a success reply (or disconnect
- * message if there was an error). This never returns if there was an error.
+ * message if there was an error).
*/
-
-void
-channel_input_port_forward_request(int is_root, int gateway_ports)
+int
+channel_input_port_forward_request(int is_root, int gateway_ports,
+ int hpn_disabled, int hpn_buffer_size)
{
u_short port, host_port;
+ int success = 0;
char *hostname;
/* Get arguments from the packet. */
#endif
/* Initiate forwarding */
- channel_setup_local_fwd_listener(NULL, port, hostname,
- host_port, gateway_ports);
+ success = channel_setup_local_fwd_listener(NULL, port, hostname,
+ host_port, gateway_ports, hpn_disabled, hpn_buffer_size);
/* Free the argument string. */
xfree(hostname);
+
+ return (success ? 0 : -1);
}
/*
channel_add_permitted_opens(char *host, int port)
{
if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
- fatal("channel_request_remote_forwarding: too many forwards");
+ fatal("channel_add_permitted_opens: too many forwards");
debug("allow port forwarding to host %s port %d", host, port);
permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host);
all_opens_permitted = 0;
}
+int
+channel_add_adm_permitted_opens(char *host, int port)
+{
+ if (num_adm_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
+ fatal("channel_add_adm_permitted_opens: too many forwards");
+ debug("config allows port forwarding to host %s port %d", host, port);
+
+ permitted_adm_opens[num_adm_permitted_opens].host_to_connect
+ = xstrdup(host);
+ permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port;
+ return ++num_adm_permitted_opens;
+}
+
void
channel_clear_permitted_opens(void)
{
if (permitted_opens[i].host_to_connect != NULL)
xfree(permitted_opens[i].host_to_connect);
num_permitted_opens = 0;
-
}
+void
+channel_clear_adm_permitted_opens(void)
+{
+ int i;
+
+ for (i = 0; i < num_adm_permitted_opens; i++)
+ if (permitted_adm_opens[i].host_to_connect != NULL)
+ xfree(permitted_adm_opens[i].host_to_connect);
+ num_adm_permitted_opens = 0;
+}
/* return socket to remote host, port */
static int
snprintf(strport, sizeof strport, "%d", port);
if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
error("connect_to %.100s: unknown host (%s)", host,
- gai_strerror(gaierr));
+ ssh_gai_strerror(gaierr));
return -1;
}
for (ai = aitop; ai; ai = ai->ai_next) {
int
channel_connect_to(const char *host, u_short port)
{
- int i, permit;
+ int i, permit, permit_adm = 1;
permit = all_opens_permitted;
if (!permit) {
permitted_opens[i].port_to_connect == port &&
strcmp(permitted_opens[i].host_to_connect, host) == 0)
permit = 1;
+ }
+ if (num_adm_permitted_opens > 0) {
+ permit_adm = 0;
+ for (i = 0; i < num_adm_permitted_opens; i++)
+ if (permitted_adm_opens[i].host_to_connect != NULL &&
+ permitted_adm_opens[i].port_to_connect == port &&
+ strcmp(permitted_adm_opens[i].host_to_connect, host)
+ == 0)
+ permit_adm = 1;
}
- if (!permit) {
+
+ if (!permit || !permit_adm) {
logit("Received request to connect to host %.100s port %d, "
"but the request was denied.", host, port);
return -1;
if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
continue;
channel_request_start(i, "window-change", 0);
- packet_put_int(ws.ws_col);
- packet_put_int(ws.ws_row);
- packet_put_int(ws.ws_xpixel);
- packet_put_int(ws.ws_ypixel);
+ packet_put_int((u_int)ws.ws_col);
+ packet_put_int((u_int)ws.ws_row);
+ packet_put_int((u_int)ws.ws_xpixel);
+ packet_put_int((u_int)ws.ws_ypixel);
packet_send();
}
}
*/
int
x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
- int single_connection, u_int *display_numberp, int **chanids)
+ int single_connection, u_int *display_numberp, int **chanids,
+ int hpn_disabled, int hpn_buffer_size)
{
Channel *nc = NULL;
int display_number, sock;
hints.ai_socktype = SOCK_STREAM;
snprintf(strport, sizeof strport, "%d", port);
if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
- error("getaddrinfo: %.100s", gai_strerror(gaierr));
+ error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr));
return -1;
}
for (ai = aitop; ai; ai = ai->ai_next) {
}
/* Allocate a channel for each socket. */
- *chanids = xmalloc(sizeof(**chanids) * (num_socks + 1));
+ *chanids = xcalloc(num_socks + 1, sizeof(**chanids));
for (n = 0; n < num_socks; n++) {
sock = socks[n];
+ /* Is this really necassary? */
+ if (hpn_disabled)
nc = channel_new("x11 listener",
SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
0, "X11 inet listener", 1);
+ else
+ nc = channel_new("x11 listener",
+ SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
+ hpn_buffer_size, CHAN_X11_PACKET_DEFAULT,
+ 0, "X11 inet listener", 1);
nc->single_connection = single_connection;
(*chanids)[n] = nc->self;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr);
- if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0)
+ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
return sock;
close(sock);
error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
int
x11_connect_display(void)
{
- int display_number, sock = 0;
+ u_int display_number;
const char *display;
char buf[1024], *cp;
struct addrinfo hints, *ai, *aitop;
char strport[NI_MAXSERV];
- int gaierr;
+ int gaierr, sock = 0;
/* Try to open a socket for the local X server. */
display = getenv("DISPLAY");
if (strncmp(display, "unix:", 5) == 0 ||
display[0] == ':') {
/* Connect to the unix domain socket. */
- if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) {
+ if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) {
error("Could not parse display number from DISPLAY: %.100s",
display);
return -1;
}
*cp = 0;
/* buf now contains the host name. But first we parse the display number. */
- if (sscanf(cp + 1, "%d", &display_number) != 1) {
+ if (sscanf(cp + 1, "%u", &display_number) != 1) {
error("Could not parse display number from DISPLAY: %.100s",
display);
return -1;
memset(&hints, 0, sizeof(hints));
hints.ai_family = IPv4or6;
hints.ai_socktype = SOCK_STREAM;
- snprintf(strport, sizeof strport, "%d", 6000 + display_number);
+ snprintf(strport, sizeof strport, "%u", 6000 + display_number);
if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
- error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr));
+ error("%.100s: unknown host. (%s)", buf,
+ ssh_gai_strerror(gaierr));
return -1;
}
for (ai = aitop; ai; ai = ai->ai_next) {
}
/* Connect it to the display. */
if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
- debug2("connect %.100s port %d: %.100s", buf,
+ debug2("connect %.100s port %u: %.100s", buf,
6000 + display_number, strerror(errno));
close(sock);
continue;
}
freeaddrinfo(aitop);
if (!ai) {
- error("connect %.100s port %d: %.100s", buf, 6000 + display_number,
+ error("connect %.100s port %u: %.100s", buf, 6000 + display_number,
strerror(errno));
return -1;
}
* with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
*/
+/* ARGSUSED */
void
x11_input_open(int type, u_int32_t seq, void *ctxt)
{
}
/* dummy protocol handler that denies SSH-1 requests (agent/x11) */
+/* ARGSUSED */
void
deny_input_open(int type, u_int32_t seq, void *ctxt)
{
return;
}
- cp = disp;
- if (disp)
- cp = strchr(disp, ':');
+ cp = strchr(disp, ':');
if (cp)
cp = strchr(cp, '.');
if (cp)
- screen_number = atoi(cp + 1);
+ screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL);
else
screen_number = 0;