-/* $OpenBSD: channels.c,v 1.255 2006/07/12 22:28:51 stevesk Exp $ */
+/* $OpenBSD: channels.c,v 1.272 2008/01/19 23:02:40 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
#include "includes.h"
-#include <sys/ioctl.h>
#include <sys/types.h>
+#include <sys/ioctl.h>
#include <sys/un.h>
#include <sys/socket.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
-#if defined(HAVE_NETDB_H)
-# include <netdb.h>
-#endif
+#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 "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 */
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
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)
{
FD_SET(c->sock, readset);
}
+/* ARGSUSED */
static void
channel_pre_connecting(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)
{
}
}
+/* ARGSUSED */
static void
channel_pre_output_draining(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)
{
#define SSH_SOCKS5_CONNECT 0x01
#define SSH_SOCKS5_SUCCESS 0x00
+/* ARGSUSED */
static int
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;
}
}
/* This is our fake X11 server socket. */
+/* ARGSUSED */
static void
channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
{
/*
* 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)
{
* 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)
{
}
}
+/* ARGSUSED */
static void
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)
{
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) {
return 1;
}
+/* ARGSUSED */
static int
channel_handle_wfd(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",
return 1;
}
+/* ARGSUSED */
static int
channel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset)
{
{
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);
channel_check_window(c);
}
+/* ARGSUSED */
static void
channel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset)
{
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;
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;
}
/* 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);
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)
{
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 */
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) {
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) {
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;
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) {
hints.ai_socktype = SOCK_STREAM;
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) {
* with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
*/
+/* ARGSUSED */
void
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)
{
return;
}
- cp = disp;
- if (disp)
- cp = strchr(disp, ':');
+ cp = strchr(disp, ':');
if (cp)
cp = strchr(cp, '.');
if (cp)