version 1.172.2.5, 2003/04/03 22:35:17 |
version 1.173, 2002/04/22 21:04:52 |
|
|
#include "ssh2.h" |
#include "ssh2.h" |
#include "packet.h" |
#include "packet.h" |
#include "xmalloc.h" |
#include "xmalloc.h" |
|
#include "uidswap.h" |
#include "log.h" |
#include "log.h" |
#include "misc.h" |
#include "misc.h" |
#include "channels.h" |
#include "channels.h" |
|
|
|
|
#define NUM_SOCKS 10 |
#define NUM_SOCKS 10 |
|
|
|
/* Name and directory of socket for authentication agent forwarding. */ |
|
static char *auth_sock_name = NULL; |
|
static char *auth_sock_dir = NULL; |
|
|
/* AF_UNSPEC or AF_INET or AF_INET6 */ |
/* AF_UNSPEC or AF_INET or AF_INET6 */ |
static int IPv4or6 = AF_UNSPEC; |
static int IPv4or6 = AF_UNSPEC; |
|
|
|
|
|
|
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) |
int window, int maxpack, int extusage, char *remote_name, int nonblock) |
{ |
{ |
int i, found; |
int i, found; |
Channel *c; |
Channel *c; |
|
|
/* There are no free slots. Take last+1 slot and expand the array. */ |
/* There are no free slots. Take last+1 slot and expand the array. */ |
found = channels_alloc; |
found = channels_alloc; |
channels_alloc += 10; |
channels_alloc += 10; |
if (channels_alloc > 10000) |
|
fatal("channel_new: internal error: channels_alloc %d " |
|
"too big.", channels_alloc); |
|
debug2("channel: expanding %d", channels_alloc); |
debug2("channel: expanding %d", channels_alloc); |
channels = xrealloc(channels, channels_alloc * sizeof(Channel *)); |
channels = xrealloc(channels, channels_alloc * sizeof(Channel *)); |
for (i = found; i < channels_alloc; i++) |
for (i = found; i < channels_alloc; i++) |
|
|
#if 0 |
#if 0 |
if (!compat20 && |
if (!compat20 && |
buffer_len(&c->input) > packet_get_maxsize()) { |
buffer_len(&c->input) > packet_get_maxsize()) { |
debug2("channel %d: big input buffer %d", |
debug("channel %d: big input buffer %d", |
c->self, buffer_len(&c->input)); |
c->self, buffer_len(&c->input)); |
return 0; |
return 0; |
} |
} |
#endif |
#endif |
if (buffer_len(&c->output) > packet_get_maxsize()) { |
if (buffer_len(&c->output) > packet_get_maxsize()) { |
debug2("channel %d: big output buffer %d > %d", |
debug("channel %d: big output buffer %d > %d", |
c->self, buffer_len(&c->output), |
c->self, buffer_len(&c->output), |
packet_get_maxsize()); |
packet_get_maxsize()); |
return 0; |
return 0; |
|
|
channel_send_open(int id) |
channel_send_open(int id) |
{ |
{ |
Channel *c = channel_lookup(id); |
Channel *c = channel_lookup(id); |
|
|
if (c == NULL) { |
if (c == NULL) { |
log("channel_send_open: %d: bad id", id); |
log("channel_send_open: %d: bad id", id); |
return; |
return; |
} |
} |
debug2("channel %d: send open", id); |
debug("send channel open %d", id); |
packet_start(SSH2_MSG_CHANNEL_OPEN); |
packet_start(SSH2_MSG_CHANNEL_OPEN); |
packet_put_cstring(c->ctype); |
packet_put_cstring(c->ctype); |
packet_put_int(c->self); |
packet_put_int(c->self); |
|
|
} |
} |
|
|
void |
void |
channel_request_start(int id, char *service, int wantconfirm) |
channel_request_start(int local_id, char *service, int wantconfirm) |
{ |
{ |
Channel *c = channel_lookup(id); |
Channel *c = channel_lookup(local_id); |
|
|
if (c == NULL) { |
if (c == NULL) { |
log("channel_request_start: %d: unknown channel id", id); |
log("channel_request_start: %d: unknown channel id", local_id); |
return; |
return; |
} |
} |
debug("channel %d: request %s", id, service) ; |
debug("channel request %d: %s", local_id, service) ; |
packet_start(SSH2_MSG_CHANNEL_REQUEST); |
packet_start(SSH2_MSG_CHANNEL_REQUEST); |
packet_put_int(c->remote_id); |
packet_put_int(c->remote_id); |
packet_put_cstring(service); |
packet_put_cstring(service); |
|
|
channel_register_confirm(int id, channel_callback_fn *fn) |
channel_register_confirm(int id, channel_callback_fn *fn) |
{ |
{ |
Channel *c = channel_lookup(id); |
Channel *c = channel_lookup(id); |
|
|
if (c == NULL) { |
if (c == NULL) { |
log("channel_register_comfirm: %d: bad id", id); |
log("channel_register_comfirm: %d: bad id", id); |
return; |
return; |
|
|
channel_register_cleanup(int id, channel_callback_fn *fn) |
channel_register_cleanup(int id, channel_callback_fn *fn) |
{ |
{ |
Channel *c = channel_lookup(id); |
Channel *c = channel_lookup(id); |
|
|
if (c == NULL) { |
if (c == NULL) { |
log("channel_register_cleanup: %d: bad id", id); |
log("channel_register_cleanup: %d: bad id", id); |
return; |
return; |
|
|
channel_cancel_cleanup(int id) |
channel_cancel_cleanup(int id) |
{ |
{ |
Channel *c = channel_lookup(id); |
Channel *c = channel_lookup(id); |
|
|
if (c == NULL) { |
if (c == NULL) { |
log("channel_cancel_cleanup: %d: bad id", id); |
log("channel_cancel_cleanup: %d: bad id", id); |
return; |
return; |
|
|
channel_register_filter(int id, channel_filter_fn *fn) |
channel_register_filter(int id, channel_filter_fn *fn) |
{ |
{ |
Channel *c = channel_lookup(id); |
Channel *c = channel_lookup(id); |
|
|
if (c == NULL) { |
if (c == NULL) { |
log("channel_register_filter: %d: bad id", id); |
log("channel_register_filter: %d: bad id", id); |
return; |
return; |
|
|
int extusage, int nonblock, u_int window_max) |
int extusage, int nonblock, u_int window_max) |
{ |
{ |
Channel *c = channel_lookup(id); |
Channel *c = channel_lookup(id); |
|
|
if (c == NULL || c->type != SSH_CHANNEL_LARVAL) |
if (c == NULL || c->type != SSH_CHANNEL_LARVAL) |
fatal("channel_activate for non-larval channel %d.", id); |
fatal("channel_activate for non-larval channel %d.", id); |
channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); |
channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); |
|
|
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_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); |
|
|
if (ret == 1) { |
if (ret == 1) { |
/* Start normal processing for the channel. */ |
/* Start normal processing for the channel. */ |
c->type = SSH_CHANNEL_OPEN; |
c->type = SSH_CHANNEL_OPEN; |
|
|
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; |
u_char *p, *host; |
int len, have, i, found; |
int len, have, i, found; |
char username[256]; |
char username[256]; |
struct { |
struct { |
|
|
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; |
|
|
/* Send buffered output data to the socket. */ |
/* Send buffered output data to the socket. */ |
if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { |
if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { |
len = write(c->sock, buffer_ptr(&c->output), |
len = write(c->sock, buffer_ptr(&c->output), |
|
|
channel_handler_init(void) |
channel_handler_init(void) |
{ |
{ |
int i; |
int i; |
|
|
for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { |
for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { |
channel_pre[i] = NULL; |
channel_pre[i] = NULL; |
channel_post[i] = NULL; |
channel_post[i] = NULL; |
|
|
void |
void |
channel_output_poll(void) |
channel_output_poll(void) |
{ |
{ |
|
int len, i; |
Channel *c; |
Channel *c; |
int i; |
|
u_int len; |
|
|
|
for (i = 0; i < channels_alloc; i++) { |
for (i = 0; i < channels_alloc; i++) { |
c = channels[i]; |
c = channels[i]; |
|
|
* 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); |
} |
} |
|
|
c->remote_window > 0 && |
c->remote_window > 0 && |
(len = buffer_len(&c->extended)) > 0 && |
(len = buffer_len(&c->extended)) > 0 && |
c->extended_usage == CHAN_EXTENDED_READ) { |
c->extended_usage == CHAN_EXTENDED_READ) { |
debug2("channel %d: rwin %u elen %u euse %d", |
debug2("channel %d: rwin %d elen %d euse %d", |
c->self, c->remote_window, buffer_len(&c->extended), |
c->self, c->remote_window, buffer_len(&c->extended), |
c->extended_usage); |
c->extended_usage); |
if (len > c->remote_window) |
if (len > c->remote_window) |
|
|
channel_input_extended_data(int type, u_int32_t seq, void *ctxt) |
channel_input_extended_data(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
int id; |
int id; |
|
int tcode; |
char *data; |
char *data; |
u_int data_len, tcode; |
u_int data_len; |
Channel *c; |
Channel *c; |
|
|
/* Get the channel number and verify it. */ |
/* Get the channel number and verify it. */ |
|
|
c->confirm(c->self, NULL); |
c->confirm(c->self, NULL); |
debug2("callback done"); |
debug2("callback done"); |
} |
} |
debug("channel %d: open confirm rwindow %u rmax %u", c->self, |
debug("channel %d: open confirm rwindow %d rmax %d", c->self, |
c->remote_window, c->remote_maxpacket); |
c->remote_window, c->remote_maxpacket); |
} |
} |
packet_check_eom(); |
packet_check_eom(); |
|
|
channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) |
channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
Channel *c; |
Channel *c; |
int id; |
int id, adjust; |
u_int adjust; |
|
|
|
if (!compat20) |
if (!compat20) |
return; |
return; |
|
|
} |
} |
adjust = packet_get_int(); |
adjust = packet_get_int(); |
packet_check_eom(); |
packet_check_eom(); |
debug2("channel %d: rcvd adjust %u", id, adjust); |
debug2("channel %d: rcvd adjust %d", id, adjust); |
c->remote_window += adjust; |
c->remote_window += adjust; |
} |
} |
|
|
|
|
c->remote_id = remote_id; |
c->remote_id = remote_id; |
} |
} |
if (c == NULL) { |
if (c == NULL) { |
xfree(originator_string); |
|
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
packet_put_int(remote_id); |
packet_put_int(remote_id); |
packet_send(); |
packet_send(); |
|
|
struct addrinfo hints, *ai, *aitop; |
struct addrinfo hints, *ai, *aitop; |
const char *host; |
const char *host; |
char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
|
struct linger linger; |
|
|
success = 0; |
success = 0; |
host = (type == SSH_CHANNEL_RPORT_LISTENER) ? |
host = (type == SSH_CHANNEL_RPORT_LISTENER) ? |
|
|
continue; |
continue; |
} |
} |
/* |
/* |
* Set socket options. |
* Set socket options. We would like the socket to disappear |
* Allow local port reuse in TIME_WAIT. |
* as soon as it has been closed for whatever reason. |
*/ |
*/ |
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, |
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); |
sizeof(on)) == -1) |
linger.l_onoff = 1; |
error("setsockopt SO_REUSEADDR: %s", strerror(errno)); |
linger.l_linger = 5; |
|
setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); |
debug("Local forwarding listening on %s port %s.", ntop, strport); |
debug("Local forwarding listening on %s port %s.", ntop, strport); |
|
|
/* Bind the socket to the address. */ |
/* Bind the socket to the address. */ |
|
|
} |
} |
sock = socket(ai->ai_family, SOCK_STREAM, 0); |
sock = socket(ai->ai_family, SOCK_STREAM, 0); |
if (sock < 0) { |
if (sock < 0) { |
if (ai->ai_next == NULL) |
error("socket: %.100s", strerror(errno)); |
error("socket: %.100s", strerror(errno)); |
|
else |
|
verbose("socket: %.100s", strerror(errno)); |
|
continue; |
continue; |
} |
} |
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) |
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) |
|
|
|
|
/* |
/* |
* Creates an internet domain socket for listening for X11 connections. |
* Creates an internet domain socket for listening for X11 connections. |
* Returns 0 and a suitable display number for the DISPLAY variable |
* Returns a suitable display number for the DISPLAY variable, or -1 if |
* stored in display_numberp , or -1 if an error occurs. |
* an error occurs. |
*/ |
*/ |
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) |
{ |
{ |
Channel *nc = NULL; |
Channel *nc = NULL; |
int display_number, sock; |
int display_number, sock; |
|
|
if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { |
if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { |
debug("bind port %d: %.100s", port, strerror(errno)); |
debug("bind port %d: %.100s", port, strerror(errno)); |
close(sock); |
close(sock); |
|
|
if (ai->ai_next) |
|
continue; |
|
|
|
for (n = 0; n < num_socks; n++) { |
for (n = 0; n < num_socks; n++) { |
close(socks[n]); |
close(socks[n]); |
} |
} |
|
|
} |
} |
|
|
/* Return the display number for the DISPLAY environment variable. */ |
/* Return the display number for the DISPLAY environment variable. */ |
*display_numberp = display_number; |
return display_number; |
return (0); |
|
} |
} |
|
|
static int |
static int |
|
|
/* Send refusal to the remote host. */ |
/* Send refusal to the remote host. */ |
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
packet_put_int(remote_id); |
packet_put_int(remote_id); |
xfree(remote_host); |
|
} else { |
} else { |
/* Send a confirmation to the remote host. */ |
/* Send a confirmation to the remote host. */ |
packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); |
packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); |
|
|
deny_input_open(int type, u_int32_t seq, void *ctxt) |
deny_input_open(int type, u_int32_t seq, void *ctxt) |
{ |
{ |
int rchan = packet_get_int(); |
int rchan = packet_get_int(); |
|
|
switch (type) { |
switch (type) { |
case SSH_SMSG_AGENT_OPEN: |
case SSH_SMSG_AGENT_OPEN: |
error("Warning: ssh server tried agent forwarding."); |
error("Warning: ssh server tried agent forwarding."); |
|
|
packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); |
packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); |
packet_send(); |
packet_send(); |
packet_write_wait(); |
packet_write_wait(); |
|
} |
|
|
|
/* |
|
* Returns the name of the forwarded authentication socket. Returns NULL if |
|
* there is no forwarded authentication socket. The returned value points to |
|
* a static buffer. |
|
*/ |
|
|
|
char * |
|
auth_get_socket_name(void) |
|
{ |
|
return auth_sock_name; |
|
} |
|
|
|
/* removes the agent forwarding socket */ |
|
|
|
void |
|
auth_sock_cleanup_proc(void *_pw) |
|
{ |
|
struct passwd *pw = _pw; |
|
|
|
if (auth_sock_name) { |
|
temporarily_use_uid(pw); |
|
unlink(auth_sock_name); |
|
rmdir(auth_sock_dir); |
|
auth_sock_name = NULL; |
|
restore_uid(); |
|
} |
|
} |
|
|
|
/* |
|
* This is called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. |
|
* This starts forwarding authentication requests. |
|
*/ |
|
|
|
int |
|
auth_input_request_forwarding(struct passwd * pw) |
|
{ |
|
Channel *nc; |
|
int sock; |
|
struct sockaddr_un sunaddr; |
|
|
|
if (auth_get_socket_name() != NULL) { |
|
error("authentication forwarding requested twice."); |
|
return 0; |
|
} |
|
|
|
/* Temporarily drop privileged uid for mkdir/bind. */ |
|
temporarily_use_uid(pw); |
|
|
|
/* Allocate a buffer for the socket name, and format the name. */ |
|
auth_sock_name = xmalloc(MAXPATHLEN); |
|
auth_sock_dir = xmalloc(MAXPATHLEN); |
|
strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); |
|
|
|
/* Create private directory for socket */ |
|
if (mkdtemp(auth_sock_dir) == NULL) { |
|
packet_send_debug("Agent forwarding disabled: " |
|
"mkdtemp() failed: %.100s", strerror(errno)); |
|
restore_uid(); |
|
xfree(auth_sock_name); |
|
xfree(auth_sock_dir); |
|
auth_sock_name = NULL; |
|
auth_sock_dir = NULL; |
|
return 0; |
|
} |
|
snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%d", |
|
auth_sock_dir, (int) getpid()); |
|
|
|
/* delete agent socket on fatal() */ |
|
fatal_add_cleanup(auth_sock_cleanup_proc, pw); |
|
|
|
/* Create the socket. */ |
|
sock = socket(AF_UNIX, SOCK_STREAM, 0); |
|
if (sock < 0) |
|
packet_disconnect("socket: %.100s", strerror(errno)); |
|
|
|
/* Bind it to the name. */ |
|
memset(&sunaddr, 0, sizeof(sunaddr)); |
|
sunaddr.sun_family = AF_UNIX; |
|
strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); |
|
|
|
if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) |
|
packet_disconnect("bind: %.100s", strerror(errno)); |
|
|
|
/* Restore the privileged uid. */ |
|
restore_uid(); |
|
|
|
/* Start listening on the socket. */ |
|
if (listen(sock, 5) < 0) |
|
packet_disconnect("listen: %.100s", strerror(errno)); |
|
|
|
/* Allocate a channel for the authentication agent socket. */ |
|
nc = channel_new("auth socket", |
|
SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, |
|
CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, |
|
0, xstrdup("auth socket"), 1); |
|
strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); |
|
return 1; |
} |
} |
|
|
/* This is called to process an SSH_SMSG_AGENT_OPEN message. */ |
/* This is called to process an SSH_SMSG_AGENT_OPEN message. */ |