version 1.210, 2004/08/23 11:48:47 |
version 1.210.2.3, 2005/09/02 03:44:59 |
|
|
|
|
/* -- channel core */ |
/* -- channel core */ |
|
|
|
#define CHAN_RBUF 16*1024 |
|
|
/* |
/* |
* Pointer to an array containing all allocated channels. The array is |
* Pointer to an array containing all allocated channels. The array is |
* dynamically extended as needed. |
* dynamically extended as needed. |
|
|
/* Maximum number of fake X11 displays to try. */ |
/* Maximum number of fake X11 displays to try. */ |
#define MAX_DISPLAYS 1000 |
#define MAX_DISPLAYS 1000 |
|
|
|
/* Saved X11 local (client) display. */ |
|
static char *x11_saved_display = NULL; |
|
|
/* Saved X11 authentication protocol name. */ |
/* Saved X11 authentication protocol name. */ |
static char *x11_saved_proto = NULL; |
static char *x11_saved_proto = NULL; |
|
|
|
|
{ |
{ |
u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); |
u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); |
|
|
|
/* check buffer limits */ |
|
limit = MIN(limit, (BUFFER_MAX_LEN - BUFFER_MAX_CHUNK - CHAN_RBUF)); |
|
|
if (c->istate == CHAN_INPUT_OPEN && |
if (c->istate == CHAN_INPUT_OPEN && |
limit > 0 && |
limit > 0 && |
buffer_len(&c->input) < limit) |
buffer_len(&c->input) < limit) |
|
|
FD_SET(c->wfd, writeset); |
FD_SET(c->wfd, writeset); |
} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) |
if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) |
debug2("channel %d: obuf_empty delayed efd %d/(%d)", |
debug2("channel %d: obuf_empty delayed efd %d/(%d)", |
c->self, c->efd, buffer_len(&c->extended)); |
c->self, c->efd, buffer_len(&c->extended)); |
else |
else |
chan_obuf_empty(c); |
chan_obuf_empty(c); |
} |
} |
|
|
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; |
char *p, *host; |
int len, have, i, found; |
u_int len, have, i, found; |
char username[256]; |
char username[256]; |
struct { |
struct { |
u_int8_t version; |
u_int8_t version; |
|
|
} s5_req, s5_rsp; |
} s5_req, s5_rsp; |
u_int16_t dest_port; |
u_int16_t dest_port; |
u_char *p, dest_addr[255+1]; |
u_char *p, dest_addr[255+1]; |
int i, have, found, nmethods, addrlen, af; |
u_int have, i, found, nmethods, addrlen, af; |
|
|
debug2("channel %d: decode socks5", c->self); |
debug2("channel %d: decode socks5", c->self); |
p = buffer_ptr(&c->input); |
p = buffer_ptr(&c->input); |
|
|
debug2("channel %d: only socks5 connect supported", c->self); |
debug2("channel %d: only socks5 connect supported", c->self); |
return -1; |
return -1; |
} |
} |
switch(s5_req.atyp){ |
switch (s5_req.atyp){ |
case SSH_SOCKS5_IPV4: |
case SSH_SOCKS5_IPV4: |
addrlen = 4; |
addrlen = 4; |
af = AF_INET; |
af = AF_INET; |
|
|
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_char *p; |
int have, ret; |
u_int have; |
|
int ret; |
|
|
have = buffer_len(&c->input); |
have = buffer_len(&c->input); |
c->delayed = 0; |
c->delayed = 0; |
|
|
int direct; |
int direct; |
char buf[1024]; |
char buf[1024]; |
char *remote_ipaddr = get_peer_ipaddr(c->sock); |
char *remote_ipaddr = get_peer_ipaddr(c->sock); |
u_short remote_port = get_peer_port(c->sock); |
int remote_port = get_peer_port(c->sock); |
|
|
direct = (strcmp(rtype, "direct-tcpip") == 0); |
direct = (strcmp(rtype, "direct-tcpip") == 0); |
|
|
|
|
} |
} |
/* originator host and port */ |
/* originator host and port */ |
packet_put_cstring(remote_ipaddr); |
packet_put_cstring(remote_ipaddr); |
packet_put_int(remote_port); |
packet_put_int((u_int)remote_port); |
packet_send(); |
packet_send(); |
} else { |
} else { |
packet_start(SSH_MSG_PORT_OPEN); |
packet_start(SSH_MSG_PORT_OPEN); |
|
|
static int |
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[16*1024]; |
char buf[CHAN_RBUF]; |
int len; |
int len; |
|
|
if (c->rfd != -1 && |
if (c->rfd != -1 && |
|
|
static int |
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[16*1024]; |
char buf[CHAN_RBUF]; |
int len; |
int len; |
|
|
/** XXX handle drain efd, too */ |
/** XXX handle drain efd, too */ |
|
|
* hack for extended data: delay EOF if EFD still in use. |
* hack for extended data: delay EOF if EFD still in use. |
*/ |
*/ |
if (CHANNEL_EFD_INPUT_ACTIVE(c)) |
if (CHANNEL_EFD_INPUT_ACTIVE(c)) |
debug2("channel %d: ibuf_empty delayed efd %d/(%d)", |
debug2("channel %d: ibuf_empty delayed efd %d/(%d)", |
c->self, c->efd, buffer_len(&c->extended)); |
c->self, c->efd, buffer_len(&c->extended)); |
else |
else |
chan_ibuf_empty(c); |
chan_ibuf_empty(c); |
} |
} |
|
|
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) |
{ |
{ |
Channel *c; |
Channel *c; |
int success, sock, on = 1; |
int sock, r, success = 0, on = 1, wildcard = 0, is_client; |
struct addrinfo hints, *ai, *aitop; |
struct addrinfo hints, *ai, *aitop; |
const char *host; |
const char *host, *addr; |
char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
|
|
success = 0; |
|
host = (type == SSH_CHANNEL_RPORT_LISTENER) ? |
host = (type == SSH_CHANNEL_RPORT_LISTENER) ? |
listen_addr : host_to_connect; |
listen_addr : host_to_connect; |
|
is_client = (type == SSH_CHANNEL_PORT_LISTENER); |
|
|
if (host == NULL) { |
if (host == NULL) { |
error("No forward host name."); |
error("No forward host name."); |
return success; |
return 0; |
} |
} |
if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { |
if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { |
error("Forward host name too long."); |
error("Forward host name too long."); |
return success; |
return 0; |
} |
} |
|
|
/* |
/* |
|
* Determine whether or not a port forward listens to loopback, |
|
* specified address or wildcard. On the client, a specified bind |
|
* address will always override gateway_ports. On the server, a |
|
* gateway_ports of 1 (``yes'') will override the client's |
|
* specification and force a wildcard bind, whereas a value of 2 |
|
* (``clientspecified'') will bind to whatever address the client |
|
* asked for. |
|
* |
|
* Special-case listen_addrs are: |
|
* |
|
* "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR |
|
* "" (empty string), "*" -> wildcard v4/v6 |
|
* "localhost" -> loopback v4/v6 |
|
*/ |
|
addr = NULL; |
|
if (listen_addr == NULL) { |
|
/* No address specified: default to gateway_ports setting */ |
|
if (gateway_ports) |
|
wildcard = 1; |
|
} else if (gateway_ports || is_client) { |
|
if (((datafellows & SSH_OLD_FORWARD_ADDR) && |
|
strcmp(listen_addr, "0.0.0.0") == 0) || |
|
*listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || |
|
(!is_client && gateway_ports == 1)) |
|
wildcard = 1; |
|
else if (strcmp(listen_addr, "localhost") != 0) |
|
addr = listen_addr; |
|
} |
|
|
|
debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", |
|
type, wildcard, (addr == NULL) ? "NULL" : addr); |
|
|
|
/* |
* getaddrinfo returns a loopback address if the hostname is |
* getaddrinfo returns a loopback address if the hostname is |
* set to NULL and hints.ai_flags is not AI_PASSIVE |
* set to NULL and hints.ai_flags is not AI_PASSIVE |
*/ |
*/ |
memset(&hints, 0, sizeof(hints)); |
memset(&hints, 0, sizeof(hints)); |
hints.ai_family = IPv4or6; |
hints.ai_family = IPv4or6; |
hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; |
hints.ai_flags = wildcard ? AI_PASSIVE : 0; |
hints.ai_socktype = SOCK_STREAM; |
hints.ai_socktype = SOCK_STREAM; |
snprintf(strport, sizeof strport, "%d", listen_port); |
snprintf(strport, sizeof strport, "%d", listen_port); |
if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) |
if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { |
packet_disconnect("getaddrinfo: fatal error"); |
if (addr == NULL) { |
|
/* This really shouldn't happen */ |
|
packet_disconnect("getaddrinfo: fatal error: %s", |
|
gai_strerror(r)); |
|
} else { |
|
error("channel_setup_fwd_listener: " |
|
"getaddrinfo(%.64s): %s", addr, gai_strerror(r)); |
|
} |
|
return 0; |
|
} |
|
|
for (ai = aitop; ai; ai = ai->ai_next) { |
for (ai = aitop; ai; ai = ai->ai_next) { |
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) |
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) |
|
|
u_int i; |
u_int i; |
int found = 0; |
int found = 0; |
|
|
for(i = 0; i < channels_alloc; i++) { |
for (i = 0; i < channels_alloc; i++) { |
Channel *c = channels[i]; |
Channel *c = channels[i]; |
|
|
if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && |
if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && |
|
|
|
|
/* protocol local port fwd, used by ssh (and sshd in v1) */ |
/* protocol local port fwd, used by ssh (and sshd in v1) */ |
int |
int |
channel_setup_local_fwd_listener(u_short listen_port, |
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) |
{ |
{ |
return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, |
return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, |
NULL, listen_port, host_to_connect, port_to_connect, gateway_ports); |
listen_host, listen_port, host_to_connect, port_to_connect, |
|
gateway_ports); |
} |
} |
|
|
/* protocol v2 remote port fwd, used by sshd */ |
/* protocol v2 remote port fwd, used by sshd */ |
|
|
*/ |
*/ |
|
|
void |
void |
channel_request_remote_forwarding(u_short listen_port, |
channel_request_remote_forwarding(const char *listen_host, u_short listen_port, |
const char *host_to_connect, u_short port_to_connect) |
const char *host_to_connect, u_short port_to_connect) |
{ |
{ |
int type, success = 0; |
int type, success = 0; |
|
|
|
|
/* Send the forward request to the remote side. */ |
/* Send the forward request to the remote side. */ |
if (compat20) { |
if (compat20) { |
const char *address_to_bind = "0.0.0.0"; |
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 |
|
address_to_bind = listen_host; |
|
|
packet_start(SSH2_MSG_GLOBAL_REQUEST); |
packet_start(SSH2_MSG_GLOBAL_REQUEST); |
packet_put_cstring("tcpip-forward"); |
packet_put_cstring("tcpip-forward"); |
packet_put_char(1); /* boolean: want reply */ |
packet_put_char(1); /* boolean: want reply */ |
|
|
* local side. |
* local side. |
*/ |
*/ |
void |
void |
channel_request_rforward_cancel(u_short port) |
channel_request_rforward_cancel(const char *host, u_short port) |
{ |
{ |
int i; |
int i; |
const char *address_to_bind = "0.0.0.0"; |
|
|
|
if (!compat20) |
if (!compat20) |
return; |
return; |
|
|
packet_start(SSH2_MSG_GLOBAL_REQUEST); |
packet_start(SSH2_MSG_GLOBAL_REQUEST); |
packet_put_cstring("cancel-tcpip-forward"); |
packet_put_cstring("cancel-tcpip-forward"); |
packet_put_char(0); |
packet_put_char(0); |
packet_put_cstring(address_to_bind); |
packet_put_cstring(host == NULL ? "" : host); |
packet_put_int(port); |
packet_put_int(port); |
packet_send(); |
packet_send(); |
|
|
|
|
packet_disconnect("Dynamic forwarding denied."); |
packet_disconnect("Dynamic forwarding denied."); |
|
|
/* Initiate forwarding */ |
/* Initiate forwarding */ |
channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports); |
channel_setup_local_fwd_listener(NULL, port, hostname, |
|
host_port, gateway_ports); |
|
|
/* Free the argument string. */ |
/* Free the argument string. */ |
xfree(hostname); |
xfree(hostname); |
|
|
struct winsize ws; |
struct winsize ws; |
|
|
for (i = 0; i < channels_alloc; i++) { |
for (i = 0; i < channels_alloc; i++) { |
if (channels[i] == NULL || |
if (channels[i] == NULL || !channels[i]->client_tty || |
channels[i]->type != SSH_CHANNEL_OPEN) |
channels[i]->type != SSH_CHANNEL_OPEN) |
continue; |
continue; |
if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) |
if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) |
|
|
*/ |
*/ |
int |
int |
x11_create_display_inet(int x11_display_offset, int x11_use_localhost, |
x11_create_display_inet(int x11_display_offset, int x11_use_localhost, |
int single_connection, u_int *display_numberp) |
int single_connection, u_int *display_numberp, int **chanids) |
{ |
{ |
Channel *nc = NULL; |
Channel *nc = NULL; |
int display_number, sock; |
int display_number, sock; |
|
|
} |
} |
|
|
/* Allocate a channel for each socket. */ |
/* Allocate a channel for each socket. */ |
|
if (chanids != NULL) |
|
*chanids = xmalloc(sizeof(**chanids) * (num_socks + 1)); |
for (n = 0; n < num_socks; n++) { |
for (n = 0; n < num_socks; n++) { |
sock = socks[n]; |
sock = socks[n]; |
nc = channel_new("x11 listener", |
nc = channel_new("x11 listener", |
|
|
CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, |
CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, |
0, "X11 inet listener", 1); |
0, "X11 inet listener", 1); |
nc->single_connection = single_connection; |
nc->single_connection = single_connection; |
|
if (*chanids != NULL) |
|
(*chanids)[n] = nc->self; |
} |
} |
|
if (*chanids != NULL) |
|
(*chanids)[n] = -1; |
|
|
/* Return the display number for the DISPLAY environment variable. */ |
/* Return the display number for the DISPLAY environment variable. */ |
*display_numberp = display_number; |
*display_numberp = display_number; |
|
|
* This should be called in the client only. |
* This should be called in the client only. |
*/ |
*/ |
void |
void |
x11_request_forwarding_with_spoofing(int client_session_id, |
x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, |
const char *proto, const char *data) |
const char *proto, const char *data) |
{ |
{ |
u_int data_len = (u_int) strlen(data) / 2; |
u_int data_len = (u_int) strlen(data) / 2; |
u_int i, value, len; |
u_int i, value; |
char *new_data; |
char *new_data; |
int screen_number; |
int screen_number; |
const char *cp; |
const char *cp; |
u_int32_t rnd = 0; |
u_int32_t rnd = 0; |
|
|
cp = getenv("DISPLAY"); |
if (x11_saved_display == NULL) |
|
x11_saved_display = xstrdup(disp); |
|
else if (strcmp(disp, x11_saved_display) != 0) { |
|
error("x11_request_forwarding_with_spoofing: different " |
|
"$DISPLAY already forwarded"); |
|
return; |
|
} |
|
|
|
cp = disp; |
|
if (disp) |
|
cp = strchr(disp, ':'); |
if (cp) |
if (cp) |
cp = strchr(cp, ':'); |
|
if (cp) |
|
cp = strchr(cp, '.'); |
cp = strchr(cp, '.'); |
if (cp) |
if (cp) |
screen_number = atoi(cp + 1); |
screen_number = atoi(cp + 1); |
else |
else |
screen_number = 0; |
screen_number = 0; |
|
|
/* Save protocol name. */ |
if (x11_saved_proto == NULL) { |
x11_saved_proto = xstrdup(proto); |
/* Save protocol name. */ |
|
x11_saved_proto = xstrdup(proto); |
/* |
/* |
* Extract real authentication data and generate fake data of the |
* Extract real authentication data and generate fake data |
* same length. |
* of the same length. |
*/ |
*/ |
x11_saved_data = xmalloc(data_len); |
x11_saved_data = xmalloc(data_len); |
x11_fake_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 authentication data: %.100s", data); |
fatal("x11_request_forwarding: bad " |
if (i % 4 == 0) |
"authentication data: %.100s", data); |
rnd = arc4random(); |
if (i % 4 == 0) |
x11_saved_data[i] = value; |
rnd = arc4random(); |
x11_fake_data[i] = rnd & 0xff; |
x11_saved_data[i] = value; |
rnd >>= 8; |
x11_fake_data[i] = rnd & 0xff; |
|
rnd >>= 8; |
|
} |
|
x11_saved_data_len = data_len; |
|
x11_fake_data_len = data_len; |
} |
} |
x11_saved_data_len = data_len; |
|
x11_fake_data_len = data_len; |
|
|
|
/* Convert the fake data into hex. */ |
/* Convert the fake data into hex. */ |
len = 2 * data_len + 1; |
new_data = tohex(x11_fake_data, data_len); |
new_data = xmalloc(len); |
|
for (i = 0; i < data_len; i++) |
|
snprintf(new_data + 2 * i, len - 2 * i, |
|
"%02x", (u_char) x11_fake_data[i]); |
|
|
|
/* Send the request packet. */ |
/* Send the request packet. */ |
if (compat20) { |
if (compat20) { |