mirror of
https://github.com/PowerShell/openssh-portable.git
synced 2025-07-31 01:35:11 +02:00
upstream commit
Add 'reverse' dynamic forwarding which combines dynamic forwarding (-D) with remote forwarding (-R) where the remote-forwarded port expects SOCKS-requests. The SSH server code is unchanged and the parsing happens at the SSH clients side. Thus the full SOCKS-request is sent over the forwarded channel and the client parses c->output. Parsing happens in channel_before_prepare_select(), _before_ the select bitmask is computed in the pre[] handlers, but after network input processing in the post[] handlers. help and ok djm@ Upstream-ID: aa25a6a3851064f34fe719e0bf15656ad5a64b89
This commit is contained in:
parent
36945fa103
commit
609d7a66ce
374
channels.c
374
channels.c
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: channels.c,v 1.371 2017/09/19 12:10:30 millert Exp $ */
|
/* $OpenBSD: channels.c,v 1.372 2017/09/21 19:16:53 markus Exp $ */
|
||||||
/*
|
/*
|
||||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||||
@ -209,6 +209,8 @@ 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 *);
|
||||||
static void channel_connect_ctx_free(struct channel_connect *);
|
static void channel_connect_ctx_free(struct channel_connect *);
|
||||||
|
static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *);
|
||||||
|
static int rdynamic_connect_finish(struct ssh *, Channel *);
|
||||||
|
|
||||||
/* Setup helper */
|
/* Setup helper */
|
||||||
static void channel_handler_init(struct ssh_channels *sc);
|
static void channel_handler_init(struct ssh_channels *sc);
|
||||||
@ -282,6 +284,8 @@ channel_lookup(struct ssh *ssh, int id)
|
|||||||
case SSH_CHANNEL_LARVAL:
|
case SSH_CHANNEL_LARVAL:
|
||||||
case SSH_CHANNEL_CONNECTING:
|
case SSH_CHANNEL_CONNECTING:
|
||||||
case SSH_CHANNEL_DYNAMIC:
|
case SSH_CHANNEL_DYNAMIC:
|
||||||
|
case SSH_CHANNEL_RDYNAMIC_OPEN:
|
||||||
|
case SSH_CHANNEL_RDYNAMIC_FINISH:
|
||||||
case SSH_CHANNEL_OPENING:
|
case SSH_CHANNEL_OPENING:
|
||||||
case SSH_CHANNEL_OPEN:
|
case SSH_CHANNEL_OPEN:
|
||||||
case SSH_CHANNEL_ABANDONED:
|
case SSH_CHANNEL_ABANDONED:
|
||||||
@ -671,6 +675,7 @@ channel_still_open(struct ssh *ssh)
|
|||||||
case SSH_CHANNEL_CLOSED:
|
case SSH_CHANNEL_CLOSED:
|
||||||
case SSH_CHANNEL_AUTH_SOCKET:
|
case SSH_CHANNEL_AUTH_SOCKET:
|
||||||
case SSH_CHANNEL_DYNAMIC:
|
case SSH_CHANNEL_DYNAMIC:
|
||||||
|
case SSH_CHANNEL_RDYNAMIC_OPEN:
|
||||||
case SSH_CHANNEL_CONNECTING:
|
case SSH_CHANNEL_CONNECTING:
|
||||||
case SSH_CHANNEL_ZOMBIE:
|
case SSH_CHANNEL_ZOMBIE:
|
||||||
case SSH_CHANNEL_ABANDONED:
|
case SSH_CHANNEL_ABANDONED:
|
||||||
@ -681,6 +686,7 @@ channel_still_open(struct ssh *ssh)
|
|||||||
continue;
|
continue;
|
||||||
case SSH_CHANNEL_OPENING:
|
case SSH_CHANNEL_OPENING:
|
||||||
case SSH_CHANNEL_OPEN:
|
case SSH_CHANNEL_OPEN:
|
||||||
|
case SSH_CHANNEL_RDYNAMIC_FINISH:
|
||||||
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:
|
case SSH_CHANNEL_MUX_PROXY:
|
||||||
@ -707,6 +713,8 @@ channel_find_open(struct ssh *ssh)
|
|||||||
switch (c->type) {
|
switch (c->type) {
|
||||||
case SSH_CHANNEL_CLOSED:
|
case SSH_CHANNEL_CLOSED:
|
||||||
case SSH_CHANNEL_DYNAMIC:
|
case SSH_CHANNEL_DYNAMIC:
|
||||||
|
case SSH_CHANNEL_RDYNAMIC_OPEN:
|
||||||
|
case SSH_CHANNEL_RDYNAMIC_FINISH:
|
||||||
case SSH_CHANNEL_X11_LISTENER:
|
case SSH_CHANNEL_X11_LISTENER:
|
||||||
case SSH_CHANNEL_PORT_LISTENER:
|
case SSH_CHANNEL_PORT_LISTENER:
|
||||||
case SSH_CHANNEL_RPORT_LISTENER:
|
case SSH_CHANNEL_RPORT_LISTENER:
|
||||||
@ -772,6 +780,8 @@ channel_open_message(struct ssh *ssh)
|
|||||||
case SSH_CHANNEL_OPENING:
|
case SSH_CHANNEL_OPENING:
|
||||||
case SSH_CHANNEL_CONNECTING:
|
case SSH_CHANNEL_CONNECTING:
|
||||||
case SSH_CHANNEL_DYNAMIC:
|
case SSH_CHANNEL_DYNAMIC:
|
||||||
|
case SSH_CHANNEL_RDYNAMIC_OPEN:
|
||||||
|
case SSH_CHANNEL_RDYNAMIC_FINISH:
|
||||||
case SSH_CHANNEL_OPEN:
|
case SSH_CHANNEL_OPEN:
|
||||||
case SSH_CHANNEL_X11_OPEN:
|
case SSH_CHANNEL_X11_OPEN:
|
||||||
case SSH_CHANNEL_MUX_PROXY:
|
case SSH_CHANNEL_MUX_PROXY:
|
||||||
@ -1124,8 +1134,7 @@ channel_pre_mux_client(struct ssh *ssh,
|
|||||||
|
|
||||||
/* try to decode a socks4 header */
|
/* try to decode a socks4 header */
|
||||||
static int
|
static int
|
||||||
channel_decode_socks4(struct ssh *ssh, Channel *c,
|
channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output)
|
||||||
fd_set *readset, fd_set *writeset)
|
|
||||||
{
|
{
|
||||||
const u_char *p;
|
const u_char *p;
|
||||||
char *host;
|
char *host;
|
||||||
@ -1141,11 +1150,11 @@ channel_decode_socks4(struct ssh *ssh, Channel *c,
|
|||||||
|
|
||||||
debug2("channel %d: decode socks4", c->self);
|
debug2("channel %d: decode socks4", c->self);
|
||||||
|
|
||||||
have = sshbuf_len(c->input);
|
have = sshbuf_len(input);
|
||||||
len = sizeof(s4_req);
|
len = sizeof(s4_req);
|
||||||
if (have < len)
|
if (have < len)
|
||||||
return 0;
|
return 0;
|
||||||
p = sshbuf_ptr(c->input);
|
p = sshbuf_ptr(input);
|
||||||
|
|
||||||
need = 1;
|
need = 1;
|
||||||
/* SOCKS4A uses an invalid IP address 0.0.0.x */
|
/* SOCKS4A uses an invalid IP address 0.0.0.x */
|
||||||
@ -1170,15 +1179,15 @@ channel_decode_socks4(struct ssh *ssh, Channel *c,
|
|||||||
}
|
}
|
||||||
if (found < need)
|
if (found < need)
|
||||||
return 0;
|
return 0;
|
||||||
if ((r = sshbuf_get(c->input, &s4_req.version, 1)) != 0 ||
|
if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 ||
|
||||||
(r = sshbuf_get(c->input, &s4_req.command, 1)) != 0 ||
|
(r = sshbuf_get(input, &s4_req.command, 1)) != 0 ||
|
||||||
(r = sshbuf_get(c->input, &s4_req.dest_port, 2)) != 0 ||
|
(r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 ||
|
||||||
(r = sshbuf_get(c->input, &s4_req.dest_addr, 4)) != 0) {
|
(r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) {
|
||||||
debug("channels %d: decode socks4: %s", c->self, ssh_err(r));
|
debug("channels %d: decode socks4: %s", c->self, ssh_err(r));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
have = sshbuf_len(c->input);
|
have = sshbuf_len(input);
|
||||||
p = sshbuf_ptr(c->input);
|
p = sshbuf_ptr(input);
|
||||||
if (memchr(p, '\0', have) == NULL) {
|
if (memchr(p, '\0', have) == NULL) {
|
||||||
error("channel %d: decode socks4: user not nul terminated",
|
error("channel %d: decode socks4: user not nul terminated",
|
||||||
c->self);
|
c->self);
|
||||||
@ -1188,7 +1197,7 @@ channel_decode_socks4(struct ssh *ssh, Channel *c,
|
|||||||
debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
|
debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
|
||||||
len++; /* trailing '\0' */
|
len++; /* trailing '\0' */
|
||||||
strlcpy(username, p, sizeof(username));
|
strlcpy(username, p, sizeof(username));
|
||||||
if ((r = sshbuf_consume(c->input, len)) != 0) {
|
if ((r = sshbuf_consume(input, len)) != 0) {
|
||||||
fatal("%s: channel %d: consume: %s", __func__,
|
fatal("%s: channel %d: consume: %s", __func__,
|
||||||
c->self, ssh_err(r));
|
c->self, ssh_err(r));
|
||||||
}
|
}
|
||||||
@ -1198,8 +1207,8 @@ channel_decode_socks4(struct ssh *ssh, Channel *c,
|
|||||||
host = inet_ntoa(s4_req.dest_addr);
|
host = inet_ntoa(s4_req.dest_addr);
|
||||||
c->path = xstrdup(host);
|
c->path = xstrdup(host);
|
||||||
} else { /* SOCKS4A: two strings */
|
} else { /* SOCKS4A: two strings */
|
||||||
have = sshbuf_len(c->input);
|
have = sshbuf_len(input);
|
||||||
p = sshbuf_ptr(c->input);
|
p = sshbuf_ptr(input);
|
||||||
if (memchr(p, '\0', have) == NULL) {
|
if (memchr(p, '\0', have) == NULL) {
|
||||||
error("channel %d: decode socks4a: host not nul "
|
error("channel %d: decode socks4a: host not nul "
|
||||||
"terminated", c->self);
|
"terminated", c->self);
|
||||||
@ -1215,7 +1224,7 @@ channel_decode_socks4(struct ssh *ssh, Channel *c,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
c->path = xstrdup(p);
|
c->path = xstrdup(p);
|
||||||
if ((r = sshbuf_consume(c->input, len)) != 0) {
|
if ((r = sshbuf_consume(input, len)) != 0) {
|
||||||
fatal("%s: channel %d: consume: %s", __func__,
|
fatal("%s: channel %d: consume: %s", __func__,
|
||||||
c->self, ssh_err(r));
|
c->self, ssh_err(r));
|
||||||
}
|
}
|
||||||
@ -1234,7 +1243,7 @@ channel_decode_socks4(struct ssh *ssh, Channel *c,
|
|||||||
s4_rsp.command = 90; /* cd: req granted */
|
s4_rsp.command = 90; /* cd: req granted */
|
||||||
s4_rsp.dest_port = 0; /* ignored */
|
s4_rsp.dest_port = 0; /* ignored */
|
||||||
s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */
|
s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */
|
||||||
if ((r = sshbuf_put(c->output, &s4_rsp, sizeof(s4_rsp))) != 0) {
|
if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0) {
|
||||||
fatal("%s: channel %d: append reply: %s", __func__,
|
fatal("%s: channel %d: append reply: %s", __func__,
|
||||||
c->self, ssh_err(r));
|
c->self, ssh_err(r));
|
||||||
}
|
}
|
||||||
@ -1251,8 +1260,7 @@ channel_decode_socks4(struct ssh *ssh, Channel *c,
|
|||||||
#define SSH_SOCKS5_SUCCESS 0x00
|
#define SSH_SOCKS5_SUCCESS 0x00
|
||||||
|
|
||||||
static int
|
static int
|
||||||
channel_decode_socks5(struct ssh *ssh, Channel *c,
|
channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output)
|
||||||
fd_set *readset, fd_set *writeset)
|
|
||||||
{
|
{
|
||||||
/* XXX use get/put_u8 instead of trusting struct padding */
|
/* XXX use get/put_u8 instead of trusting struct padding */
|
||||||
struct {
|
struct {
|
||||||
@ -1268,10 +1276,10 @@ channel_decode_socks5(struct ssh *ssh, Channel *c,
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
debug2("channel %d: decode socks5", c->self);
|
debug2("channel %d: decode socks5", c->self);
|
||||||
p = sshbuf_ptr(c->input);
|
p = sshbuf_ptr(input);
|
||||||
if (p[0] != 0x05)
|
if (p[0] != 0x05)
|
||||||
return -1;
|
return -1;
|
||||||
have = sshbuf_len(c->input);
|
have = sshbuf_len(input);
|
||||||
if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
|
if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
|
||||||
/* format: ver | nmethods | methods */
|
/* format: ver | nmethods | methods */
|
||||||
if (have < 2)
|
if (have < 2)
|
||||||
@ -1291,17 +1299,16 @@ channel_decode_socks5(struct ssh *ssh, Channel *c,
|
|||||||
c->self);
|
c->self);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if ((r = sshbuf_consume(c->input, nmethods + 2)) != 0) {
|
if ((r = sshbuf_consume(input, nmethods + 2)) != 0) {
|
||||||
fatal("%s: channel %d: consume: %s", __func__,
|
fatal("%s: channel %d: consume: %s", __func__,
|
||||||
c->self, ssh_err(r));
|
c->self, ssh_err(r));
|
||||||
}
|
}
|
||||||
/* version, method */
|
/* version, method */
|
||||||
if ((r = sshbuf_put_u8(c->output, 0x05)) != 0 ||
|
if ((r = sshbuf_put_u8(output, 0x05)) != 0 ||
|
||||||
(r = sshbuf_put_u8(c->output, SSH_SOCKS5_NOAUTH)) != 0) {
|
(r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0) {
|
||||||
fatal("%s: channel %d: append reply: %s", __func__,
|
fatal("%s: channel %d: append reply: %s", __func__,
|
||||||
c->self, ssh_err(r));
|
c->self, ssh_err(r));
|
||||||
}
|
}
|
||||||
FD_SET(c->sock, writeset);
|
|
||||||
c->flags |= SSH_SOCKS5_AUTHDONE;
|
c->flags |= SSH_SOCKS5_AUTHDONE;
|
||||||
debug2("channel %d: socks5 auth done", c->self);
|
debug2("channel %d: socks5 auth done", c->self);
|
||||||
return 0; /* need more */
|
return 0; /* need more */
|
||||||
@ -1338,19 +1345,19 @@ channel_decode_socks5(struct ssh *ssh, Channel *c,
|
|||||||
need++;
|
need++;
|
||||||
if (have < need)
|
if (have < need)
|
||||||
return 0;
|
return 0;
|
||||||
if ((r = sshbuf_consume(c->input, sizeof(s5_req))) != 0) {
|
if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0) {
|
||||||
fatal("%s: channel %d: consume: %s", __func__,
|
fatal("%s: channel %d: consume: %s", __func__,
|
||||||
c->self, ssh_err(r));
|
c->self, ssh_err(r));
|
||||||
}
|
}
|
||||||
if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
|
if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
|
||||||
/* host string length */
|
/* host string length */
|
||||||
if ((r = sshbuf_consume(c->input, 1)) != 0) {
|
if ((r = sshbuf_consume(input, 1)) != 0) {
|
||||||
fatal("%s: channel %d: consume: %s", __func__,
|
fatal("%s: channel %d: consume: %s", __func__,
|
||||||
c->self, ssh_err(r));
|
c->self, ssh_err(r));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((r = sshbuf_get(c->input, &dest_addr, addrlen)) != 0 ||
|
if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 ||
|
||||||
(r = sshbuf_get(c->input, &dest_port, 2)) != 0) {
|
(r = sshbuf_get(input, &dest_port, 2)) != 0) {
|
||||||
debug("channel %d: parse addr/port: %s", c->self, ssh_err(r));
|
debug("channel %d: parse addr/port: %s", c->self, ssh_err(r));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -1380,9 +1387,9 @@ channel_decode_socks5(struct ssh *ssh, Channel *c,
|
|||||||
s5_rsp.atyp = SSH_SOCKS5_IPV4;
|
s5_rsp.atyp = SSH_SOCKS5_IPV4;
|
||||||
dest_port = 0; /* ignored */
|
dest_port = 0; /* ignored */
|
||||||
|
|
||||||
if ((r = sshbuf_put(c->output, &s5_rsp, sizeof(s5_rsp))) != 0 ||
|
if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 ||
|
||||||
(r = sshbuf_put_u32(c->output, ntohl(INADDR_ANY))) != 0 ||
|
(r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 ||
|
||||||
(r = sshbuf_put(c->output, &dest_port, sizeof(dest_port))) != 0)
|
(r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0)
|
||||||
fatal("%s: channel %d: append reply: %s", __func__,
|
fatal("%s: channel %d: append reply: %s", __func__,
|
||||||
c->self, ssh_err(r));
|
c->self, ssh_err(r));
|
||||||
return 1;
|
return 1;
|
||||||
@ -1434,10 +1441,10 @@ channel_pre_dynamic(struct ssh *ssh, Channel *c,
|
|||||||
/* XXX sshbuf_peek_u8? */
|
/* XXX sshbuf_peek_u8? */
|
||||||
switch (p[0]) {
|
switch (p[0]) {
|
||||||
case 0x04:
|
case 0x04:
|
||||||
ret = channel_decode_socks4(ssh, c, readset, writeset);
|
ret = channel_decode_socks4(c, c->input, c->output);
|
||||||
break;
|
break;
|
||||||
case 0x05:
|
case 0x05:
|
||||||
ret = channel_decode_socks5(ssh, c, readset, writeset);
|
ret = channel_decode_socks5(c, c->input, c->output);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -1;
|
ret = -1;
|
||||||
@ -1449,6 +1456,8 @@ channel_pre_dynamic(struct ssh *ssh, Channel *c,
|
|||||||
debug2("channel %d: pre_dynamic: need more", c->self);
|
debug2("channel %d: pre_dynamic: need more", c->self);
|
||||||
/* need more */
|
/* need more */
|
||||||
FD_SET(c->sock, readset);
|
FD_SET(c->sock, readset);
|
||||||
|
if (sshbuf_len(c->output))
|
||||||
|
FD_SET(c->sock, writeset);
|
||||||
} else {
|
} else {
|
||||||
/* switch to the next state */
|
/* switch to the next state */
|
||||||
c->type = SSH_CHANNEL_OPENING;
|
c->type = SSH_CHANNEL_OPENING;
|
||||||
@ -1456,6 +1465,81 @@ channel_pre_dynamic(struct ssh *ssh, Channel *c,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* simulate read-error */
|
||||||
|
static void
|
||||||
|
rdynamic_close(struct ssh *ssh, Channel *c)
|
||||||
|
{
|
||||||
|
c->type = SSH_CHANNEL_OPEN;
|
||||||
|
chan_read_failed(ssh, c);
|
||||||
|
sshbuf_reset(c->input);
|
||||||
|
chan_ibuf_empty(ssh, c);
|
||||||
|
sshbuf_reset(c->output);
|
||||||
|
chan_write_failed(ssh, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reverse dynamic port forwarding */
|
||||||
|
static void
|
||||||
|
channel_before_prepare_select_rdynamic(struct ssh *ssh, Channel *c)
|
||||||
|
{
|
||||||
|
const u_char *p;
|
||||||
|
u_int have, len;
|
||||||
|
int r, ret;
|
||||||
|
|
||||||
|
have = sshbuf_len(c->output);
|
||||||
|
debug2("channel %d: pre_rdynamic: have %d", c->self, have);
|
||||||
|
/* sshbuf_dump(c->output, stderr); */
|
||||||
|
/* EOF received */
|
||||||
|
if (c->flags & CHAN_EOF_RCVD) {
|
||||||
|
if ((r = sshbuf_consume(c->output, have)) != 0) {
|
||||||
|
fatal("%s: channel %d: consume: %s",
|
||||||
|
__func__, c->self, ssh_err(r));
|
||||||
|
}
|
||||||
|
rdynamic_close(ssh, c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* check if the fixed size part of the packet is in buffer. */
|
||||||
|
if (have < 3)
|
||||||
|
return;
|
||||||
|
/* try to guess the protocol */
|
||||||
|
p = sshbuf_ptr(c->output);
|
||||||
|
switch (p[0]) {
|
||||||
|
case 0x04:
|
||||||
|
/* switch input/output for reverse forwarding */
|
||||||
|
ret = channel_decode_socks4(c, c->output, c->input);
|
||||||
|
break;
|
||||||
|
case 0x05:
|
||||||
|
ret = channel_decode_socks5(c, c->output, c->input);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
rdynamic_close(ssh, c);
|
||||||
|
} else if (ret == 0) {
|
||||||
|
debug2("channel %d: pre_rdynamic: need more", c->self);
|
||||||
|
/* send socks request to peer */
|
||||||
|
len = sshbuf_len(c->input);
|
||||||
|
if (len > 0 && len < c->remote_window) {
|
||||||
|
if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
|
||||||
|
(r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
|
||||||
|
(r = sshpkt_put_stringb(ssh, c->input)) != 0 ||
|
||||||
|
(r = sshpkt_send(ssh)) != 0) {
|
||||||
|
fatal("%s: channel %i: rdynamic: %s", __func__,
|
||||||
|
c->self, ssh_err(r));
|
||||||
|
}
|
||||||
|
if ((r = sshbuf_consume(c->input, len)) != 0) {
|
||||||
|
fatal("%s: channel %d: consume: %s",
|
||||||
|
__func__, c->self, ssh_err(r));
|
||||||
|
}
|
||||||
|
c->remote_window -= len;
|
||||||
|
}
|
||||||
|
} else if (rdynamic_connect_finish(ssh, c) < 0) {
|
||||||
|
/* the connect failed */
|
||||||
|
rdynamic_close(ssh, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* This is our fake X11 server socket. */
|
/* This is our fake X11 server socket. */
|
||||||
static void
|
static void
|
||||||
channel_post_x11_listener(struct ssh *ssh, Channel *c,
|
channel_post_x11_listener(struct ssh *ssh, Channel *c,
|
||||||
@ -1699,14 +1783,15 @@ static void
|
|||||||
channel_post_connecting(struct ssh *ssh, Channel *c,
|
channel_post_connecting(struct ssh *ssh, Channel *c,
|
||||||
fd_set *readset, fd_set *writeset)
|
fd_set *readset, fd_set *writeset)
|
||||||
{
|
{
|
||||||
int err = 0, sock, r;
|
int err = 0, sock, isopen, r;
|
||||||
socklen_t sz = sizeof(err);
|
socklen_t sz = sizeof(err);
|
||||||
|
|
||||||
if (!FD_ISSET(c->sock, writeset))
|
if (!FD_ISSET(c->sock, writeset))
|
||||||
return;
|
return;
|
||||||
if (!c->have_remote_id)
|
if (!c->have_remote_id)
|
||||||
fatal(":%s: channel %d: no remote id", __func__, c->self);
|
fatal(":%s: channel %d: no remote id", __func__, c->self);
|
||||||
|
/* for rdynamic the OPEN_CONFIRMATION has been sent already */
|
||||||
|
isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH);
|
||||||
if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
|
if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
|
||||||
err = errno;
|
err = errno;
|
||||||
error("getsockopt SO_ERROR failed");
|
error("getsockopt SO_ERROR failed");
|
||||||
@ -1716,14 +1801,21 @@ channel_post_connecting(struct ssh *ssh, Channel *c,
|
|||||||
c->self, c->connect_ctx.host, c->connect_ctx.port);
|
c->self, c->connect_ctx.host, c->connect_ctx.port);
|
||||||
channel_connect_ctx_free(&c->connect_ctx);
|
channel_connect_ctx_free(&c->connect_ctx);
|
||||||
c->type = SSH_CHANNEL_OPEN;
|
c->type = SSH_CHANNEL_OPEN;
|
||||||
if ((r = sshpkt_start(ssh,
|
if (isopen) {
|
||||||
SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
|
/* no message necessary */
|
||||||
(r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
|
} else {
|
||||||
(r = sshpkt_put_u32(ssh, c->self)) != 0 ||
|
if ((r = sshpkt_start(ssh,
|
||||||
(r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
|
SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
|
||||||
(r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) {
|
(r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
|
||||||
fatal("%s: channel %i: confirm: %s", __func__,
|
(r = sshpkt_put_u32(ssh, c->self)) != 0 ||
|
||||||
c->self, ssh_err(r));
|
(r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
|
||||||
|
(r = sshpkt_put_u32(ssh, c->local_maxpacket))
|
||||||
|
!= 0)
|
||||||
|
fatal("%s: channel %i: confirm: %s", __func__,
|
||||||
|
c->self, ssh_err(r));
|
||||||
|
if ((r = sshpkt_send(ssh)) != 0)
|
||||||
|
fatal("%s: channel %i: %s", __func__, c->self,
|
||||||
|
ssh_err(r));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug("channel %d: connection failed: %s",
|
debug("channel %d: connection failed: %s",
|
||||||
@ -1739,22 +1831,27 @@ channel_post_connecting(struct ssh *ssh, Channel *c,
|
|||||||
error("connect_to %.100s port %d: failed.",
|
error("connect_to %.100s port %d: failed.",
|
||||||
c->connect_ctx.host, c->connect_ctx.port);
|
c->connect_ctx.host, c->connect_ctx.port);
|
||||||
channel_connect_ctx_free(&c->connect_ctx);
|
channel_connect_ctx_free(&c->connect_ctx);
|
||||||
if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 ||
|
if (isopen) {
|
||||||
(r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
|
rdynamic_close(ssh, c);
|
||||||
(r = sshpkt_put_u32(ssh, SSH2_OPEN_CONNECT_FAILED)) != 0) {
|
} else {
|
||||||
fatal("%s: channel %i: failure: %s", __func__,
|
if ((r = sshpkt_start(ssh,
|
||||||
c->self, ssh_err(r));
|
SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 ||
|
||||||
|
(r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
|
||||||
|
(r = sshpkt_put_u32(ssh, SSH2_OPEN_CONNECT_FAILED))
|
||||||
|
!= 0)
|
||||||
|
fatal("%s: channel %i: failure: %s", __func__,
|
||||||
|
c->self, ssh_err(r));
|
||||||
|
if ((datafellows & SSH_BUG_OPENFAILURE) == 0 &&
|
||||||
|
((r = sshpkt_put_cstring(ssh, strerror(err))) != 0 ||
|
||||||
|
(r = sshpkt_put_cstring(ssh, "")) != 0))
|
||||||
|
fatal("%s: channel %i: failure: %s", __func__,
|
||||||
|
c->self, ssh_err(r));
|
||||||
|
if ((r = sshpkt_send(ssh)) != 0)
|
||||||
|
fatal("%s: channel %i: %s", __func__, c->self,
|
||||||
|
ssh_err(r));
|
||||||
|
chan_mark_dead(ssh, c);
|
||||||
}
|
}
|
||||||
if ((datafellows & SSH_BUG_OPENFAILURE) == 0 &&
|
|
||||||
((r = sshpkt_put_cstring(ssh, strerror(err))) != 0 ||
|
|
||||||
(r = sshpkt_put_cstring(ssh, "")) != 0)) {
|
|
||||||
fatal("%s: channel %i: failure: %s", __func__,
|
|
||||||
c->self, ssh_err(r));
|
|
||||||
}
|
|
||||||
chan_mark_dead(ssh, c);
|
|
||||||
}
|
}
|
||||||
if ((r = sshpkt_send(ssh)) != 0)
|
|
||||||
fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -2187,6 +2284,7 @@ channel_handler_init(struct ssh_channels *sc)
|
|||||||
pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener;
|
pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener;
|
||||||
pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting;
|
pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting;
|
||||||
pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic;
|
pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic;
|
||||||
|
pre[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_pre_connecting;
|
||||||
pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener;
|
pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener;
|
||||||
pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client;
|
pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client;
|
||||||
|
|
||||||
@ -2199,6 +2297,7 @@ channel_handler_init(struct ssh_channels *sc)
|
|||||||
post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
|
post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
|
||||||
post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
|
post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
|
||||||
post[SSH_CHANNEL_DYNAMIC] = &channel_post_open;
|
post[SSH_CHANNEL_DYNAMIC] = &channel_post_open;
|
||||||
|
post[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_post_connecting;
|
||||||
post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener;
|
post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener;
|
||||||
post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client;
|
post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client;
|
||||||
|
|
||||||
@ -2279,6 +2378,27 @@ channel_handler(struct ssh *ssh, int table,
|
|||||||
__func__, (int)*unpause_secs);
|
__func__, (int)*unpause_secs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create sockets before allocating the select bitmasks.
|
||||||
|
* This is necessary for things that need to happen after reading
|
||||||
|
* the network-input but before channel_prepare_select().
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
channel_before_prepare_select(struct ssh *ssh)
|
||||||
|
{
|
||||||
|
struct ssh_channels *sc = ssh->chanctxt;
|
||||||
|
Channel *c;
|
||||||
|
u_int i, oalloc;
|
||||||
|
|
||||||
|
for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
|
||||||
|
c = sc->channels[i];
|
||||||
|
if (c == NULL)
|
||||||
|
continue;
|
||||||
|
if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN)
|
||||||
|
channel_before_prepare_select_rdynamic(ssh, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate/update select bitmasks and add any bits relevant to channels in
|
* Allocate/update select bitmasks and add any bits relevant to channels in
|
||||||
* select bitmasks.
|
* select bitmasks.
|
||||||
@ -2289,6 +2409,8 @@ channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp,
|
|||||||
{
|
{
|
||||||
u_int n, sz, nfdset;
|
u_int n, sz, nfdset;
|
||||||
|
|
||||||
|
channel_before_prepare_select(ssh); /* might update channel_max_fd */
|
||||||
|
|
||||||
n = MAXIMUM(*maxfdp, ssh->chanctxt->channel_max_fd);
|
n = MAXIMUM(*maxfdp, ssh->chanctxt->channel_max_fd);
|
||||||
|
|
||||||
nfdset = howmany(n+1, NFDBITS);
|
nfdset = howmany(n+1, NFDBITS);
|
||||||
@ -2794,6 +2916,8 @@ channel_input_data(int type, u_int32_t seq, struct ssh *ssh)
|
|||||||
|
|
||||||
/* 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 &&
|
||||||
|
c->type != SSH_CHANNEL_RDYNAMIC_OPEN &&
|
||||||
|
c->type != SSH_CHANNEL_RDYNAMIC_FINISH &&
|
||||||
c->type != SSH_CHANNEL_X11_OPEN)
|
c->type != SSH_CHANNEL_X11_OPEN)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -3032,7 +3156,7 @@ channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh)
|
|||||||
if ((c = channel_lookup(ssh, id)) == NULL) {
|
if ((c = channel_lookup(ssh, id)) == NULL) {
|
||||||
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, ssh))
|
if (channel_proxy_upstream(c, type, seq, ssh))
|
||||||
return 0;
|
return 0;
|
||||||
@ -3939,21 +4063,18 @@ channel_connect_ctx_free(struct channel_connect *cctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return CONNECTING channel to remote host:port or local socket path,
|
* Return connecting socket to remote host:port or local socket path,
|
||||||
* passing back the failure reason if appropriate.
|
* passing back the failure reason if appropriate.
|
||||||
*/
|
*/
|
||||||
static Channel *
|
static int
|
||||||
connect_to_reason(struct ssh *ssh, const char *name, int port,
|
connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype,
|
||||||
char *ctype, char *rname, int *reason, const char **errmsg)
|
char *ctype, char *rname, struct channel_connect *cctx,
|
||||||
|
int *reason, const char **errmsg)
|
||||||
{
|
{
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
int gaierr;
|
int gaierr;
|
||||||
int sock = -1;
|
int sock = -1;
|
||||||
char strport[NI_MAXSERV];
|
char strport[NI_MAXSERV];
|
||||||
struct channel_connect cctx;
|
|
||||||
Channel *c;
|
|
||||||
|
|
||||||
memset(&cctx, 0, sizeof(cctx));
|
|
||||||
|
|
||||||
if (port == PORT_STREAMLOCAL) {
|
if (port == PORT_STREAMLOCAL) {
|
||||||
struct sockaddr_un *sunaddr;
|
struct sockaddr_un *sunaddr;
|
||||||
@ -3961,7 +4082,7 @@ connect_to_reason(struct ssh *ssh, const char *name, int port,
|
|||||||
|
|
||||||
if (strlen(name) > sizeof(sunaddr->sun_path)) {
|
if (strlen(name) > sizeof(sunaddr->sun_path)) {
|
||||||
error("%.100s: %.100s", name, strerror(ENAMETOOLONG));
|
error("%.100s: %.100s", name, strerror(ENAMETOOLONG));
|
||||||
return (NULL);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3974,18 +4095,18 @@ connect_to_reason(struct ssh *ssh, const char *name, int port,
|
|||||||
ai->ai_addr = (struct sockaddr *)(ai + 1);
|
ai->ai_addr = (struct sockaddr *)(ai + 1);
|
||||||
ai->ai_addrlen = sizeof(*sunaddr);
|
ai->ai_addrlen = sizeof(*sunaddr);
|
||||||
ai->ai_family = AF_UNIX;
|
ai->ai_family = AF_UNIX;
|
||||||
ai->ai_socktype = SOCK_STREAM;
|
ai->ai_socktype = socktype;
|
||||||
ai->ai_protocol = PF_UNSPEC;
|
ai->ai_protocol = PF_UNSPEC;
|
||||||
sunaddr = (struct sockaddr_un *)ai->ai_addr;
|
sunaddr = (struct sockaddr_un *)ai->ai_addr;
|
||||||
sunaddr->sun_family = AF_UNIX;
|
sunaddr->sun_family = AF_UNIX;
|
||||||
strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path));
|
strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path));
|
||||||
cctx.aitop = ai;
|
cctx->aitop = ai;
|
||||||
} else {
|
} else {
|
||||||
memset(&hints, 0, sizeof(hints));
|
memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_family = ssh->chanctxt->IPv4or6;
|
hints.ai_family = ssh->chanctxt->IPv4or6;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = socktype;
|
||||||
snprintf(strport, sizeof strport, "%d", port);
|
snprintf(strport, sizeof strport, "%d", port);
|
||||||
if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop))
|
if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop))
|
||||||
!= 0) {
|
!= 0) {
|
||||||
if (errmsg != NULL)
|
if (errmsg != NULL)
|
||||||
*errmsg = ssh_gai_strerror(gaierr);
|
*errmsg = ssh_gai_strerror(gaierr);
|
||||||
@ -3993,32 +4114,46 @@ connect_to_reason(struct ssh *ssh, const char *name, int port,
|
|||||||
*reason = SSH2_OPEN_CONNECT_FAILED;
|
*reason = SSH2_OPEN_CONNECT_FAILED;
|
||||||
error("connect_to %.100s: unknown host (%s)", name,
|
error("connect_to %.100s: unknown host (%s)", name,
|
||||||
ssh_gai_strerror(gaierr));
|
ssh_gai_strerror(gaierr));
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cctx.host = xstrdup(name);
|
cctx->host = xstrdup(name);
|
||||||
cctx.port = port;
|
cctx->port = port;
|
||||||
cctx.ai = cctx.aitop;
|
cctx->ai = cctx->aitop;
|
||||||
|
|
||||||
if ((sock = connect_next(&cctx)) == -1) {
|
if ((sock = connect_next(cctx)) == -1) {
|
||||||
error("connect to %.100s port %d failed: %s",
|
error("connect to %.100s port %d failed: %s",
|
||||||
name, port, strerror(errno));
|
name, port, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return CONNECTING channel to remote host:port or local socket path */
|
||||||
|
static Channel *
|
||||||
|
connect_to(struct ssh *ssh, const char *host, int port,
|
||||||
|
char *ctype, char *rname)
|
||||||
|
{
|
||||||
|
struct channel_connect cctx;
|
||||||
|
Channel *c;
|
||||||
|
int sock;
|
||||||
|
|
||||||
|
memset(&cctx, 0, sizeof(cctx));
|
||||||
|
sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
|
||||||
|
&cctx, NULL, NULL);
|
||||||
|
if (sock == -1) {
|
||||||
channel_connect_ctx_free(&cctx);
|
channel_connect_ctx_free(&cctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
|
c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
|
||||||
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
|
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
|
||||||
|
c->host_port = port;
|
||||||
|
c->path = xstrdup(host);
|
||||||
c->connect_ctx = cctx;
|
c->connect_ctx = cctx;
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return CONNECTING channel to remote host:port or local socket path */
|
return c;
|
||||||
static Channel *
|
|
||||||
connect_to(struct ssh *ssh, const char *name, int port,
|
|
||||||
char *ctype, char *rname)
|
|
||||||
{
|
|
||||||
return connect_to_reason(ssh, name, port, ctype, rname, NULL, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4038,6 +4173,9 @@ channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host,
|
|||||||
if (open_listen_match_tcpip(fp, listen_host, listen_port, 1)) {
|
if (open_listen_match_tcpip(fp, listen_host, listen_port, 1)) {
|
||||||
if (fp->downstream)
|
if (fp->downstream)
|
||||||
return fp->downstream;
|
return fp->downstream;
|
||||||
|
if (fp->port_to_connect == 0)
|
||||||
|
return rdynamic_connect_prepare(ssh,
|
||||||
|
ctype, rname);
|
||||||
return connect_to(ssh,
|
return connect_to(ssh,
|
||||||
fp->host_to_connect, fp->port_to_connect,
|
fp->host_to_connect, fp->port_to_connect,
|
||||||
ctype, rname);
|
ctype, rname);
|
||||||
@ -4075,7 +4213,10 @@ channel_connect_to_port(struct ssh *ssh, const char *host, u_short port,
|
|||||||
char *ctype, char *rname, int *reason, const char **errmsg)
|
char *ctype, char *rname, int *reason, const char **errmsg)
|
||||||
{
|
{
|
||||||
struct ssh_channels *sc = ssh->chanctxt;
|
struct ssh_channels *sc = ssh->chanctxt;
|
||||||
|
struct channel_connect cctx;
|
||||||
|
Channel *c;
|
||||||
u_int i, permit, permit_adm = 1;
|
u_int i, permit, permit_adm = 1;
|
||||||
|
int sock;
|
||||||
ForwardPermission *fp;
|
ForwardPermission *fp;
|
||||||
|
|
||||||
permit = sc->all_opens_permitted;
|
permit = sc->all_opens_permitted;
|
||||||
@ -4107,7 +4248,22 @@ channel_connect_to_port(struct ssh *ssh, const char *host, u_short port,
|
|||||||
*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
|
*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return connect_to_reason(ssh, host, port, ctype, rname, reason, errmsg);
|
|
||||||
|
memset(&cctx, 0, sizeof(cctx));
|
||||||
|
sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
|
||||||
|
&cctx, reason, errmsg);
|
||||||
|
if (sock == -1) {
|
||||||
|
channel_connect_ctx_free(&cctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
|
||||||
|
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
|
||||||
|
c->host_port = port;
|
||||||
|
c->path = xstrdup(host);
|
||||||
|
c->connect_ctx = cctx;
|
||||||
|
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if connecting to that path is permitted and connect. */
|
/* Check if connecting to that path is permitted and connect. */
|
||||||
@ -4174,6 +4330,54 @@ channel_send_window_changes(struct ssh *ssh)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */
|
||||||
|
static Channel *
|
||||||
|
rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname)
|
||||||
|
{
|
||||||
|
Channel *c;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1,
|
||||||
|
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
|
||||||
|
c->host_port = 0;
|
||||||
|
c->path = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to open the channel before we have a FD,
|
||||||
|
* so that we can get SOCKS header from peer.
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
fatal("%s: channel %i: confirm: %s", __func__,
|
||||||
|
c->self, ssh_err(r));
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return CONNECTING socket to remote host:port or local socket path */
|
||||||
|
static int
|
||||||
|
rdynamic_connect_finish(struct ssh *ssh, Channel *c)
|
||||||
|
{
|
||||||
|
struct channel_connect cctx;
|
||||||
|
int sock;
|
||||||
|
|
||||||
|
memset(&cctx, 0, sizeof(cctx));
|
||||||
|
sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL,
|
||||||
|
NULL, &cctx, NULL, NULL);
|
||||||
|
if (sock == -1)
|
||||||
|
channel_connect_ctx_free(&cctx);
|
||||||
|
else {
|
||||||
|
/* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */
|
||||||
|
c->type = SSH_CHANNEL_RDYNAMIC_FINISH;
|
||||||
|
c->connect_ctx = cctx;
|
||||||
|
channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0);
|
||||||
|
}
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
/* -- X11 forwarding */
|
/* -- X11 forwarding */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: channels.h,v 1.129 2017/09/12 06:35:32 djm Exp $ */
|
/* $OpenBSD: channels.h,v 1.130 2017/09/21 19:16:53 markus Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
@ -57,7 +57,9 @@
|
|||||||
#define SSH_CHANNEL_UNIX_LISTENER 18 /* Listening on a domain socket. */
|
#define SSH_CHANNEL_UNIX_LISTENER 18 /* Listening on a domain socket. */
|
||||||
#define SSH_CHANNEL_RUNIX_LISTENER 19 /* Listening to a R-style domain socket. */
|
#define SSH_CHANNEL_RUNIX_LISTENER 19 /* Listening to a R-style domain socket. */
|
||||||
#define SSH_CHANNEL_MUX_PROXY 20 /* proxy channel for mux-slave */
|
#define SSH_CHANNEL_MUX_PROXY 20 /* proxy channel for mux-slave */
|
||||||
#define SSH_CHANNEL_MAX_TYPE 21
|
#define SSH_CHANNEL_RDYNAMIC_OPEN 21 /* reverse SOCKS, parsing request */
|
||||||
|
#define SSH_CHANNEL_RDYNAMIC_FINISH 22 /* reverse SOCKS, finishing connect */
|
||||||
|
#define SSH_CHANNEL_MAX_TYPE 23
|
||||||
|
|
||||||
#define CHANNEL_CANCEL_PORT_STATIC -1
|
#define CHANNEL_CANCEL_PORT_STATIC -1
|
||||||
|
|
||||||
|
42
readconf.c
42
readconf.c
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: readconf.c,v 1.278 2017/09/03 23:33:13 djm Exp $ */
|
/* $OpenBSD: readconf.c,v 1.279 2017/09/21 19:16:53 markus Exp $ */
|
||||||
/*
|
/*
|
||||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||||
@ -836,6 +836,7 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
|
|||||||
char **cpptr, fwdarg[256];
|
char **cpptr, fwdarg[256];
|
||||||
u_int i, *uintptr, max_entries = 0;
|
u_int i, *uintptr, max_entries = 0;
|
||||||
int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
|
int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
|
||||||
|
int remotefwd, dynamicfwd;
|
||||||
LogLevel *log_level_ptr;
|
LogLevel *log_level_ptr;
|
||||||
SyslogFacility *log_facility_ptr;
|
SyslogFacility *log_facility_ptr;
|
||||||
long long val64;
|
long long val64;
|
||||||
@ -1255,31 +1256,36 @@ parse_keytypes:
|
|||||||
fatal("%.200s line %d: Missing port argument.",
|
fatal("%.200s line %d: Missing port argument.",
|
||||||
filename, linenum);
|
filename, linenum);
|
||||||
|
|
||||||
if (opcode == oLocalForward ||
|
remotefwd = (opcode == oRemoteForward);
|
||||||
opcode == oRemoteForward) {
|
dynamicfwd = (opcode == oDynamicForward);
|
||||||
|
|
||||||
|
if (!dynamicfwd) {
|
||||||
arg2 = strdelim(&s);
|
arg2 = strdelim(&s);
|
||||||
if (arg2 == NULL || *arg2 == '\0')
|
if (arg2 == NULL || *arg2 == '\0') {
|
||||||
fatal("%.200s line %d: Missing target argument.",
|
if (remotefwd)
|
||||||
filename, linenum);
|
dynamicfwd = 1;
|
||||||
|
else
|
||||||
/* construct a string for parse_forward */
|
fatal("%.200s line %d: Missing target "
|
||||||
snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2);
|
"argument.", filename, linenum);
|
||||||
} else if (opcode == oDynamicForward) {
|
} else {
|
||||||
strlcpy(fwdarg, arg, sizeof(fwdarg));
|
/* construct a string for parse_forward */
|
||||||
|
snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg,
|
||||||
|
arg2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (dynamicfwd)
|
||||||
|
strlcpy(fwdarg, arg, sizeof(fwdarg));
|
||||||
|
|
||||||
if (parse_forward(&fwd, fwdarg,
|
if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0)
|
||||||
opcode == oDynamicForward ? 1 : 0,
|
|
||||||
opcode == oRemoteForward ? 1 : 0) == 0)
|
|
||||||
fatal("%.200s line %d: Bad forwarding specification.",
|
fatal("%.200s line %d: Bad forwarding specification.",
|
||||||
filename, linenum);
|
filename, linenum);
|
||||||
|
|
||||||
if (*activep) {
|
if (*activep) {
|
||||||
if (opcode == oLocalForward ||
|
if (remotefwd) {
|
||||||
opcode == oDynamicForward)
|
|
||||||
add_local_forward(options, &fwd);
|
|
||||||
else if (opcode == oRemoteForward)
|
|
||||||
add_remote_forward(options, &fwd);
|
add_remote_forward(options, &fwd);
|
||||||
|
} else {
|
||||||
|
add_local_forward(options, &fwd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
21
ssh.1
21
ssh.1
@ -33,8 +33,8 @@
|
|||||||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
.\"
|
.\"
|
||||||
.\" $OpenBSD: ssh.1,v 1.383 2017/06/09 06:43:01 djm Exp $
|
.\" $OpenBSD: ssh.1,v 1.384 2017/09/21 19:16:53 markus Exp $
|
||||||
.Dd $Mdocdate: June 9 2017 $
|
.Dd $Mdocdate: September 21 2017 $
|
||||||
.Dt SSH 1
|
.Dt SSH 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -592,21 +592,30 @@ Causes most warning and diagnostic messages to be suppressed.
|
|||||||
.Ar remote_socket : local_socket
|
.Ar remote_socket : local_socket
|
||||||
.Sm on
|
.Sm on
|
||||||
.Xc
|
.Xc
|
||||||
|
.It Fl R Xo
|
||||||
|
.Sm off
|
||||||
|
.Oo Ar bind_address : Oc
|
||||||
|
.Ar port
|
||||||
|
.Sm on
|
||||||
|
.Xc
|
||||||
Specifies that connections to the given TCP port or Unix socket on the remote
|
Specifies that connections to the given TCP port or Unix socket on the remote
|
||||||
(server) host are to be forwarded to the given host and port, or Unix socket,
|
(server) host are to be forwarded to the local side.
|
||||||
on the local side.
|
.Pp
|
||||||
This works by allocating a socket to listen to either a TCP
|
This works by allocating a socket to listen to either a TCP
|
||||||
.Ar port
|
.Ar port
|
||||||
or to a Unix socket on the remote side.
|
or to a Unix socket on the remote side.
|
||||||
Whenever a connection is made to this port or Unix socket, the
|
Whenever a connection is made to this port or Unix socket, the
|
||||||
connection is forwarded over the secure channel, and a connection
|
connection is forwarded over the secure channel, and a connection
|
||||||
is made to either
|
is made from the local machine to either an explicit destination specified by
|
||||||
.Ar host
|
.Ar host
|
||||||
port
|
port
|
||||||
.Ar hostport ,
|
.Ar hostport ,
|
||||||
or
|
or
|
||||||
.Ar local_socket ,
|
.Ar local_socket ,
|
||||||
from the local machine.
|
or, if no explicit destination was specified,
|
||||||
|
.Nm
|
||||||
|
will act as a SOCKS 4/5 proxy and forward connections to the destinations
|
||||||
|
requested by the remote SOCKS client.
|
||||||
.Pp
|
.Pp
|
||||||
Port forwardings can also be specified in the configuration file.
|
Port forwardings can also be specified in the configuration file.
|
||||||
Privileged ports can be forwarded only when
|
Privileged ports can be forwarded only when
|
||||||
|
5
ssh.c
5
ssh.c
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: ssh.c,v 1.463 2017/09/12 06:32:07 djm Exp $ */
|
/* $OpenBSD: ssh.c,v 1.464 2017/09/21 19:16:53 markus Exp $ */
|
||||||
/*
|
/*
|
||||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||||
@ -868,7 +868,8 @@ main(int ac, char **av)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'R':
|
case 'R':
|
||||||
if (parse_forward(&fwd, optarg, 0, 1)) {
|
if (parse_forward(&fwd, optarg, 0, 1) ||
|
||||||
|
parse_forward(&fwd, optarg, 1, 1)) {
|
||||||
add_remote_forward(&options, &fwd);
|
add_remote_forward(&options, &fwd);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
16
ssh_config.5
16
ssh_config.5
@ -33,8 +33,8 @@
|
|||||||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
.\"
|
.\"
|
||||||
.\" $OpenBSD: ssh_config.5,v 1.255 2017/09/04 06:34:43 jmc Exp $
|
.\" $OpenBSD: ssh_config.5,v 1.256 2017/09/21 19:16:53 markus Exp $
|
||||||
.Dd $Mdocdate: September 4 2017 $
|
.Dd $Mdocdate: September 21 2017 $
|
||||||
.Dt SSH_CONFIG 5
|
.Dt SSH_CONFIG 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -1298,13 +1298,19 @@ accept the tokens described in the
|
|||||||
section.
|
section.
|
||||||
.It Cm RemoteForward
|
.It Cm RemoteForward
|
||||||
Specifies that a TCP port on the remote machine be forwarded over
|
Specifies that a TCP port on the remote machine be forwarded over
|
||||||
the secure channel to the specified host and port from the local machine.
|
the secure channel.
|
||||||
|
The remote port may either be fowarded to a specified host and port
|
||||||
|
from the local machine, or may act as a SOCKS 4/5 proxy that allows a remote
|
||||||
|
client to connect to arbitrary destinations from the local machine.
|
||||||
The first argument must be
|
The first argument must be
|
||||||
.Sm off
|
.Sm off
|
||||||
.Oo Ar bind_address : Oc Ar port
|
.Oo Ar bind_address : Oc Ar port
|
||||||
.Sm on
|
.Sm on
|
||||||
and the second argument must be
|
If forwarding to a specific destination then the second argument must be
|
||||||
.Ar host : Ns Ar hostport .
|
.Ar host : Ns Ar hostport ,
|
||||||
|
otherwise if no destination argument is specified then the remote forwarding
|
||||||
|
will be established as a SOCKS proxy.
|
||||||
|
.Pp
|
||||||
IPv6 addresses can be specified by enclosing addresses in square brackets.
|
IPv6 addresses can be specified by enclosing addresses in square brackets.
|
||||||
Multiple forwardings may be specified, and additional
|
Multiple forwardings may be specified, and additional
|
||||||
forwardings can be given on the command line.
|
forwardings can be given on the command line.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user