version 1.223.2.1, 2006/02/03 03:01:55 |
version 1.223.2.2, 2006/10/06 03:19:32 |
|
|
|
/* $OpenBSD$ */ |
/* |
/* |
* 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 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
*/ |
|
|
#include "includes.h" |
#include <sys/types.h> |
RCSID("$OpenBSD$"); |
#include <sys/ioctl.h> |
|
#include <sys/un.h> |
|
#include <sys/socket.h> |
|
#include <sys/time.h> |
|
|
|
#include <netinet/in.h> |
|
#include <arpa/inet.h> |
|
|
|
#include <errno.h> |
|
#include <netdb.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <termios.h> |
|
#include <unistd.h> |
|
#include <stdarg.h> |
|
|
|
#include "xmalloc.h" |
#include "ssh.h" |
#include "ssh.h" |
#include "ssh1.h" |
#include "ssh1.h" |
#include "ssh2.h" |
#include "ssh2.h" |
#include "packet.h" |
#include "packet.h" |
#include "xmalloc.h" |
|
#include "log.h" |
#include "log.h" |
#include "misc.h" |
#include "misc.h" |
|
#include "buffer.h" |
#include "channels.h" |
#include "channels.h" |
#include "compat.h" |
#include "compat.h" |
#include "canohost.h" |
#include "canohost.h" |
#include "key.h" |
#include "key.h" |
#include "authfd.h" |
#include "authfd.h" |
#include "pathnames.h" |
#include "pathnames.h" |
#include "bufaux.h" |
|
|
|
/* -- channel core */ |
/* -- channel core */ |
|
|
|
|
u_short listen_port; /* Remote side should listen port number. */ |
u_short listen_port; /* Remote side should listen port number. */ |
} ForwardPermission; |
} ForwardPermission; |
|
|
/* List of all permitted host/port pairs to connect. */ |
/* List of all permitted host/port pairs to connect by the user. */ |
static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; |
static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; |
|
|
/* Number of permitted host/port pairs in the array. */ |
/* List of all permitted host/port pairs to connect by the admin. */ |
|
static ForwardPermission permitted_adm_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; |
|
|
|
/* Number of permitted host/port pairs in the array permitted by the user. */ |
static int num_permitted_opens = 0; |
static int num_permitted_opens = 0; |
|
|
|
/* Number of permitted host/port pair in the array permitted by the admin. */ |
|
static int num_adm_permitted_opens = 0; |
|
|
/* |
/* |
* If this is true, all opens are permitted. This is the case on the server |
* 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 |
|
|
* Fake X11 authentication data. This is what the server will be sending us; |
* Fake X11 authentication data. This is what the server will be sending us; |
* we should replace any occurrences of this by the real data. |
* we should replace any occurrences of this by the real data. |
*/ |
*/ |
static char *x11_fake_data = NULL; |
static u_char *x11_fake_data = NULL; |
static u_int x11_fake_data_len; |
static u_int x11_fake_data_len; |
|
|
|
|
|
|
if ((c = channel_by_id(id)) == NULL) |
if ((c = channel_by_id(id)) == NULL) |
return (NULL); |
return (NULL); |
|
|
switch(c->type) { |
switch (c->type) { |
case SSH_CHANNEL_X11_OPEN: |
case SSH_CHANNEL_X11_OPEN: |
case SSH_CHANNEL_LARVAL: |
case SSH_CHANNEL_LARVAL: |
case SSH_CHANNEL_CONNECTING: |
case SSH_CHANNEL_CONNECTING: |
|
|
case SSH_CHANNEL_INPUT_DRAINING: |
case SSH_CHANNEL_INPUT_DRAINING: |
case SSH_CHANNEL_OUTPUT_DRAINING: |
case SSH_CHANNEL_OUTPUT_DRAINING: |
return (c); |
return (c); |
break; |
|
} |
} |
logit("Non-public channel %d, type %d.", id, c->type); |
logit("Non-public channel %d, type %d.", id, c->type); |
return (NULL); |
return (NULL); |
|
|
* Register filedescriptors for a channel, used when allocating a channel or |
* Register filedescriptors for a channel, used when allocating a channel or |
* when the channel consumer/producer is ready, e.g. shell exec'd |
* when the channel consumer/producer is ready, e.g. shell exec'd |
*/ |
*/ |
|
|
static void |
static void |
channel_register_fds(Channel *c, int rfd, int wfd, int efd, |
channel_register_fds(Channel *c, int rfd, int wfd, int efd, |
int extusage, int nonblock) |
int extusage, int nonblock) |
|
|
* Allocate a new channel object and set its type and socket. This will cause |
* Allocate a new channel object and set its type and socket. This will cause |
* remote_name to be freed. |
* remote_name to be freed. |
*/ |
*/ |
|
|
Channel * |
Channel * |
channel_new(char *ctype, int type, int rfd, int wfd, int efd, |
channel_new(char *ctype, int type, int rfd, int wfd, int efd, |
u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) |
u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) |
|
|
/* Do initial allocation if this is the first call. */ |
/* Do initial allocation if this is the first call. */ |
if (channels_alloc == 0) { |
if (channels_alloc == 0) { |
channels_alloc = 10; |
channels_alloc = 10; |
channels = xmalloc(channels_alloc * sizeof(Channel *)); |
channels = xcalloc(channels_alloc, sizeof(Channel *)); |
for (i = 0; i < channels_alloc; i++) |
for (i = 0; i < channels_alloc; i++) |
channels[i] = NULL; |
channels[i] = NULL; |
} |
} |
|
|
if (channels_alloc > 10000) |
if (channels_alloc > 10000) |
fatal("channel_new: internal error: channels_alloc %d " |
fatal("channel_new: internal error: channels_alloc %d " |
"too big.", channels_alloc); |
"too big.", channels_alloc); |
channels = xrealloc(channels, |
channels = xrealloc(channels, channels_alloc + 10, |
(channels_alloc + 10) * sizeof(Channel *)); |
sizeof(Channel *)); |
channels_alloc += 10; |
channels_alloc += 10; |
debug2("channel: expanding %d", channels_alloc); |
debug2("channel: expanding %d", channels_alloc); |
for (i = found; i < channels_alloc; i++) |
for (i = found; i < channels_alloc; i++) |
channels[i] = NULL; |
channels[i] = NULL; |
} |
} |
/* Initialize and return new channel. */ |
/* Initialize and return new channel. */ |
c = channels[found] = xmalloc(sizeof(Channel)); |
c = channels[found] = xcalloc(1, sizeof(Channel)); |
memset(c, 0, sizeof(Channel)); |
|
buffer_init(&c->input); |
buffer_init(&c->input); |
buffer_init(&c->output); |
buffer_init(&c->output); |
buffer_init(&c->extended); |
buffer_init(&c->extended); |
|
|
} |
} |
|
|
/* Close all channel fd/socket. */ |
/* Close all channel fd/socket. */ |
|
|
static void |
static void |
channel_close_fds(Channel *c) |
channel_close_fds(Channel *c) |
{ |
{ |
|
|
} |
} |
|
|
/* Free the channel and close its fd/socket. */ |
/* Free the channel and close its fd/socket. */ |
|
|
void |
void |
channel_free(Channel *c) |
channel_free(Channel *c) |
{ |
{ |
|
|
* Closes the sockets/fds of all channels. This is used to close extra file |
* Closes the sockets/fds of all channels. This is used to close extra file |
* descriptors after a fork. |
* descriptors after a fork. |
*/ |
*/ |
|
|
void |
void |
channel_close_all(void) |
channel_close_all(void) |
{ |
{ |
|
|
/* |
/* |
* Stop listening to channels. |
* Stop listening to channels. |
*/ |
*/ |
|
|
void |
void |
channel_stop_listening(void) |
channel_stop_listening(void) |
{ |
{ |
|
|
* Returns true if no channel has too much buffered data, and false if one or |
* Returns true if no channel has too much buffered data, and false if one or |
* more channel is overfull. |
* more channel is overfull. |
*/ |
*/ |
|
|
int |
int |
channel_not_very_much_buffered_data(void) |
channel_not_very_much_buffered_data(void) |
{ |
{ |
|
|
} |
} |
|
|
/* Returns true if any channel is still open. */ |
/* Returns true if any channel is still open. */ |
|
|
int |
int |
channel_still_open(void) |
channel_still_open(void) |
{ |
{ |
|
|
} |
} |
|
|
/* Returns the id of an open channel suitable for keepaliving */ |
/* Returns the id of an open channel suitable for keepaliving */ |
|
|
int |
int |
channel_find_open(void) |
channel_find_open(void) |
{ |
{ |
|
|
* suitable for sending to the client. The message contains crlf pairs for |
* suitable for sending to the client. The message contains crlf pairs for |
* newlines. |
* newlines. |
*/ |
*/ |
|
|
char * |
char * |
channel_open_message(void) |
channel_open_message(void) |
{ |
{ |
|
|
packet_put_cstring(service); |
packet_put_cstring(service); |
packet_put_char(wantconfirm); |
packet_put_char(wantconfirm); |
} |
} |
|
|
void |
void |
channel_register_confirm(int id, channel_callback_fn *fn, void *ctx) |
channel_register_confirm(int id, channel_callback_fn *fn, void *ctx) |
{ |
{ |
|
|
c->confirm = fn; |
c->confirm = fn; |
c->confirm_ctx = ctx; |
c->confirm_ctx = ctx; |
} |
} |
|
|
void |
void |
channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) |
channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) |
{ |
{ |
|
|
c->detach_user = fn; |
c->detach_user = fn; |
c->detach_close = do_close; |
c->detach_close = do_close; |
} |
} |
|
|
void |
void |
channel_cancel_cleanup(int id) |
channel_cancel_cleanup(int id) |
{ |
{ |
|
|
c->detach_user = NULL; |
c->detach_user = NULL; |
c->detach_close = 0; |
c->detach_close = 0; |
} |
} |
|
|
void |
void |
channel_register_filter(int id, channel_infilter_fn *ifn, |
channel_register_filter(int id, channel_infilter_fn *ifn, |
channel_outfilter_fn *ofn) |
channel_outfilter_fn *ofn) |
|
|
* 'channel_post*': perform any appropriate operations for channels which |
* 'channel_post*': perform any appropriate operations for channels which |
* have events pending. |
* have events pending. |
*/ |
*/ |
typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); |
typedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset); |
chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; |
chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; |
chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; |
chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; |
|
|
|
/* ARGSUSED */ |
static void |
static void |
channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) |
channel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
FD_SET(c->sock, readset); |
FD_SET(c->sock, readset); |
} |
} |
|
|
|
/* ARGSUSED */ |
static void |
static void |
channel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset) |
channel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
debug3("channel %d: waiting for connection", c->self); |
debug3("channel %d: waiting for connection", c->self); |
FD_SET(c->sock, writeset); |
FD_SET(c->sock, writeset); |
} |
} |
|
|
static void |
static void |
channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) |
channel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
if (buffer_len(&c->input) < packet_get_maxsize()) |
if (buffer_len(&c->input) < packet_get_maxsize()) |
FD_SET(c->sock, readset); |
FD_SET(c->sock, readset); |
|
|
} |
} |
|
|
static void |
static void |
channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset) |
channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); |
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 && |
|
buffer_check_alloc(&c->input, CHAN_RBUF)) |
FD_SET(c->rfd, readset); |
FD_SET(c->rfd, readset); |
if (c->ostate == CHAN_OUTPUT_OPEN || |
if (c->ostate == CHAN_OUTPUT_OPEN || |
c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
|
|
FD_SET(c->ctl_fd, readset); |
FD_SET(c->ctl_fd, readset); |
} |
} |
|
|
|
/* ARGSUSED */ |
static void |
static void |
channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) |
channel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
if (buffer_len(&c->input) == 0) { |
if (buffer_len(&c->input) == 0) { |
packet_start(SSH_MSG_CHANNEL_CLOSE); |
packet_start(SSH_MSG_CHANNEL_CLOSE); |
|
|
} |
} |
} |
} |
|
|
|
/* ARGSUSED */ |
static void |
static void |
channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) |
channel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
if (buffer_len(&c->output) == 0) |
if (buffer_len(&c->output) == 0) |
chan_mark_dead(c); |
chan_mark_dead(c); |
|
|
} |
} |
|
|
static void |
static void |
channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) |
channel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
int ret = x11_open_helper(&c->output); |
int ret = x11_open_helper(&c->output); |
|
|
|
|
} |
} |
|
|
static void |
static void |
channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) |
channel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
int ret = x11_open_helper(&c->output); |
int ret = x11_open_helper(&c->output); |
|
|
|
|
} |
} |
|
|
/* try to decode a socks4 header */ |
/* try to decode a socks4 header */ |
|
/* ARGSUSED */ |
static int |
static int |
channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) |
channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
char *p, *host; |
char *p, *host; |
u_int len, have, i, found; |
u_int len, have, i, found; |
|
|
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 */ |
buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp)); |
buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); |
return 1; |
return 1; |
} |
} |
|
|
|
|
#define SSH_SOCKS5_CONNECT 0x01 |
#define SSH_SOCKS5_CONNECT 0x01 |
#define SSH_SOCKS5_SUCCESS 0x00 |
#define SSH_SOCKS5_SUCCESS 0x00 |
|
|
|
/* ARGSUSED */ |
static int |
static int |
channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) |
channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
struct { |
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]; |
u_int have, i, found, nmethods, addrlen, af; |
u_int have, need, 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); |
|
|
return 0; |
return 0; |
/* look for method: "NO AUTHENTICATION REQUIRED" */ |
/* look for method: "NO AUTHENTICATION REQUIRED" */ |
for (found = 0, i = 2 ; i < nmethods + 2; i++) { |
for (found = 0, i = 2 ; i < nmethods + 2; i++) { |
if (p[i] == SSH_SOCKS5_NOAUTH ) { |
if (p[i] == SSH_SOCKS5_NOAUTH) { |
found = 1; |
found = 1; |
break; |
break; |
} |
} |
|
|
debug2("channel %d: socks5 post auth", c->self); |
debug2("channel %d: socks5 post auth", c->self); |
if (have < sizeof(s5_req)+1) |
if (have < sizeof(s5_req)+1) |
return 0; /* need more */ |
return 0; /* need more */ |
memcpy((char *)&s5_req, p, sizeof(s5_req)); |
memcpy(&s5_req, p, sizeof(s5_req)); |
if (s5_req.version != 0x05 || |
if (s5_req.version != 0x05 || |
s5_req.command != SSH_SOCKS5_CONNECT || |
s5_req.command != SSH_SOCKS5_CONNECT || |
s5_req.reserved != 0x00) { |
s5_req.reserved != 0x00) { |
|
|
debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); |
debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); |
return -1; |
return -1; |
} |
} |
if (have < 4 + addrlen + 2) |
need = sizeof(s5_req) + addrlen + 2; |
|
if (s5_req.atyp == SSH_SOCKS5_DOMAIN) |
|
need++; |
|
if (have < need) |
return 0; |
return 0; |
buffer_consume(&c->input, sizeof(s5_req)); |
buffer_consume(&c->input, sizeof(s5_req)); |
if (s5_req.atyp == SSH_SOCKS5_DOMAIN) |
if (s5_req.atyp == SSH_SOCKS5_DOMAIN) |
|
|
((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; |
((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; |
dest_port = 0; /* ignored */ |
dest_port = 0; /* ignored */ |
|
|
buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp)); |
buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); |
buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr)); |
buffer_append(&c->output, &dest_addr, sizeof(struct in_addr)); |
buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port)); |
buffer_append(&c->output, &dest_port, sizeof(dest_port)); |
return 1; |
return 1; |
} |
} |
|
|
/* dynamic port forwarding */ |
/* dynamic port forwarding */ |
static void |
static void |
channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) |
channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
u_char *p; |
u_char *p; |
u_int have; |
u_int have; |
|
|
} |
} |
|
|
/* This is our fake X11 server socket. */ |
/* This is our fake X11 server socket. */ |
|
/* ARGSUSED */ |
static void |
static void |
channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) |
channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
Channel *nc; |
Channel *nc; |
struct sockaddr addr; |
struct sockaddr addr; |
|
|
/* |
/* |
* This socket is listening for connections to a forwarded TCP/IP port. |
* This socket is listening for connections to a forwarded TCP/IP port. |
*/ |
*/ |
|
/* ARGSUSED */ |
static void |
static void |
channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) |
channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
Channel *nc; |
Channel *nc; |
struct sockaddr addr; |
struct sockaddr addr; |
|
|
* This is the authentication agent socket listening for connections from |
* This is the authentication agent socket listening for connections from |
* clients. |
* clients. |
*/ |
*/ |
|
/* ARGSUSED */ |
static void |
static void |
channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) |
channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
Channel *nc; |
Channel *nc; |
int newsock; |
int newsock; |
|
|
} |
} |
} |
} |
|
|
|
/* ARGSUSED */ |
static void |
static void |
channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset) |
channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
int err = 0; |
int err = 0; |
socklen_t sz = sizeof(err); |
socklen_t sz = sizeof(err); |
|
|
} |
} |
} |
} |
|
|
|
/* ARGSUSED */ |
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[CHAN_RBUF]; |
char buf[CHAN_RBUF]; |
int len; |
int len; |
|
|
} |
} |
return 1; |
return 1; |
} |
} |
|
|
|
/* ARGSUSED */ |
static int |
static int |
channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) |
channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
struct termios tio; |
struct termios tio; |
u_char *data = NULL, *buf; |
u_char *data = NULL, *buf; |
|
|
} |
} |
return 1; |
return 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[CHAN_RBUF]; |
char buf[CHAN_RBUF]; |
int len; |
int len; |
|
|
} |
} |
return 1; |
return 1; |
} |
} |
|
|
|
/* ARGSUSED */ |
static int |
static int |
channel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset) |
channel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
char buf[16]; |
char buf[16]; |
int len; |
int len; |
|
|
} |
} |
return 1; |
return 1; |
} |
} |
|
|
static int |
static int |
channel_check_window(Channel *c) |
channel_check_window(Channel *c) |
{ |
{ |
|
|
} |
} |
|
|
static void |
static void |
channel_post_open(Channel *c, fd_set * readset, fd_set * writeset) |
channel_post_open(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
if (c->delayed) |
if (c->delayed) |
return; |
return; |
|
|
channel_check_window(c); |
channel_check_window(c); |
} |
} |
|
|
|
/* ARGSUSED */ |
static void |
static void |
channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) |
channel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
int len; |
int len; |
|
|
|
|
} |
} |
|
|
static void |
static void |
channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) |
channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset) |
{ |
{ |
static int did_init = 0; |
static int did_init = 0; |
u_int i; |
u_int i; |
|
|
channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, |
channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, |
u_int *nallocp, int rekeying) |
u_int *nallocp, int rekeying) |
{ |
{ |
u_int n, sz; |
u_int n, sz, nfdset; |
|
|
n = MAX(*maxfdp, channel_max_fd); |
n = MAX(*maxfdp, channel_max_fd); |
|
|
sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); |
nfdset = howmany(n+1, NFDBITS); |
|
/* Explicitly test here, because xrealloc isn't always called */ |
|
if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask)) |
|
fatal("channel_prepare_select: max_fd (%d) is too large", n); |
|
sz = nfdset * sizeof(fd_mask); |
|
|
/* perhaps check sz < nalloc/2 and shrink? */ |
/* perhaps check sz < nalloc/2 and shrink? */ |
if (*readsetp == NULL || sz > *nallocp) { |
if (*readsetp == NULL || sz > *nallocp) { |
*readsetp = xrealloc(*readsetp, sz); |
*readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask)); |
*writesetp = xrealloc(*writesetp, sz); |
*writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask)); |
*nallocp = sz; |
*nallocp = sz; |
} |
} |
*maxfdp = n; |
*maxfdp = n; |
|
|
* events pending. |
* events pending. |
*/ |
*/ |
void |
void |
channel_after_select(fd_set * readset, fd_set * writeset) |
channel_after_select(fd_set *readset, fd_set *writeset) |
{ |
{ |
channel_handler(channel_post, readset, writeset); |
channel_handler(channel_post, readset, writeset); |
} |
} |
|
|
|
|
/* If there is data to send to the connection, enqueue some of it now. */ |
/* If there is data to send to the connection, enqueue some of it now. */ |
|
|
void |
void |
channel_output_poll(void) |
channel_output_poll(void) |
{ |
{ |
|
|
|
|
/* -- protocol input */ |
/* -- protocol input */ |
|
|
|
/* ARGSUSED */ |
void |
void |
channel_input_data(int type, u_int32_t seq, void *ctxt) |
channel_input_data(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
xfree(data); |
xfree(data); |
} |
} |
|
|
|
/* ARGSUSED */ |
void |
void |
channel_input_extended_data(int type, u_int32_t seq, void *ctxt) |
channel_input_extended_data(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
xfree(data); |
xfree(data); |
} |
} |
|
|
|
/* ARGSUSED */ |
void |
void |
channel_input_ieof(int type, u_int32_t seq, void *ctxt) |
channel_input_ieof(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
|
|
} |
} |
|
|
|
/* ARGSUSED */ |
void |
void |
channel_input_close(int type, u_int32_t seq, void *ctxt) |
channel_input_close(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
} |
} |
|
|
/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ |
/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ |
|
/* ARGSUSED */ |
void |
void |
channel_input_oclose(int type, u_int32_t seq, void *ctxt) |
channel_input_oclose(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
chan_rcvd_oclose(c); |
chan_rcvd_oclose(c); |
} |
} |
|
|
|
/* ARGSUSED */ |
void |
void |
channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) |
channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
channel_free(c); |
channel_free(c); |
} |
} |
|
|
|
/* ARGSUSED */ |
void |
void |
channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) |
channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
return "unknown reason"; |
return "unknown reason"; |
} |
} |
|
|
|
/* ARGSUSED */ |
void |
void |
channel_input_open_failure(int type, u_int32_t seq, void *ctxt) |
channel_input_open_failure(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
channel_free(c); |
channel_free(c); |
} |
} |
|
|
|
/* ARGSUSED */ |
void |
void |
channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) |
channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
c->remote_window += adjust; |
c->remote_window += adjust; |
} |
} |
|
|
|
/* ARGSUSED */ |
void |
void |
channel_input_port_open(int type, u_int32_t seq, void *ctxt) |
channel_input_port_open(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
* the secure channel to host:port from local side. |
* the secure channel to host:port from local side. |
*/ |
*/ |
|
|
void |
int |
channel_request_remote_forwarding(const char *listen_host, 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) |
{ |
{ |
|
|
success = 1; |
success = 1; |
break; |
break; |
case SSH_SMSG_FAILURE: |
case SSH_SMSG_FAILURE: |
logit("Warning: Server denied remote port forwarding."); |
|
break; |
break; |
default: |
default: |
/* Unknown packet */ |
/* Unknown packet */ |
|
|
permitted_opens[num_permitted_opens].listen_port = listen_port; |
permitted_opens[num_permitted_opens].listen_port = listen_port; |
num_permitted_opens++; |
num_permitted_opens++; |
} |
} |
|
return (success ? 0 : -1); |
} |
} |
|
|
/* |
/* |
|
|
/* |
/* |
* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates |
* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates |
* listening for the port, and sends back a success reply (or disconnect |
* listening for the port, and sends back a success reply (or disconnect |
* message if there was an error). This never returns if there was an error. |
* message if there was an error). |
*/ |
*/ |
|
int |
void |
|
channel_input_port_forward_request(int is_root, int gateway_ports) |
channel_input_port_forward_request(int is_root, int gateway_ports) |
{ |
{ |
u_short port, host_port; |
u_short port, host_port; |
|
int success = 0; |
char *hostname; |
char *hostname; |
|
|
/* Get arguments from the packet. */ |
/* Get arguments from the packet. */ |
|
|
packet_disconnect("Dynamic forwarding denied."); |
packet_disconnect("Dynamic forwarding denied."); |
|
|
/* Initiate forwarding */ |
/* Initiate forwarding */ |
channel_setup_local_fwd_listener(NULL, port, hostname, |
success = channel_setup_local_fwd_listener(NULL, port, hostname, |
host_port, gateway_ports); |
host_port, gateway_ports); |
|
|
/* Free the argument string. */ |
/* Free the argument string. */ |
xfree(hostname); |
xfree(hostname); |
|
|
|
return (success ? 0 : -1); |
} |
} |
|
|
/* |
/* |
|
|
channel_add_permitted_opens(char *host, int port) |
channel_add_permitted_opens(char *host, int port) |
{ |
{ |
if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) |
if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) |
fatal("channel_request_remote_forwarding: too many forwards"); |
fatal("channel_add_permitted_opens: too many forwards"); |
debug("allow port forwarding to host %s port %d", host, port); |
debug("allow port forwarding to host %s port %d", host, port); |
|
|
permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); |
permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); |
|
|
all_opens_permitted = 0; |
all_opens_permitted = 0; |
} |
} |
|
|
|
int |
|
channel_add_adm_permitted_opens(char *host, int port) |
|
{ |
|
if (num_adm_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) |
|
fatal("channel_add_adm_permitted_opens: too many forwards"); |
|
debug("config allows port forwarding to host %s port %d", host, port); |
|
|
|
permitted_adm_opens[num_adm_permitted_opens].host_to_connect |
|
= xstrdup(host); |
|
permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; |
|
return ++num_adm_permitted_opens; |
|
} |
|
|
void |
void |
channel_clear_permitted_opens(void) |
channel_clear_permitted_opens(void) |
{ |
{ |
|
|
if (permitted_opens[i].host_to_connect != NULL) |
if (permitted_opens[i].host_to_connect != NULL) |
xfree(permitted_opens[i].host_to_connect); |
xfree(permitted_opens[i].host_to_connect); |
num_permitted_opens = 0; |
num_permitted_opens = 0; |
|
|
} |
} |
|
|
|
void |
|
channel_clear_adm_permitted_opens(void) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < num_adm_permitted_opens; i++) |
|
if (permitted_adm_opens[i].host_to_connect != NULL) |
|
xfree(permitted_adm_opens[i].host_to_connect); |
|
num_adm_permitted_opens = 0; |
|
} |
|
|
/* return socket to remote host, port */ |
/* return socket to remote host, port */ |
static int |
static int |
connect_to(const char *host, u_short port) |
connect_to(const char *host, u_short port) |
|
|
int |
int |
channel_connect_to(const char *host, u_short port) |
channel_connect_to(const char *host, u_short port) |
{ |
{ |
int i, permit; |
int i, permit, permit_adm = 1; |
|
|
permit = all_opens_permitted; |
permit = all_opens_permitted; |
if (!permit) { |
if (!permit) { |
|
|
permitted_opens[i].port_to_connect == port && |
permitted_opens[i].port_to_connect == port && |
strcmp(permitted_opens[i].host_to_connect, host) == 0) |
strcmp(permitted_opens[i].host_to_connect, host) == 0) |
permit = 1; |
permit = 1; |
|
} |
|
|
|
if (num_adm_permitted_opens > 0) { |
|
permit_adm = 0; |
|
for (i = 0; i < num_adm_permitted_opens; i++) |
|
if (permitted_adm_opens[i].host_to_connect != NULL && |
|
permitted_adm_opens[i].port_to_connect == port && |
|
strcmp(permitted_adm_opens[i].host_to_connect, host) |
|
== 0) |
|
permit_adm = 1; |
} |
} |
if (!permit) { |
|
|
if (!permit || !permit_adm) { |
logit("Received request to connect to host %.100s port %d, " |
logit("Received request to connect to host %.100s port %d, " |
"but the request was denied.", host, port); |
"but the request was denied.", host, port); |
return -1; |
return -1; |
|
|
if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) |
if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) |
continue; |
continue; |
channel_request_start(i, "window-change", 0); |
channel_request_start(i, "window-change", 0); |
packet_put_int(ws.ws_col); |
packet_put_int((u_int)ws.ws_col); |
packet_put_int(ws.ws_row); |
packet_put_int((u_int)ws.ws_row); |
packet_put_int(ws.ws_xpixel); |
packet_put_int((u_int)ws.ws_xpixel); |
packet_put_int(ws.ws_ypixel); |
packet_put_int((u_int)ws.ws_ypixel); |
packet_send(); |
packet_send(); |
} |
} |
} |
} |
|
|
} |
} |
|
|
/* Allocate a channel for each socket. */ |
/* Allocate a channel for each socket. */ |
*chanids = xmalloc(sizeof(**chanids) * (num_socks + 1)); |
*chanids = xcalloc(num_socks + 1, sizeof(**chanids)); |
for (n = 0; n < num_socks; n++) { |
for (n = 0; n < num_socks; n++) { |
sock = socks[n]; |
sock = socks[n]; |
nc = channel_new("x11 listener", |
nc = channel_new("x11 listener", |
|
|
memset(&addr, 0, sizeof(addr)); |
memset(&addr, 0, sizeof(addr)); |
addr.sun_family = AF_UNIX; |
addr.sun_family = AF_UNIX; |
snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr); |
snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr); |
if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) |
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) |
return sock; |
return sock; |
close(sock); |
close(sock); |
error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); |
error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); |
|
|
int |
int |
x11_connect_display(void) |
x11_connect_display(void) |
{ |
{ |
int display_number, sock = 0; |
u_int display_number; |
const char *display; |
const char *display; |
char buf[1024], *cp; |
char buf[1024], *cp; |
struct addrinfo hints, *ai, *aitop; |
struct addrinfo hints, *ai, *aitop; |
char strport[NI_MAXSERV]; |
char strport[NI_MAXSERV]; |
int gaierr; |
int gaierr, sock = 0; |
|
|
/* Try to open a socket for the local X server. */ |
/* Try to open a socket for the local X server. */ |
display = getenv("DISPLAY"); |
display = getenv("DISPLAY"); |
|
|
if (strncmp(display, "unix:", 5) == 0 || |
if (strncmp(display, "unix:", 5) == 0 || |
display[0] == ':') { |
display[0] == ':') { |
/* Connect to the unix domain socket. */ |
/* Connect to the unix domain socket. */ |
if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { |
if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { |
error("Could not parse display number from DISPLAY: %.100s", |
error("Could not parse display number from DISPLAY: %.100s", |
display); |
display); |
return -1; |
return -1; |
|
|
} |
} |
*cp = 0; |
*cp = 0; |
/* buf now contains the host name. But first we parse the display number. */ |
/* buf now contains the host name. But first we parse the display number. */ |
if (sscanf(cp + 1, "%d", &display_number) != 1) { |
if (sscanf(cp + 1, "%u", &display_number) != 1) { |
error("Could not parse display number from DISPLAY: %.100s", |
error("Could not parse display number from DISPLAY: %.100s", |
display); |
display); |
return -1; |
return -1; |
|
|
memset(&hints, 0, sizeof(hints)); |
memset(&hints, 0, sizeof(hints)); |
hints.ai_family = IPv4or6; |
hints.ai_family = IPv4or6; |
hints.ai_socktype = SOCK_STREAM; |
hints.ai_socktype = SOCK_STREAM; |
snprintf(strport, sizeof strport, "%d", 6000 + display_number); |
snprintf(strport, sizeof strport, "%u", 6000 + display_number); |
if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { |
if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { |
error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); |
error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); |
return -1; |
return -1; |
|
|
} |
} |
/* Connect it to the display. */ |
/* Connect it to the display. */ |
if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { |
if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { |
debug2("connect %.100s port %d: %.100s", buf, |
debug2("connect %.100s port %u: %.100s", buf, |
6000 + display_number, strerror(errno)); |
6000 + display_number, strerror(errno)); |
close(sock); |
close(sock); |
continue; |
continue; |
|
|
} |
} |
freeaddrinfo(aitop); |
freeaddrinfo(aitop); |
if (!ai) { |
if (!ai) { |
error("connect %.100s port %d: %.100s", buf, 6000 + display_number, |
error("connect %.100s port %u: %.100s", buf, 6000 + display_number, |
strerror(errno)); |
strerror(errno)); |
return -1; |
return -1; |
} |
} |
|
|
* with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. |
* with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. |
*/ |
*/ |
|
|
|
/* ARGSUSED */ |
void |
void |
x11_input_open(int type, u_int32_t seq, void *ctxt) |
x11_input_open(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
} |
} |
|
|
/* dummy protocol handler that denies SSH-1 requests (agent/x11) */ |
/* dummy protocol handler that denies SSH-1 requests (agent/x11) */ |
|
/* ARGSUSED */ |
void |
void |
deny_input_open(int type, u_int32_t seq, void *ctxt) |
deny_input_open(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
|
|
return; |
return; |
} |
} |
|
|
cp = disp; |
cp = strchr(disp, ':'); |
if (disp) |
|
cp = strchr(disp, ':'); |
|
if (cp) |
if (cp) |
cp = strchr(cp, '.'); |
cp = strchr(cp, '.'); |
if (cp) |
if (cp) |
screen_number = atoi(cp + 1); |
screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); |
else |
else |
screen_number = 0; |
screen_number = 0; |
|
|