X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/1908529fcf23bab32dea66bccdbf02ae4b599818..caba7de7d39b58f9f4fa9f33ca04531e0358e266:/channels.c diff --git a/channels.c b/channels.c index b431532a..c766cc27 100644 --- a/channels.c +++ b/channels.c @@ -1,3 +1,4 @@ +/* $OpenBSD: channels.c,v 1.272 2008/01/19 23:02:40 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -39,27 +40,44 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.230 2005/12/28 22:46:06 stevesk Exp $"); +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "packet.h" -#include "xmalloc.h" #include "log.h" #include "misc.h" +#include "buffer.h" #include "channels.h" #include "compat.h" #include "canohost.h" #include "key.h" #include "authfd.h" #include "pathnames.h" -#include "bufaux.h" /* -- channel core */ -#define CHAN_RBUF 16*1024 - /* * Pointer to an array containing all allocated channels. The array is * dynamically extended as needed. @@ -93,11 +111,18 @@ typedef struct { u_short listen_port; /* Remote side should listen port number. */ } 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]; -/* 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; + +/* 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 * on which we have to trust the client anyway, and the user could do @@ -125,7 +150,7 @@ static u_int x11_saved_data_len = 0; * Fake X11 authentication data. This is what the server will be sending us; * 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; @@ -170,7 +195,7 @@ channel_lookup(int id) if ((c = channel_by_id(id)) == NULL) return (NULL); - switch(c->type) { + switch (c->type) { case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_CONNECTING: @@ -180,7 +205,6 @@ channel_lookup(int id) case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: return (c); - break; } logit("Non-public channel %d, type %d.", id, c->type); return (NULL); @@ -190,7 +214,6 @@ channel_lookup(int id) * Register filedescriptors for a channel, used when allocating a channel or * when the channel consumer/producer is ready, e.g. shell exec'd */ - static void channel_register_fds(Channel *c, int rfd, int wfd, int efd, int extusage, int nonblock) @@ -237,7 +260,6 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, * Allocate a new channel object and set its type and socket. This will cause * remote_name to be freed. */ - Channel * 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) @@ -249,7 +271,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, /* Do initial allocation if this is the first call. */ if (channels_alloc == 0) { channels_alloc = 10; - channels = xmalloc(channels_alloc * sizeof(Channel *)); + channels = xcalloc(channels_alloc, sizeof(Channel *)); for (i = 0; i < channels_alloc; i++) channels[i] = NULL; } @@ -266,16 +288,15 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, if (channels_alloc > 10000) fatal("channel_new: internal error: channels_alloc %d " "too big.", channels_alloc); - channels = xrealloc(channels, - (channels_alloc + 10) * sizeof(Channel *)); + channels = xrealloc(channels, channels_alloc + 10, + sizeof(Channel *)); channels_alloc += 10; debug2("channel: expanding %d", channels_alloc); for (i = found; i < channels_alloc; i++) channels[i] = NULL; } /* Initialize and return new channel. */ - c = channels[found] = xmalloc(sizeof(Channel)); - memset(c, 0, sizeof(Channel)); + c = channels[found] = xcalloc(1, sizeof(Channel)); buffer_init(&c->input); buffer_init(&c->output); buffer_init(&c->extended); @@ -301,6 +322,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, c->confirm = NULL; c->confirm_ctx = NULL; c->input_filter = NULL; + c->output_filter = NULL; debug("channel %d: new [%s]", found, remote_name); return c; } @@ -338,7 +360,6 @@ channel_close_fd(int *fdp) } /* Close all channel fd/socket. */ - static void channel_close_fds(Channel *c) { @@ -353,7 +374,6 @@ channel_close_fds(Channel *c) } /* Free the channel and close its fd/socket. */ - void channel_free(Channel *c) { @@ -400,7 +420,6 @@ channel_free_all(void) * Closes the sockets/fds of all channels. This is used to close extra file * descriptors after a fork. */ - void channel_close_all(void) { @@ -414,7 +433,6 @@ channel_close_all(void) /* * Stop listening to channels. */ - void channel_stop_listening(void) { @@ -441,7 +459,6 @@ channel_stop_listening(void) * Returns true if no channel has too much buffered data, and false if one or * more channel is overfull. */ - int channel_not_very_much_buffered_data(void) { @@ -471,7 +488,6 @@ channel_not_very_much_buffered_data(void) } /* Returns true if any channel is still open. */ - int channel_still_open(void) { @@ -514,7 +530,6 @@ channel_still_open(void) } /* Returns the id of an open channel suitable for keepaliving */ - int channel_find_open(void) { @@ -559,7 +574,6 @@ channel_find_open(void) * suitable for sending to the client. The message contains crlf pairs for * newlines. */ - char * channel_open_message(void) { @@ -644,6 +658,7 @@ channel_request_start(int id, char *service, int wantconfirm) packet_put_cstring(service); packet_put_char(wantconfirm); } + void channel_register_confirm(int id, channel_callback_fn *fn, void *ctx) { @@ -656,6 +671,7 @@ channel_register_confirm(int id, channel_callback_fn *fn, void *ctx) c->confirm = fn; c->confirm_ctx = ctx; } + void channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) { @@ -668,6 +684,7 @@ channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) c->detach_user = fn; c->detach_close = do_close; } + void channel_cancel_cleanup(int id) { @@ -680,8 +697,10 @@ channel_cancel_cleanup(int id) c->detach_user = NULL; c->detach_close = 0; } + void -channel_register_filter(int id, channel_filter_fn *fn) +channel_register_filter(int id, channel_infilter_fn *ifn, + channel_outfilter_fn *ofn) { Channel *c = channel_lookup(id); @@ -689,7 +708,8 @@ channel_register_filter(int id, channel_filter_fn *fn) logit("channel_register_filter: %d: bad id", id); return; } - c->input_filter = fn; + c->input_filter = ifn; + c->output_filter = ofn; } void @@ -717,25 +737,27 @@ channel_set_fds(int id, int rfd, int wfd, int efd, * 'channel_post*': perform any appropriate operations for channels which * 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_post[SSH_CHANNEL_MAX_TYPE]; +/* ARGSUSED */ 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); } +/* ARGSUSED */ 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); FD_SET(c->sock, writeset); } 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()) FD_SET(c->sock, readset); @@ -744,16 +766,14 @@ channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) } 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(); - /* check buffer limits */ - limit = MIN(limit, (BUFFER_MAX_LEN - BUFFER_MAX_CHUNK - CHAN_RBUF)); - if (c->istate == CHAN_INPUT_OPEN && limit > 0 && - buffer_len(&c->input) < limit) + buffer_len(&c->input) < limit && + buffer_check_alloc(&c->input, CHAN_RBUF)) FD_SET(c->rfd, readset); if (c->ostate == CHAN_OUTPUT_OPEN || c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { @@ -783,8 +803,9 @@ channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset) FD_SET(c->ctl_fd, readset); } +/* ARGSUSED */ 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) { packet_start(SSH_MSG_CHANNEL_CLOSE); @@ -795,8 +816,9 @@ channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) } } +/* ARGSUSED */ 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) chan_mark_dead(c); @@ -872,7 +894,7 @@ x11_open_helper(Buffer *b) } 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); @@ -898,7 +920,7 @@ channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) } 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); @@ -924,8 +946,9 @@ channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) } /* try to decode a socks4 header */ +/* ARGSUSED */ 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_int len, have, i, found; @@ -989,7 +1012,7 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) s4_rsp.command = 90; /* cd: req granted */ s4_rsp.dest_port = 0; /* 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; } @@ -1002,8 +1025,9 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) #define SSH_SOCKS5_CONNECT 0x01 #define SSH_SOCKS5_SUCCESS 0x00 +/* ARGSUSED */ 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 { u_int8_t version; @@ -1013,7 +1037,7 @@ channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) } s5_req, s5_rsp; u_int16_t dest_port; 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); p = buffer_ptr(&c->input); @@ -1028,8 +1052,8 @@ channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) if (have < nmethods + 2) return 0; /* look for method: "NO AUTHENTICATION REQUIRED" */ - for (found = 0, i = 2 ; i < nmethods + 2; i++) { - if (p[i] == SSH_SOCKS5_NOAUTH ) { + for (found = 0, i = 2; i < nmethods + 2; i++) { + if (p[i] == SSH_SOCKS5_NOAUTH) { found = 1; break; } @@ -1050,7 +1074,7 @@ channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) debug2("channel %d: socks5 post auth", c->self); if (have < sizeof(s5_req)+1) return 0; /* need more */ - memcpy((char *)&s5_req, p, sizeof(s5_req)); + memcpy(&s5_req, p, sizeof(s5_req)); if (s5_req.version != 0x05 || s5_req.command != SSH_SOCKS5_CONNECT || s5_req.reserved != 0x00) { @@ -1074,7 +1098,10 @@ channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); 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; buffer_consume(&c->input, sizeof(s5_req)); if (s5_req.atyp == SSH_SOCKS5_DOMAIN) @@ -1098,15 +1125,15 @@ channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; dest_port = 0; /* ignored */ - buffer_append(&c->output, (char *)&s5_rsp, sizeof(s5_rsp)); - buffer_append(&c->output, (char *)&dest_addr, sizeof(struct in_addr)); - buffer_append(&c->output, (char *)&dest_port, sizeof(dest_port)); + buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); + buffer_append(&c->output, &dest_addr, sizeof(struct in_addr)); + buffer_append(&c->output, &dest_port, sizeof(dest_port)); return 1; } /* dynamic port forwarding */ 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_int have; @@ -1149,8 +1176,9 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) } /* This is our fake X11 server socket. */ +/* ARGSUSED */ 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; struct sockaddr addr; @@ -1274,8 +1302,9 @@ channel_set_reuseaddr(int fd) /* * This socket is listening for connections to a forwarded TCP/IP port. */ +/* ARGSUSED */ 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; struct sockaddr addr; @@ -1331,8 +1360,9 @@ channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) * This is the authentication agent socket listening for connections from * clients. */ +/* ARGSUSED */ 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; int newsock; @@ -1364,8 +1394,9 @@ channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) } } +/* ARGSUSED */ 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; socklen_t sz = sizeof(err); @@ -1410,18 +1441,25 @@ channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset) } } +/* ARGSUSED */ 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]; - int len; + int len, force; - if (c->rfd != -1 && - FD_ISSET(c->rfd, readset)) { + force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; + if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { + errno = 0; len = read(c->rfd, buf, sizeof(buf)); - if (len < 0 && (errno == EINTR || errno == EAGAIN)) + if (len < 0 && (errno == EINTR || (errno == EAGAIN && !force))) return 1; +#ifndef PTY_ZEROREAD if (len <= 0) { +#else + if ((!c->isatty && len <= 0) || + (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { +#endif debug2("channel %d: read<=0 rfd %d len %d", c->self, c->rfd, len); if (c->type != SSH_CHANNEL_OPEN) { @@ -1450,11 +1488,13 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) } return 1; } + +/* ARGSUSED */ 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; - u_char *data; + u_char *data = NULL, *buf; u_int dlen; int len; @@ -1462,11 +1502,26 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && buffer_len(&c->output) > 0) { + if (c->output_filter != NULL) { + if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { + debug2("channel %d: filter stops", c->self); + if (c->type != SSH_CHANNEL_OPEN) + chan_mark_dead(c); + else + chan_write_failed(c); + return -1; + } + } else if (c->datagram) { + buf = data = buffer_get_string(&c->output, &dlen); + } else { + buf = data = buffer_ptr(&c->output); + dlen = buffer_len(&c->output); + } + if (c->datagram) { - data = buffer_get_string(&c->output, &dlen); /* ignore truncated writes, datagrams might get lost */ c->local_consumed += dlen + 4; - len = write(c->wfd, data, dlen); + len = write(c->wfd, buf, dlen); xfree(data); if (len < 0 && (errno == EINTR || errno == EAGAIN)) return 1; @@ -1479,14 +1534,13 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) } return 1; } - data = buffer_ptr(&c->output); - dlen = buffer_len(&c->output); #ifdef _AIX /* XXX: Later AIX versions can't push as much data to tty */ if (compat20 && c->wfd_isatty) dlen = MIN(dlen, 8*1024); #endif - len = write(c->wfd, data, dlen); + + len = write(c->wfd, buf, dlen); if (len < 0 && (errno == EINTR || errno == EAGAIN)) return 1; if (len <= 0) { @@ -1503,14 +1557,14 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) } return -1; } - if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') { + if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { if (tcgetattr(c->wfd, &tio) == 0 && !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { /* * Simulate echo to reduce the impact of * traffic analysis. We need to match the * size of a SSH2_MSG_CHANNEL_DATA message - * (4 byte channel id + data) + * (4 byte channel id + buf) */ packet_send_ignore(4 + len); packet_send(); @@ -1523,8 +1577,9 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) } return 1; } + 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]; int len; @@ -1549,11 +1604,12 @@ channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) c->local_consumed += len; } } else if (c->extended_usage == CHAN_EXTENDED_READ && - FD_ISSET(c->efd, readset)) { + (c->detach_close || FD_ISSET(c->efd, readset))) { len = read(c->efd, buf, sizeof(buf)); debug2("channel %d: read %d from efd %d", c->self, len, c->efd); - if (len < 0 && (errno == EINTR || errno == EAGAIN)) + if (len < 0 && (errno == EINTR || + (errno == EAGAIN && !c->detach_close))) return 1; if (len <= 0) { debug2("channel %d: closing read-efd %d", @@ -1566,8 +1622,10 @@ channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) } return 1; } + +/* ARGSUSED */ 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]; int len; @@ -1593,12 +1651,15 @@ channel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset) } return 1; } + static int channel_check_window(Channel *c) { if (c->type == SSH_CHANNEL_OPEN && !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && - c->local_window < c->local_window_max/2 && + ((c->local_window_max - c->local_window > + c->local_maxpacket*3) || + c->local_window < c->local_window_max/2) && c->local_consumed > 0) { packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); packet_put_int(c->remote_id); @@ -1614,7 +1675,7 @@ channel_check_window(Channel *c) } 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) return; @@ -1627,8 +1688,9 @@ channel_post_open(Channel *c, fd_set * readset, fd_set * writeset) channel_check_window(c); } +/* ARGSUSED */ 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; @@ -1745,7 +1807,7 @@ channel_garbage_collect(Channel *c) } 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; u_int i; @@ -1773,15 +1835,20 @@ void channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, int rekeying) { - u_int n, sz; + u_int n, sz, nfdset; 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? */ if (*readsetp == NULL || sz > *nallocp) { - *readsetp = xrealloc(*readsetp, sz); - *writesetp = xrealloc(*writesetp, sz); + *readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask)); + *writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask)); *nallocp = sz; } *maxfdp = n; @@ -1797,14 +1864,13 @@ channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, * events pending. */ 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); } /* If there is data to send to the connection, enqueue some of it now. */ - void channel_output_poll(void) { @@ -1925,6 +1991,7 @@ channel_output_poll(void) /* -- protocol input */ +/* ARGSUSED */ void channel_input_data(int type, u_int32_t seq, void *ctxt) { @@ -1984,6 +2051,7 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) xfree(data); } +/* ARGSUSED */ void channel_input_extended_data(int type, u_int32_t seq, void *ctxt) { @@ -2030,6 +2098,7 @@ channel_input_extended_data(int type, u_int32_t seq, void *ctxt) xfree(data); } +/* ARGSUSED */ void channel_input_ieof(int type, u_int32_t seq, void *ctxt) { @@ -2053,6 +2122,7 @@ channel_input_ieof(int type, u_int32_t seq, void *ctxt) } +/* ARGSUSED */ void channel_input_close(int type, u_int32_t seq, void *ctxt) { @@ -2091,6 +2161,7 @@ channel_input_close(int type, u_int32_t seq, void *ctxt) } /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ +/* ARGSUSED */ void channel_input_oclose(int type, u_int32_t seq, void *ctxt) { @@ -2103,6 +2174,7 @@ channel_input_oclose(int type, u_int32_t seq, void *ctxt) chan_rcvd_oclose(c); } +/* ARGSUSED */ void channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) { @@ -2119,6 +2191,7 @@ channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) channel_free(c); } +/* ARGSUSED */ void channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) { @@ -2166,6 +2239,7 @@ reason2txt(int reason) return "unknown reason"; } +/* ARGSUSED */ void channel_input_open_failure(int type, u_int32_t seq, void *ctxt) { @@ -2197,6 +2271,7 @@ channel_input_open_failure(int type, u_int32_t seq, void *ctxt) channel_free(c); } +/* ARGSUSED */ void channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) { @@ -2221,6 +2296,7 @@ channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) c->remote_window += adjust; } +/* ARGSUSED */ void channel_input_port_open(int type, u_int32_t seq, void *ctxt) { @@ -2309,7 +2385,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por wildcard = 1; } else if (gateway_ports || is_client) { if (((datafellows & SSH_OLD_FORWARD_ADDR) && - strcmp(listen_addr, "0.0.0.0") == 0) || + strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || (!is_client && gateway_ports == 1)) wildcard = 1; @@ -2333,10 +2409,11 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por if (addr == NULL) { /* This really shouldn't happen */ packet_disconnect("getaddrinfo: fatal error: %s", - gai_strerror(r)); + ssh_gai_strerror(r)); } else { error("channel_setup_fwd_listener: " - "getaddrinfo(%.64s): %s", addr, gai_strerror(r)); + "getaddrinfo(%.64s): %s", addr, + ssh_gai_strerror(r)); } return 0; } @@ -2439,7 +2516,7 @@ channel_setup_remote_fwd_listener(const char *listen_address, * the secure channel to host:port from local side. */ -void +int channel_request_remote_forwarding(const char *listen_host, u_short listen_port, const char *host_to_connect, u_short port_to_connect) { @@ -2452,11 +2529,18 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port, /* Send the forward request to the remote side. */ if (compat20) { 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 + if (listen_host == NULL) { + if (datafellows & SSH_BUG_RFWD_ADDR) + address_to_bind = "127.0.0.1"; + else + address_to_bind = "localhost"; + } else if (*listen_host == '\0' || + strcmp(listen_host, "*") == 0) { + if (datafellows & SSH_BUG_RFWD_ADDR) + address_to_bind = "0.0.0.0"; + else + address_to_bind = ""; + } else address_to_bind = listen_host; packet_start(SSH2_MSG_GLOBAL_REQUEST); @@ -2483,7 +2567,6 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port, success = 1; break; case SSH_SMSG_FAILURE: - logit("Warning: Server denied remote port forwarding."); break; default: /* Unknown packet */ @@ -2497,6 +2580,7 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port, permitted_opens[num_permitted_opens].listen_port = listen_port; num_permitted_opens++; } + return (success ? 0 : -1); } /* @@ -2536,13 +2620,13 @@ channel_request_rforward_cancel(const char *host, u_short port) /* * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates * listening for the port, and sends back a success reply (or disconnect - * message if there was an error). This never returns if there was an error. + * message if there was an error). */ - -void +int channel_input_port_forward_request(int is_root, int gateway_ports) { u_short port, host_port; + int success = 0; char *hostname; /* Get arguments from the packet. */ @@ -2564,11 +2648,13 @@ channel_input_port_forward_request(int is_root, int gateway_ports) #endif /* Initiate forwarding */ - channel_setup_local_fwd_listener(NULL, port, hostname, + success = channel_setup_local_fwd_listener(NULL, port, hostname, host_port, gateway_ports); /* Free the argument string. */ xfree(hostname); + + return (success ? 0 : -1); } /* @@ -2587,7 +2673,7 @@ void channel_add_permitted_opens(char *host, int port) { 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); permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); @@ -2597,6 +2683,19 @@ channel_add_permitted_opens(char *host, int port) 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 channel_clear_permitted_opens(void) { @@ -2606,9 +2705,18 @@ channel_clear_permitted_opens(void) if (permitted_opens[i].host_to_connect != NULL) xfree(permitted_opens[i].host_to_connect); 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 */ static int @@ -2625,7 +2733,7 @@ connect_to(const char *host, u_short port) snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { error("connect_to %.100s: unknown host (%s)", host, - gai_strerror(gaierr)); + ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { @@ -2686,7 +2794,7 @@ channel_connect_by_listen_address(u_short listen_port) int channel_connect_to(const char *host, u_short port) { - int i, permit; + int i, permit, permit_adm = 1; permit = all_opens_permitted; if (!permit) { @@ -2695,9 +2803,19 @@ channel_connect_to(const char *host, u_short port) permitted_opens[i].port_to_connect == port && strcmp(permitted_opens[i].host_to_connect, host) == 0) 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, " "but the request was denied.", host, port); return -1; @@ -2718,10 +2836,10 @@ channel_send_window_changes(void) if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) continue; channel_request_start(i, "window-change", 0); - packet_put_int(ws.ws_col); - packet_put_int(ws.ws_row); - packet_put_int(ws.ws_xpixel); - packet_put_int(ws.ws_ypixel); + packet_put_int((u_int)ws.ws_col); + packet_put_int((u_int)ws.ws_row); + packet_put_int((u_int)ws.ws_xpixel); + packet_put_int((u_int)ws.ws_ypixel); packet_send(); } } @@ -2757,7 +2875,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { - error("getaddrinfo: %.100s", gai_strerror(gaierr)); + error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { @@ -2829,7 +2947,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, } /* 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++) { sock = socks[n]; nc = channel_new("x11 listener", @@ -2858,7 +2976,7 @@ connect_local_xsocket(u_int dnr) memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; 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; close(sock); error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); @@ -2868,12 +2986,12 @@ connect_local_xsocket(u_int dnr) int x11_connect_display(void) { - int display_number, sock = 0; + u_int display_number; const char *display; char buf[1024], *cp; struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; - int gaierr; + int gaierr, sock = 0; /* Try to open a socket for the local X server. */ display = getenv("DISPLAY"); @@ -2893,7 +3011,7 @@ x11_connect_display(void) if (strncmp(display, "unix:", 5) == 0 || display[0] == ':') { /* 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", display); return -1; @@ -2918,7 +3036,7 @@ x11_connect_display(void) } *cp = 0; /* 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", display); return -1; @@ -2928,9 +3046,10 @@ x11_connect_display(void) memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; 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) { - error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); + error("%.100s: unknown host. (%s)", buf, + ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { @@ -2942,7 +3061,7 @@ x11_connect_display(void) } /* Connect it to the display. */ 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)); close(sock); continue; @@ -2952,7 +3071,7 @@ x11_connect_display(void) } freeaddrinfo(aitop); if (!ai) { - error("connect %.100s port %d: %.100s", buf, 6000 + display_number, + error("connect %.100s port %u: %.100s", buf, 6000 + display_number, strerror(errno)); return -1; } @@ -2966,6 +3085,7 @@ x11_connect_display(void) * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ +/* ARGSUSED */ void x11_input_open(int type, u_int32_t seq, void *ctxt) { @@ -3009,6 +3129,7 @@ x11_input_open(int type, u_int32_t seq, void *ctxt) } /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ +/* ARGSUSED */ void deny_input_open(int type, u_int32_t seq, void *ctxt) { @@ -3055,13 +3176,11 @@ x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, return; } - cp = disp; - if (disp) - cp = strchr(disp, ':'); + cp = strchr(disp, ':'); if (cp) cp = strchr(cp, '.'); if (cp) - screen_number = atoi(cp + 1); + screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); else screen_number = 0;