version 1.15, 2016/03/11 01:55:00 |
version 1.15.2.2, 2017/01/07 08:53:41 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
/* $OpenBSD: channels.c,v 1.349 2016/02/05 13:28:19 naddy Exp $ */ |
/* $OpenBSD: channels.c,v 1.356 2016/10/18 17:32:54 dtucker Exp $ */ |
|
|
/* |
/* |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
|
|
__RCSID("$NetBSD$"); |
__RCSID("$NetBSD$"); |
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/param.h> /* MIN MAX */ |
|
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/ioctl.h> |
#include <sys/ioctl.h> |
#include <sys/un.h> |
#include <sys/un.h> |
Line 71 __RCSID("$NetBSD$"); |
|
Line 70 __RCSID("$NetBSD$"); |
|
#include "ssh.h" |
#include "ssh.h" |
#include "ssh1.h" |
#include "ssh1.h" |
#include "ssh2.h" |
#include "ssh2.h" |
|
#include "ssherr.h" |
#include "packet.h" |
#include "packet.h" |
#include "log.h" |
#include "log.h" |
#include "misc.h" |
#include "misc.h" |
Line 124 typedef struct { |
|
Line 124 typedef struct { |
|
char *listen_host; /* Remote side should listen address. */ |
char *listen_host; /* Remote side should listen address. */ |
char *listen_path; /* Remote side should listen path. */ |
char *listen_path; /* Remote side should listen path. */ |
int listen_port; /* Remote side should listen port. */ |
int listen_port; /* Remote side should listen port. */ |
|
Channel *downstream; /* Downstream mux*/ |
} ForwardPermission; |
} ForwardPermission; |
|
|
/* List of all permitted host/port pairs to connect by the user. */ |
/* List of all permitted host/port pairs to connect by the user. */ |
Line 141 static int num_adm_permitted_opens = 0; |
|
Line 142 static int num_adm_permitted_opens = 0; |
|
/* special-case port number meaning allow any port */ |
/* special-case port number meaning allow any port */ |
#define FWD_PERMIT_ANY_PORT 0 |
#define FWD_PERMIT_ANY_PORT 0 |
|
|
|
/* special-case wildcard meaning allow any host */ |
|
#define FWD_PERMIT_ANY_HOST "*" |
|
|
/* |
/* |
* If this is true, all opens are permitted. This is the case on the server |
* 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 |
* on which we have to trust the client anyway, and the user could do |
Line 184 static int IPv4or6 = AF_UNSPEC; |
|
Line 188 static int IPv4or6 = AF_UNSPEC; |
|
|
|
/* helper */ |
/* helper */ |
static void port_open_helper(Channel *c, const char *rtype); |
static void port_open_helper(Channel *c, const char *rtype); |
|
static const char *channel_rfwd_bind_host(const char *listen_host); |
|
|
/* non-blocking connect helpers */ |
/* non-blocking connect helpers */ |
static int connect_next(struct channel_connect *); |
static int connect_next(struct channel_connect *); |
Line 208 channel_by_id(int id) |
|
Line 213 channel_by_id(int id) |
|
return c; |
return c; |
} |
} |
|
|
|
Channel * |
|
channel_by_remote_id(int remote_id) |
|
{ |
|
Channel *c; |
|
u_int i; |
|
|
|
for (i = 0; i < channels_alloc; i++) { |
|
c = channels[i]; |
|
if (c != NULL && c->remote_id == remote_id) |
|
return c; |
|
} |
|
return NULL; |
|
} |
|
|
/* |
/* |
* Returns the channel if it is allowed to receive protocol messages. |
* Returns the channel if it is allowed to receive protocol messages. |
* Private channels, like listening sockets, may not receive messages. |
* Private channels, like listening sockets, may not receive messages. |
Line 230 channel_lookup(int id) |
|
Line 249 channel_lookup(int id) |
|
case SSH_CHANNEL_INPUT_DRAINING: |
case SSH_CHANNEL_INPUT_DRAINING: |
case SSH_CHANNEL_OUTPUT_DRAINING: |
case SSH_CHANNEL_OUTPUT_DRAINING: |
case SSH_CHANNEL_ABANDONED: |
case SSH_CHANNEL_ABANDONED: |
|
case SSH_CHANNEL_MUX_PROXY: |
return (c); |
return (c); |
} |
} |
logit("Non-public channel %d, type %d.", id, c->type); |
logit("Non-public channel %d, type %d.", id, c->type); |
Line 245 channel_register_fds(Channel *c, int rfd |
|
Line 265 channel_register_fds(Channel *c, int rfd |
|
int extusage, int nonblock, int is_tty) |
int extusage, int nonblock, int is_tty) |
{ |
{ |
/* Update the maximum file descriptor value. */ |
/* Update the maximum file descriptor value. */ |
channel_max_fd = MAX(channel_max_fd, rfd); |
channel_max_fd = MAXIMUM(channel_max_fd, rfd); |
channel_max_fd = MAX(channel_max_fd, wfd); |
channel_max_fd = MAXIMUM(channel_max_fd, wfd); |
channel_max_fd = MAX(channel_max_fd, efd); |
channel_max_fd = MAXIMUM(channel_max_fd, efd); |
|
|
if (rfd != -1) |
if (rfd != -1) |
fcntl(rfd, F_SETFD, FD_CLOEXEC); |
fcntl(rfd, F_SETFD, FD_CLOEXEC); |
Line 371 channel_find_maxfd(void) |
|
Line 391 channel_find_maxfd(void) |
|
for (i = 0; i < channels_alloc; i++) { |
for (i = 0; i < channels_alloc; i++) { |
c = channels[i]; |
c = channels[i]; |
if (c != NULL) { |
if (c != NULL) { |
max = MAX(max, c->rfd); |
max = MAXIMUM(max, c->rfd); |
max = MAX(max, c->wfd); |
max = MAXIMUM(max, c->wfd); |
max = MAX(max, c->efd); |
max = MAXIMUM(max, c->efd); |
} |
} |
} |
} |
return max; |
return max; |
Line 409 channel_free(Channel *c) |
|
Line 429 channel_free(Channel *c) |
|
{ |
{ |
char *s; |
char *s; |
u_int i, n; |
u_int i, n; |
|
Channel *other; |
struct channel_confirm *cc; |
struct channel_confirm *cc; |
|
|
for (n = 0, i = 0; i < channels_alloc; i++) |
for (n = 0, i = 0; i < channels_alloc; i++) { |
if (channels[i]) |
if ((other = channels[i]) != NULL) { |
n++; |
n++; |
|
|
|
/* detach from mux client and prepare for closing */ |
|
if (c->type == SSH_CHANNEL_MUX_CLIENT && |
|
other->type == SSH_CHANNEL_MUX_PROXY && |
|
other->mux_ctx == c) { |
|
other->mux_ctx = NULL; |
|
other->type = SSH_CHANNEL_OPEN; |
|
other->istate = CHAN_INPUT_CLOSED; |
|
other->ostate = CHAN_OUTPUT_CLOSED; |
|
} |
|
} |
|
} |
debug("channel %d: free: %s, nchannels %u", c->self, |
debug("channel %d: free: %s, nchannels %u", c->self, |
c->remote_name ? c->remote_name : "???", n); |
c->remote_name ? c->remote_name : "???", n); |
|
|
|
/* XXX more MUX cleanup: remove remote forwardings */ |
|
if (c->type == SSH_CHANNEL_MUX_CLIENT) { |
|
for (i = 0; i < (u_int)num_permitted_opens; i++) { |
|
if (permitted_opens[i].downstream != c) |
|
continue; |
|
/* cancel on the server, since mux client is gone */ |
|
debug("channel %d: cleanup remote forward for %s:%u", |
|
c->self, |
|
permitted_opens[i].listen_host, |
|
permitted_opens[i].listen_port); |
|
packet_start(SSH2_MSG_GLOBAL_REQUEST); |
|
packet_put_cstring("cancel-tcpip-forward"); |
|
packet_put_char(0); |
|
packet_put_cstring(channel_rfwd_bind_host( |
|
permitted_opens[i].listen_host)); |
|
packet_put_int(permitted_opens[i].listen_port); |
|
packet_send(); |
|
/* unregister */ |
|
permitted_opens[i].listen_port = 0; |
|
permitted_opens[i].port_to_connect = 0; |
|
free(permitted_opens[i].host_to_connect); |
|
permitted_opens[i].host_to_connect = NULL; |
|
free(permitted_opens[i].listen_host); |
|
permitted_opens[i].listen_host = NULL; |
|
permitted_opens[i].listen_path = NULL; |
|
permitted_opens[i].downstream = NULL; |
|
} |
|
} |
|
|
s = channel_open_message(); |
s = channel_open_message(); |
debug3("channel %d: status: %s", c->self, s); |
debug3("channel %d: status: %s", c->self, s); |
free(s); |
free(s); |
Line 562 channel_still_open(void) |
|
Line 624 channel_still_open(void) |
|
case SSH_CHANNEL_OPEN: |
case SSH_CHANNEL_OPEN: |
case SSH_CHANNEL_X11_OPEN: |
case SSH_CHANNEL_X11_OPEN: |
case SSH_CHANNEL_MUX_CLIENT: |
case SSH_CHANNEL_MUX_CLIENT: |
|
case SSH_CHANNEL_MUX_PROXY: |
return 1; |
return 1; |
case SSH_CHANNEL_INPUT_DRAINING: |
case SSH_CHANNEL_INPUT_DRAINING: |
case SSH_CHANNEL_OUTPUT_DRAINING: |
case SSH_CHANNEL_OUTPUT_DRAINING: |
Line 595 channel_find_open(void) |
|
Line 658 channel_find_open(void) |
|
case SSH_CHANNEL_RPORT_LISTENER: |
case SSH_CHANNEL_RPORT_LISTENER: |
case SSH_CHANNEL_MUX_LISTENER: |
case SSH_CHANNEL_MUX_LISTENER: |
case SSH_CHANNEL_MUX_CLIENT: |
case SSH_CHANNEL_MUX_CLIENT: |
|
case SSH_CHANNEL_MUX_PROXY: |
case SSH_CHANNEL_OPENING: |
case SSH_CHANNEL_OPENING: |
case SSH_CHANNEL_CONNECTING: |
case SSH_CHANNEL_CONNECTING: |
case SSH_CHANNEL_ZOMBIE: |
case SSH_CHANNEL_ZOMBIE: |
Line 620 channel_find_open(void) |
|
Line 684 channel_find_open(void) |
|
return -1; |
return -1; |
} |
} |
|
|
|
|
/* |
/* |
* Returns a message describing the currently open forwarded connections, |
* Returns a message describing the currently open forwarded connections, |
* suitable for sending to the client. The message contains crlf pairs for |
* suitable for sending to the client. The message contains crlf pairs for |
Line 649 channel_open_message(void) |
|
Line 712 channel_open_message(void) |
|
case SSH_CHANNEL_AUTH_SOCKET: |
case SSH_CHANNEL_AUTH_SOCKET: |
case SSH_CHANNEL_ZOMBIE: |
case SSH_CHANNEL_ZOMBIE: |
case SSH_CHANNEL_ABANDONED: |
case SSH_CHANNEL_ABANDONED: |
case SSH_CHANNEL_MUX_CLIENT: |
|
case SSH_CHANNEL_MUX_LISTENER: |
case SSH_CHANNEL_MUX_LISTENER: |
case SSH_CHANNEL_UNIX_LISTENER: |
case SSH_CHANNEL_UNIX_LISTENER: |
case SSH_CHANNEL_RUNIX_LISTENER: |
case SSH_CHANNEL_RUNIX_LISTENER: |
Line 662 channel_open_message(void) |
|
Line 724 channel_open_message(void) |
|
case SSH_CHANNEL_X11_OPEN: |
case SSH_CHANNEL_X11_OPEN: |
case SSH_CHANNEL_INPUT_DRAINING: |
case SSH_CHANNEL_INPUT_DRAINING: |
case SSH_CHANNEL_OUTPUT_DRAINING: |
case SSH_CHANNEL_OUTPUT_DRAINING: |
|
case SSH_CHANNEL_MUX_PROXY: |
|
case SSH_CHANNEL_MUX_CLIENT: |
snprintf(buf, sizeof buf, |
snprintf(buf, sizeof buf, |
" #%d %.300s (t%d r%d i%u/%d o%u/%d fd %d/%d cc %d)\r\n", |
" #%d %.300s (t%d r%d i%u/%d o%u/%d fd %d/%d cc %d)\r\n", |
c->self, c->remote_name, |
c->self, c->remote_name, |
Line 1443 port_open_helper(Channel *c, const char |
|
Line 1507 port_open_helper(Channel *c, const char |
|
{ |
{ |
char buf[1024]; |
char buf[1024]; |
char *local_ipaddr = get_local_ipaddr(c->sock); |
char *local_ipaddr = get_local_ipaddr(c->sock); |
int local_port = c->sock == -1 ? 65536 : get_sock_port(c->sock, 1); |
int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); |
char *remote_ipaddr = get_peer_ipaddr(c->sock); |
char *remote_ipaddr = get_peer_ipaddr(c->sock); |
int remote_port = get_peer_port(c->sock); |
int remote_port = get_peer_port(c->sock); |
|
|
Line 1912 read_mux(Channel *c, u_int need) |
|
Line 1976 read_mux(Channel *c, u_int need) |
|
|
|
if (buffer_len(&c->input) < need) { |
if (buffer_len(&c->input) < need) { |
rlen = need - buffer_len(&c->input); |
rlen = need - buffer_len(&c->input); |
len = read(c->rfd, buf, MIN(rlen, CHAN_RBUF)); |
len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); |
if (len < 0 && (errno == EINTR || errno == EAGAIN)) |
if (len < 0 && (errno == EINTR || errno == EAGAIN)) |
return buffer_len(&c->input); |
return buffer_len(&c->input); |
if (len <= 0) { |
if (len <= 0) { |
Line 2215 channel_prepare_select(fd_set **readsetp |
|
Line 2279 channel_prepare_select(fd_set **readsetp |
|
{ |
{ |
u_int n, sz, nfdset; |
u_int n, sz, nfdset; |
|
|
n = MAX(*maxfdp, channel_max_fd); |
n = MAXIMUM(*maxfdp, channel_max_fd); |
|
|
nfdset = howmany(n+1, NFDBITS); |
nfdset = howmany(n+1, NFDBITS); |
/* Explicitly test here, because xrealloc isn't always called */ |
/* Explicitly test here, because xrealloc isn't always called */ |
Line 2377 channel_output_poll(void) |
|
Line 2441 channel_output_poll(void) |
|
return (packet_length); |
return (packet_length); |
} |
} |
|
|
|
/* -- mux proxy support */ |
|
|
|
/* |
|
* When multiplexing channel messages for mux clients we have to deal |
|
* with downstream messages from the mux client and upstream messages |
|
* from the ssh server: |
|
* 1) Handling downstream messages is straightforward and happens |
|
* in channel_proxy_downstream(): |
|
* - We forward all messages (mostly) unmodified to the server. |
|
* - However, in order to route messages from upstream to the correct |
|
* downstream client, we have to replace the channel IDs used by the |
|
* mux clients with a unique channel ID because the mux clients might |
|
* use conflicting channel IDs. |
|
* - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and |
|
* SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local |
|
* SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID |
|
* with the newly allocated channel ID. |
|
* 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY |
|
* channels and procesed by channel_proxy_upstream(). The local channel ID |
|
* is then translated back to the original mux client ID. |
|
* 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE |
|
* messages so we can clean up SSH_CHANNEL_MUX_PROXY channels. |
|
* 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the |
|
* downstream mux client are removed. |
|
* 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server |
|
* requires more work, because they are not addressed to a specific |
|
* channel. E.g. client_request_forwarded_tcpip() needs to figure |
|
* out whether the request is addressed to the local client or a |
|
* specific downstream client based on the listen-address/port. |
|
* 6) Agent and X11-Forwarding have a similar problem and are currenly |
|
* not supported as the matching session/channel cannot be identified |
|
* easily. |
|
*/ |
|
|
|
/* |
|
* receive packets from downstream mux clients: |
|
* channel callback fired on read from mux client, creates |
|
* SSH_CHANNEL_MUX_PROXY channels and translates channel IDs |
|
* on channel creation. |
|
*/ |
|
int |
|
channel_proxy_downstream(Channel *downstream) |
|
{ |
|
Channel *c = NULL; |
|
struct ssh *ssh = active_state; |
|
struct sshbuf *original = NULL, *modified = NULL; |
|
const u_char *cp; |
|
char *ctype = NULL, *listen_host = NULL; |
|
u_char type; |
|
size_t have; |
|
int ret = -1, r, idx; |
|
u_int id, remote_id, listen_port; |
|
|
|
/* sshbuf_dump(&downstream->input, stderr); */ |
|
if ((r = sshbuf_get_string_direct(&downstream->input, &cp, &have)) |
|
!= 0) { |
|
error("%s: malformed message: %s", __func__, ssh_err(r)); |
|
return -1; |
|
} |
|
if (have < 2) { |
|
error("%s: short message", __func__); |
|
return -1; |
|
} |
|
type = cp[1]; |
|
/* skip padlen + type */ |
|
cp += 2; |
|
have -= 2; |
|
if (ssh_packet_log_type(type)) |
|
debug3("%s: channel %u: down->up: type %u", __func__, |
|
downstream->self, type); |
|
|
|
switch (type) { |
|
case SSH2_MSG_CHANNEL_OPEN: |
|
if ((original = sshbuf_from(cp, have)) == NULL || |
|
(modified = sshbuf_new()) == NULL) { |
|
error("%s: alloc", __func__); |
|
goto out; |
|
} |
|
if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 || |
|
(r = sshbuf_get_u32(original, &id)) != 0) { |
|
error("%s: parse error %s", __func__, ssh_err(r)); |
|
goto out; |
|
} |
|
c = channel_new("mux proxy", SSH_CHANNEL_MUX_PROXY, |
|
-1, -1, -1, 0, 0, 0, ctype, 1); |
|
c->mux_ctx = downstream; /* point to mux client */ |
|
c->mux_downstream_id = id; /* original downstream id */ |
|
if ((r = sshbuf_put_cstring(modified, ctype)) != 0 || |
|
(r = sshbuf_put_u32(modified, c->self)) != 0 || |
|
(r = sshbuf_putb(modified, original)) != 0) { |
|
error("%s: compose error %s", __func__, ssh_err(r)); |
|
channel_free(c); |
|
goto out; |
|
} |
|
break; |
|
case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: |
|
/* |
|
* Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we |
|
* need to parse 'remote_id' instead of 'ctype'. |
|
*/ |
|
if ((original = sshbuf_from(cp, have)) == NULL || |
|
(modified = sshbuf_new()) == NULL) { |
|
error("%s: alloc", __func__); |
|
goto out; |
|
} |
|
if ((r = sshbuf_get_u32(original, &remote_id)) != 0 || |
|
(r = sshbuf_get_u32(original, &id)) != 0) { |
|
error("%s: parse error %s", __func__, ssh_err(r)); |
|
goto out; |
|
} |
|
c = channel_new("mux proxy", SSH_CHANNEL_MUX_PROXY, |
|
-1, -1, -1, 0, 0, 0, "mux-down-connect", 1); |
|
c->mux_ctx = downstream; /* point to mux client */ |
|
c->mux_downstream_id = id; |
|
c->remote_id = remote_id; |
|
if ((r = sshbuf_put_u32(modified, remote_id)) != 0 || |
|
(r = sshbuf_put_u32(modified, c->self)) != 0 || |
|
(r = sshbuf_putb(modified, original)) != 0) { |
|
error("%s: compose error %s", __func__, ssh_err(r)); |
|
channel_free(c); |
|
goto out; |
|
} |
|
break; |
|
case SSH2_MSG_GLOBAL_REQUEST: |
|
if ((original = sshbuf_from(cp, have)) == NULL) { |
|
error("%s: alloc", __func__); |
|
goto out; |
|
} |
|
if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) { |
|
error("%s: parse error %s", __func__, ssh_err(r)); |
|
goto out; |
|
} |
|
if (strcmp(ctype, "tcpip-forward") != 0) { |
|
error("%s: unsupported request %s", __func__, ctype); |
|
goto out; |
|
} |
|
if ((r = sshbuf_get_u8(original, NULL)) != 0 || |
|
(r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 || |
|
(r = sshbuf_get_u32(original, &listen_port)) != 0) { |
|
error("%s: parse error %s", __func__, ssh_err(r)); |
|
goto out; |
|
} |
|
if (listen_port > 65535) { |
|
error("%s: tcpip-forward for %s: bad port %u", |
|
__func__, listen_host, listen_port); |
|
goto out; |
|
} |
|
/* Record that connection to this host/port is permitted. */ |
|
permitted_opens = xreallocarray(permitted_opens, |
|
num_permitted_opens + 1, sizeof(*permitted_opens)); |
|
idx = num_permitted_opens++; |
|
permitted_opens[idx].host_to_connect = xstrdup("<mux>"); |
|
permitted_opens[idx].port_to_connect = -1; |
|
permitted_opens[idx].listen_host = listen_host; |
|
permitted_opens[idx].listen_port = (int)listen_port; |
|
permitted_opens[idx].downstream = downstream; |
|
listen_host = NULL; |
|
break; |
|
case SSH2_MSG_CHANNEL_CLOSE: |
|
if (have < 4) |
|
break; |
|
remote_id = PEEK_U32(cp); |
|
if ((c = channel_by_remote_id(remote_id)) != NULL) { |
|
if (c->flags & CHAN_CLOSE_RCVD) |
|
channel_free(c); |
|
else |
|
c->flags |= CHAN_CLOSE_SENT; |
|
} |
|
break; |
|
} |
|
if (modified) { |
|
if ((r = sshpkt_start(ssh, type)) != 0 || |
|
(r = sshpkt_putb(ssh, modified)) != 0 || |
|
(r = sshpkt_send(ssh)) != 0) { |
|
error("%s: send %s", __func__, ssh_err(r)); |
|
goto out; |
|
} |
|
} else { |
|
if ((r = sshpkt_start(ssh, type)) != 0 || |
|
(r = sshpkt_put(ssh, cp, have)) != 0 || |
|
(r = sshpkt_send(ssh)) != 0) { |
|
error("%s: send %s", __func__, ssh_err(r)); |
|
goto out; |
|
} |
|
} |
|
ret = 0; |
|
out: |
|
free(ctype); |
|
free(listen_host); |
|
sshbuf_free(original); |
|
sshbuf_free(modified); |
|
return ret; |
|
} |
|
|
|
/* |
|
* receive packets from upstream server and de-multiplex packets |
|
* to correct downstream: |
|
* implemented as a helper for channel input handlers, |
|
* replaces local (proxy) channel ID with downstream channel ID. |
|
*/ |
|
int |
|
channel_proxy_upstream(Channel *c, int type, u_int32_t seq, void *ctxt) |
|
{ |
|
struct ssh *ssh = active_state; |
|
struct sshbuf *b = NULL; |
|
Channel *downstream; |
|
const u_char *cp = NULL; |
|
size_t len; |
|
int r; |
|
|
|
/* |
|
* When receiving packets from the peer we need to check whether we |
|
* need to forward the packets to the mux client. In this case we |
|
* restore the orignal channel id and keep track of CLOSE messages, |
|
* so we can cleanup the channel. |
|
*/ |
|
if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY) |
|
return 0; |
|
if ((downstream = c->mux_ctx) == NULL) |
|
return 0; |
|
switch (type) { |
|
case SSH2_MSG_CHANNEL_CLOSE: |
|
case SSH2_MSG_CHANNEL_DATA: |
|
case SSH2_MSG_CHANNEL_EOF: |
|
case SSH2_MSG_CHANNEL_EXTENDED_DATA: |
|
case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: |
|
case SSH2_MSG_CHANNEL_OPEN_FAILURE: |
|
case SSH2_MSG_CHANNEL_WINDOW_ADJUST: |
|
case SSH2_MSG_CHANNEL_SUCCESS: |
|
case SSH2_MSG_CHANNEL_FAILURE: |
|
case SSH2_MSG_CHANNEL_REQUEST: |
|
break; |
|
default: |
|
debug2("%s: channel %u: unsupported type %u", __func__, |
|
c->self, type); |
|
return 0; |
|
} |
|
if ((b = sshbuf_new()) == NULL) { |
|
error("%s: alloc reply", __func__); |
|
goto out; |
|
} |
|
/* get remaining payload (after id) */ |
|
cp = sshpkt_ptr(ssh, &len); |
|
if (cp == NULL) { |
|
error("%s: no packet", __func__); |
|
goto out; |
|
} |
|
/* translate id and send to muxclient */ |
|
if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ |
|
(r = sshbuf_put_u8(b, type)) != 0 || |
|
(r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 || |
|
(r = sshbuf_put(b, cp, len)) != 0 || |
|
(r = sshbuf_put_stringb(&downstream->output, b)) != 0) { |
|
error("%s: compose for muxclient %s", __func__, ssh_err(r)); |
|
goto out; |
|
} |
|
/* sshbuf_dump(b, stderr); */ |
|
if (ssh_packet_log_type(type)) |
|
debug3("%s: channel %u: up->down: type %u", __func__, c->self, |
|
type); |
|
out: |
|
/* update state */ |
|
switch (type) { |
|
case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: |
|
/* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ |
|
if (cp && len > 4) |
|
c->remote_id = PEEK_U32(cp); |
|
break; |
|
case SSH2_MSG_CHANNEL_CLOSE: |
|
if (c->flags & CHAN_CLOSE_SENT) |
|
channel_free(c); |
|
else |
|
c->flags |= CHAN_CLOSE_RCVD; |
|
break; |
|
} |
|
sshbuf_free(b); |
|
return 1; |
|
} |
|
|
/* -- protocol input */ |
/* -- protocol input */ |
|
|
Line 2394 channel_input_data(int type, u_int32_t s |
|
Line 2736 channel_input_data(int type, u_int32_t s |
|
c = channel_lookup(id); |
c = channel_lookup(id); |
if (c == NULL) |
if (c == NULL) |
packet_disconnect("Received data for nonexistent channel %d.", id); |
packet_disconnect("Received data for nonexistent channel %d.", id); |
|
if (channel_proxy_upstream(c, type, seq, ctxt)) |
|
return 0; |
|
|
/* Ignore any data for non-open channels (might happen on close) */ |
/* Ignore any data for non-open channels (might happen on close) */ |
if (c->type != SSH_CHANNEL_OPEN && |
if (c->type != SSH_CHANNEL_OPEN && |
Line 2456 channel_input_extended_data(int type, u_ |
|
Line 2800 channel_input_extended_data(int type, u_ |
|
|
|
if (c == NULL) |
if (c == NULL) |
packet_disconnect("Received extended_data for bad channel %d.", id); |
packet_disconnect("Received extended_data for bad channel %d.", id); |
|
if (channel_proxy_upstream(c, type, seq, ctxt)) |
|
return 0; |
if (c->type != SSH_CHANNEL_OPEN) { |
if (c->type != SSH_CHANNEL_OPEN) { |
logit("channel %d: ext data for non open", id); |
logit("channel %d: ext data for non open", id); |
return 0; |
return 0; |
Line 2501 channel_input_ieof(int type, u_int32_t s |
|
Line 2847 channel_input_ieof(int type, u_int32_t s |
|
c = channel_lookup(id); |
c = channel_lookup(id); |
if (c == NULL) |
if (c == NULL) |
packet_disconnect("Received ieof for nonexistent channel %d.", id); |
packet_disconnect("Received ieof for nonexistent channel %d.", id); |
|
if (channel_proxy_upstream(c, type, seq, ctxt)) |
|
return 0; |
chan_rcvd_ieof(c); |
chan_rcvd_ieof(c); |
|
|
/* XXX force input close */ |
/* XXX force input close */ |
Line 2525 channel_input_close(int type, u_int32_t |
|
Line 2873 channel_input_close(int type, u_int32_t |
|
c = channel_lookup(id); |
c = channel_lookup(id); |
if (c == NULL) |
if (c == NULL) |
packet_disconnect("Received close for nonexistent channel %d.", id); |
packet_disconnect("Received close for nonexistent channel %d.", id); |
|
if (channel_proxy_upstream(c, type, seq, ctxt)) |
|
return 0; |
/* |
/* |
* Send a confirmation that we have closed the channel and no more |
* Send a confirmation that we have closed the channel and no more |
* data is coming for it. |
* data is coming for it. |
Line 2560 channel_input_oclose(int type, u_int32_t |
|
Line 2909 channel_input_oclose(int type, u_int32_t |
|
int id = packet_get_int(); |
int id = packet_get_int(); |
Channel *c = channel_lookup(id); |
Channel *c = channel_lookup(id); |
|
|
packet_check_eom(); |
|
if (c == NULL) |
if (c == NULL) |
packet_disconnect("Received oclose for nonexistent channel %d.", id); |
packet_disconnect("Received oclose for nonexistent channel %d.", id); |
|
if (channel_proxy_upstream(c, type, seq, ctxt)) |
|
return 0; |
|
packet_check_eom(); |
chan_rcvd_oclose(c); |
chan_rcvd_oclose(c); |
return 0; |
return 0; |
} |
} |
Line 2574 channel_input_close_confirmation(int typ |
|
Line 2925 channel_input_close_confirmation(int typ |
|
int id = packet_get_int(); |
int id = packet_get_int(); |
Channel *c = channel_lookup(id); |
Channel *c = channel_lookup(id); |
|
|
packet_check_eom(); |
|
if (c == NULL) |
if (c == NULL) |
packet_disconnect("Received close confirmation for " |
packet_disconnect("Received close confirmation for " |
"out-of-range channel %d.", id); |
"out-of-range channel %d.", id); |
|
if (channel_proxy_upstream(c, type, seq, ctxt)) |
|
return 0; |
|
packet_check_eom(); |
if (c->type != SSH_CHANNEL_CLOSED && c->type != SSH_CHANNEL_ABANDONED) |
if (c->type != SSH_CHANNEL_CLOSED && c->type != SSH_CHANNEL_ABANDONED) |
packet_disconnect("Received close confirmation for " |
packet_disconnect("Received close confirmation for " |
"non-closed channel %d (type %d).", id, c->type); |
"non-closed channel %d (type %d).", id, c->type); |
Line 2595 channel_input_open_confirmation(int type |
|
Line 2948 channel_input_open_confirmation(int type |
|
id = packet_get_int(); |
id = packet_get_int(); |
c = channel_lookup(id); |
c = channel_lookup(id); |
|
|
if (c==NULL || c->type != SSH_CHANNEL_OPENING) |
if (c==NULL) |
|
packet_disconnect("Received open confirmation for " |
|
"unknown channel %d.", id); |
|
if (channel_proxy_upstream(c, type, seq, ctxt)) |
|
return 0; |
|
if (c->type != SSH_CHANNEL_OPENING) |
packet_disconnect("Received open confirmation for " |
packet_disconnect("Received open confirmation for " |
"non-opening channel %d.", id); |
"non-opening channel %d.", id); |
remote_id = packet_get_int(); |
remote_id = packet_get_int(); |
Line 2645 channel_input_open_failure(int type, u_i |
|
Line 3003 channel_input_open_failure(int type, u_i |
|
id = packet_get_int(); |
id = packet_get_int(); |
c = channel_lookup(id); |
c = channel_lookup(id); |
|
|
if (c==NULL || c->type != SSH_CHANNEL_OPENING) |
if (c==NULL) |
|
packet_disconnect("Received open failure for " |
|
"unknown channel %d.", id); |
|
if (channel_proxy_upstream(c, type, seq, ctxt)) |
|
return 0; |
|
if (c->type != SSH_CHANNEL_OPENING) |
packet_disconnect("Received open failure for " |
packet_disconnect("Received open failure for " |
"non-opening channel %d.", id); |
"non-opening channel %d.", id); |
if (compat20) { |
if (compat20) { |
Line 2689 channel_input_window_adjust(int type, u_ |
|
Line 3052 channel_input_window_adjust(int type, u_ |
|
logit("Received window adjust for non-open channel %d.", id); |
logit("Received window adjust for non-open channel %d.", id); |
return 0; |
return 0; |
} |
} |
|
if (channel_proxy_upstream(c, type, seq, ctxt)) |
|
return 0; |
adjust = packet_get_int(); |
adjust = packet_get_int(); |
packet_check_eom(); |
packet_check_eom(); |
debug2("channel %d: rcvd adjust %u", id, adjust); |
debug2("channel %d: rcvd adjust %u", id, adjust); |
Line 2743 channel_input_status_confirm(int type, u |
|
Line 3108 channel_input_status_confirm(int type, u |
|
packet_set_alive_timeouts(0); |
packet_set_alive_timeouts(0); |
|
|
id = packet_get_int(); |
id = packet_get_int(); |
packet_check_eom(); |
|
|
|
debug2("channel_input_status_confirm: type %d id %d", type, id); |
debug2("channel_input_status_confirm: type %d id %d", type, id); |
|
|
if ((c = channel_lookup(id)) == NULL) { |
if ((c = channel_lookup(id)) == NULL) { |
logit("channel_input_status_confirm: %d: unknown", id); |
logit("channel_input_status_confirm: %d: unknown", id); |
return 0; |
return 0; |
} |
} |
|
if (channel_proxy_upstream(c, type, seq, ctxt)) |
|
return 0; |
|
packet_check_eom(); |
if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) |
if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) |
return 0; |
return 0; |
cc->cb(type, c, cc->ctx); |
cc->cb(type, c, cc->ctx); |
Line 2955 channel_setup_fwd_listener_tcpip(int typ |
|
Line 3321 channel_setup_fwd_listener_tcpip(int typ |
|
if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && |
if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && |
allocated_listen_port != NULL && |
allocated_listen_port != NULL && |
*allocated_listen_port == 0) { |
*allocated_listen_port == 0) { |
*allocated_listen_port = get_sock_port(sock, 1); |
*allocated_listen_port = get_local_port(sock); |
debug("Allocated listen port %d", |
debug("Allocated listen port %d", |
*allocated_listen_port); |
*allocated_listen_port); |
} |
} |
Line 3311 channel_request_remote_forwarding(struct |
|
Line 3677 channel_request_remote_forwarding(struct |
|
permitted_opens[idx].listen_path = NULL; |
permitted_opens[idx].listen_path = NULL; |
permitted_opens[idx].listen_port = fwd->listen_port; |
permitted_opens[idx].listen_port = fwd->listen_port; |
} |
} |
|
permitted_opens[idx].downstream = NULL; |
} |
} |
return (idx); |
return (idx); |
} |
} |
Line 3324 open_match(ForwardPermission *allowed_op |
|
Line 3691 open_match(ForwardPermission *allowed_op |
|
if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && |
if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && |
allowed_open->port_to_connect != requestedport) |
allowed_open->port_to_connect != requestedport) |
return 0; |
return 0; |
if (strcmp(allowed_open->host_to_connect, requestedhost) != 0) |
if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 && |
|
strcmp(allowed_open->host_to_connect, requestedhost) != 0) |
return 0; |
return 0; |
return 1; |
return 1; |
} |
} |
Line 3405 channel_request_rforward_cancel_tcpip(co |
|
Line 3773 channel_request_rforward_cancel_tcpip(co |
|
free(permitted_opens[i].listen_host); |
free(permitted_opens[i].listen_host); |
permitted_opens[i].listen_host = NULL; |
permitted_opens[i].listen_host = NULL; |
permitted_opens[i].listen_path = NULL; |
permitted_opens[i].listen_path = NULL; |
|
permitted_opens[i].downstream = NULL; |
|
|
return 0; |
return 0; |
} |
} |
Line 3442 channel_request_rforward_cancel_streamlo |
|
Line 3811 channel_request_rforward_cancel_streamlo |
|
permitted_opens[i].listen_host = NULL; |
permitted_opens[i].listen_host = NULL; |
free(permitted_opens[i].listen_path); |
free(permitted_opens[i].listen_path); |
permitted_opens[i].listen_path = NULL; |
permitted_opens[i].listen_path = NULL; |
|
permitted_opens[i].downstream = NULL; |
|
|
return 0; |
return 0; |
} |
} |
Line 3462 channel_request_rforward_cancel(struct F |
|
Line 3832 channel_request_rforward_cancel(struct F |
|
} |
} |
|
|
/* |
/* |
* 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). |
|
*/ |
|
int |
|
channel_input_port_forward_request(int is_root, struct ForwardOptions *fwd_opts) |
|
{ |
|
int success = 0; |
|
struct Forward fwd; |
|
|
|
/* Get arguments from the packet. */ |
|
memset(&fwd, 0, sizeof(fwd)); |
|
fwd.listen_port = packet_get_int(); |
|
fwd.connect_host = packet_get_string(NULL); |
|
fwd.connect_port = packet_get_int(); |
|
|
|
/* |
|
* Check that an unprivileged user is not trying to forward a |
|
* privileged port. |
|
*/ |
|
if (fwd.listen_port < IPPORT_RESERVED && !is_root) |
|
packet_disconnect( |
|
"Requested forwarding of port %d but user is not root.", |
|
fwd.listen_port); |
|
if (fwd.connect_port == 0) |
|
packet_disconnect("Dynamic forwarding denied."); |
|
|
|
/* Initiate forwarding */ |
|
success = channel_setup_local_fwd_listener(&fwd, fwd_opts); |
|
|
|
/* Free the argument string. */ |
|
free(fwd.connect_host); |
|
|
|
return (success ? 0 : -1); |
|
} |
|
|
|
/* |
|
* Permits opening to any host/port if permitted_opens[] is empty. This is |
* Permits opening to any host/port if permitted_opens[] is empty. This is |
* usually called by the server, because the user could connect to any port |
* usually called by the server, because the user could connect to any port |
* anyway, and the server has no way to know but to trust the client anyway. |
* anyway, and the server has no way to know but to trust the client anyway. |
Line 3522 channel_add_permitted_opens(char *host, |
|
Line 3855 channel_add_permitted_opens(char *host, |
|
permitted_opens[num_permitted_opens].listen_host = NULL; |
permitted_opens[num_permitted_opens].listen_host = NULL; |
permitted_opens[num_permitted_opens].listen_path = NULL; |
permitted_opens[num_permitted_opens].listen_path = NULL; |
permitted_opens[num_permitted_opens].listen_port = 0; |
permitted_opens[num_permitted_opens].listen_port = 0; |
|
permitted_opens[num_permitted_opens].downstream = NULL; |
num_permitted_opens++; |
num_permitted_opens++; |
|
|
all_opens_permitted = 0; |
all_opens_permitted = 0; |
Line 3653 connect_next(struct channel_connect *cct |
|
Line 3987 connect_next(struct channel_connect *cct |
|
{ |
{ |
int sock, saved_errno; |
int sock, saved_errno; |
struct sockaddr_un *sunaddr; |
struct sockaddr_un *sunaddr; |
char ntop[NI_MAXHOST], strport[MAX(NI_MAXSERV,sizeof(sunaddr->sun_path))]; |
char ntop[NI_MAXHOST], strport[MAXIMUM(NI_MAXSERV,sizeof(sunaddr->sun_path))]; |
|
|
for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { |
for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { |
switch (cctx->ai->ai_family) { |
switch (cctx->ai->ai_family) { |
Line 3784 connect_to(const char *name, int port, c |
|
Line 4118 connect_to(const char *name, int port, c |
|
return c; |
return c; |
} |
} |
|
|
|
/* |
|
* returns either the newly connected channel or the downstream channel |
|
* that needs to deal with this connection. |
|
*/ |
Channel * |
Channel * |
channel_connect_by_listen_address(const char *listen_host, |
channel_connect_by_listen_address(const char *listen_host, |
u_short listen_port, const char *ctype, char *rname) |
u_short listen_port, const char *ctype, char *rname) |
Line 3793 channel_connect_by_listen_address(const |
|
Line 4131 channel_connect_by_listen_address(const |
|
for (i = 0; i < num_permitted_opens; i++) { |
for (i = 0; i < num_permitted_opens; i++) { |
if (open_listen_match_tcpip(&permitted_opens[i], listen_host, |
if (open_listen_match_tcpip(&permitted_opens[i], listen_host, |
listen_port, 1)) { |
listen_port, 1)) { |
|
if (permitted_opens[i].downstream) |
|
return permitted_opens[i].downstream; |
return connect_to( |
return connect_to( |
permitted_opens[i].host_to_connect, |
permitted_opens[i].host_to_connect, |
permitted_opens[i].port_to_connect, ctype, rname); |
permitted_opens[i].port_to_connect, ctype, rname); |
Line 4211 x11_request_forwarding_with_spoofing(int |
|
Line 4551 x11_request_forwarding_with_spoofing(int |
|
char *new_data; |
char *new_data; |
int screen_number; |
int screen_number; |
const char *cp; |
const char *cp; |
u_int32_t rnd = 0; |
|
|
|
if (x11_saved_display == NULL) |
if (x11_saved_display == NULL) |
x11_saved_display = xstrdup(disp); |
x11_saved_display = xstrdup(disp); |
Line 4232 x11_request_forwarding_with_spoofing(int |
|
Line 4571 x11_request_forwarding_with_spoofing(int |
|
if (x11_saved_proto == NULL) { |
if (x11_saved_proto == NULL) { |
/* Save protocol name. */ |
/* Save protocol name. */ |
x11_saved_proto = xstrdup(proto); |
x11_saved_proto = xstrdup(proto); |
/* |
|
* Extract real authentication data and generate fake data |
/* Extract real authentication data. */ |
* of the same length. |
|
*/ |
|
x11_saved_data = xmalloc(data_len); |
x11_saved_data = xmalloc(data_len); |
x11_fake_data = xmalloc(data_len); |
|
for (i = 0; i < data_len; i++) { |
for (i = 0; i < data_len; i++) { |
if (sscanf(data + 2 * i, "%2x", &value) != 1) |
if (sscanf(data + 2 * i, "%2x", &value) != 1) |
fatal("x11_request_forwarding: bad " |
fatal("x11_request_forwarding: bad " |
"authentication data: %.100s", data); |
"authentication data: %.100s", data); |
if (i % 4 == 0) |
|
rnd = arc4random(); |
|
x11_saved_data[i] = value; |
x11_saved_data[i] = value; |
x11_fake_data[i] = rnd & 0xff; |
|
rnd >>= 8; |
|
} |
} |
x11_saved_data_len = data_len; |
x11_saved_data_len = data_len; |
|
|
|
/* Generate fake data of the same length. */ |
|
x11_fake_data = xmalloc(data_len); |
|
arc4random_buf(x11_fake_data, data_len); |
x11_fake_data_len = data_len; |
x11_fake_data_len = data_len; |
} |
} |
|
|