summaryrefslogtreecommitdiffstats
path: root/channels.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2014-07-18 14:11:24 +1000
committerDamien Miller <djm@mindrot.org>2014-07-18 14:11:24 +1000
commit7acefbbcbeab725420ea07397ae35992f505f702 (patch)
treebfb07917715d425438dab987a47ccd7a8d7f118b /channels.c
parent6262d760e00714523633bd989d62e273a3dca99a (diff)
- millert@cvs.openbsd.org 2014/07/15 15:54:14
[PROTOCOL auth-options.c auth-passwd.c auth-rh-rsa.c auth-rhosts.c] [auth-rsa.c auth.c auth1.c auth2-hostbased.c auth2-kbdint.c auth2-none.c] [auth2-passwd.c auth2-pubkey.c auth2.c canohost.c channels.c channels.h] [clientloop.c misc.c misc.h monitor.c mux.c packet.c readconf.c] [readconf.h servconf.c servconf.h serverloop.c session.c ssh-agent.c] [ssh.c ssh_config.5 sshconnect.c sshconnect1.c sshconnect2.c sshd.c] [sshd_config.5 sshlogin.c] Add support for Unix domain socket forwarding. A remote TCP port may be forwarded to a local Unix domain socket and vice versa or both ends may be a Unix domain socket. This is a reimplementation of the streamlocal patches by William Ahern from: http://www.25thandclement.com/~william/projects/streamlocal.html OK djm@ markus@
Diffstat (limited to 'channels.c')
-rw-r--r--channels.c600
1 files changed, 483 insertions, 117 deletions
diff --git a/channels.c b/channels.c
index dcd75346..d67fdf48 100644
--- a/channels.c
+++ b/channels.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.335 2014/07/05 23:11:48 djm Exp $ */
+/* $OpenBSD: channels.c,v 1.336 2014/07/15 15:54:14 millert Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -42,6 +42,7 @@
#include "includes.h"
#include <sys/types.h>
+#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <sys/socket.h>
@@ -107,11 +108,15 @@ static int channel_max_fd = 0;
* a corrupt remote server from accessing arbitrary TCP/IP ports on our local
* network (which might be behind a firewall).
*/
+/* XXX: streamlocal wants a path instead of host:port */
+/* Overload host_to_connect; we could just make this match Forward */
+/* XXX - can we use listen_host instead of listen_path? */
typedef struct {
char *host_to_connect; /* Connect to 'host'. */
- u_short port_to_connect; /* Connect to 'port'. */
+ int port_to_connect; /* Connect to 'port'. */
char *listen_host; /* Remote side should listen address. */
- u_short listen_port; /* Remote side should listen port. */
+ char *listen_path; /* Remote side should listen path. */
+ int listen_port; /* Remote side should listen port. */
} ForwardPermission;
/* List of all permitted host/port pairs to connect by the user. */
@@ -474,6 +479,8 @@ channel_stop_listening(void)
case SSH_CHANNEL_PORT_LISTENER:
case SSH_CHANNEL_RPORT_LISTENER:
case SSH_CHANNEL_X11_LISTENER:
+ case SSH_CHANNEL_UNIX_LISTENER:
+ case SSH_CHANNEL_RUNIX_LISTENER:
channel_close_fd(&c->sock);
channel_free(c);
break;
@@ -536,6 +543,8 @@ channel_still_open(void)
case SSH_CHANNEL_CONNECTING:
case SSH_CHANNEL_ZOMBIE:
case SSH_CHANNEL_ABANDONED:
+ case SSH_CHANNEL_UNIX_LISTENER:
+ case SSH_CHANNEL_RUNIX_LISTENER:
continue;
case SSH_CHANNEL_LARVAL:
if (!compat20)
@@ -582,6 +591,8 @@ channel_find_open(void)
case SSH_CHANNEL_CONNECTING:
case SSH_CHANNEL_ZOMBIE:
case SSH_CHANNEL_ABANDONED:
+ case SSH_CHANNEL_UNIX_LISTENER:
+ case SSH_CHANNEL_RUNIX_LISTENER:
continue;
case SSH_CHANNEL_LARVAL:
case SSH_CHANNEL_AUTH_SOCKET:
@@ -632,6 +643,8 @@ channel_open_message(void)
case SSH_CHANNEL_ABANDONED:
case SSH_CHANNEL_MUX_CLIENT:
case SSH_CHANNEL_MUX_LISTENER:
+ case SSH_CHANNEL_UNIX_LISTENER:
+ case SSH_CHANNEL_RUNIX_LISTENER:
continue;
case SSH_CHANNEL_LARVAL:
case SSH_CHANNEL_OPENING:
@@ -1387,7 +1400,6 @@ channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
static void
port_open_helper(Channel *c, char *rtype)
{
- int direct;
char buf[1024];
char *local_ipaddr = get_local_ipaddr(c->sock);
int local_port = c->sock == -1 ? 65536 : get_sock_port(c->sock, 1);
@@ -1401,8 +1413,6 @@ port_open_helper(Channel *c, char *rtype)
remote_port = 65535;
}
- direct = (strcmp(rtype, "direct-tcpip") == 0);
-
snprintf(buf, sizeof buf,
"%s: listening port %d for %.100s port %d, "
"connect from %.200s port %d to %.100s port %d",
@@ -1418,18 +1428,29 @@ port_open_helper(Channel *c, char *rtype)
packet_put_int(c->self);
packet_put_int(c->local_window_max);
packet_put_int(c->local_maxpacket);
- if (direct) {
+ if (strcmp(rtype, "direct-tcpip") == 0) {
/* target host, port */
packet_put_cstring(c->path);
packet_put_int(c->host_port);
+ } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) {
+ /* target path */
+ packet_put_cstring(c->path);
+ } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
+ /* listen path */
+ packet_put_cstring(c->path);
} else {
/* listen address, port */
packet_put_cstring(c->path);
packet_put_int(local_port);
}
- /* originator host and port */
- packet_put_cstring(remote_ipaddr);
- packet_put_int((u_int)remote_port);
+ if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
+ /* reserved for future owner/mode info */
+ packet_put_cstring("");
+ } else {
+ /* originator host and port */
+ packet_put_cstring(remote_ipaddr);
+ packet_put_int((u_int)remote_port);
+ }
packet_send();
} else {
packet_start(SSH_MSG_PORT_OPEN);
@@ -1479,14 +1500,18 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
nextstate = SSH_CHANNEL_OPENING;
rtype = "forwarded-tcpip";
+ } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) {
+ nextstate = SSH_CHANNEL_OPENING;
+ rtype = "forwarded-streamlocal@openssh.com";
+ } else if (c->host_port == PORT_STREAMLOCAL) {
+ nextstate = SSH_CHANNEL_OPENING;
+ rtype = "direct-streamlocal@openssh.com";
+ } else if (c->host_port == 0) {
+ nextstate = SSH_CHANNEL_DYNAMIC;
+ rtype = "dynamic-tcpip";
} else {
- if (c->host_port == 0) {
- nextstate = SSH_CHANNEL_DYNAMIC;
- rtype = "dynamic-tcpip";
- } else {
- nextstate = SSH_CHANNEL_OPENING;
- rtype = "direct-tcpip";
- }
+ nextstate = SSH_CHANNEL_OPENING;
+ rtype = "direct-tcpip";
}
addrlen = sizeof(addr);
@@ -1499,7 +1524,8 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
c->notbefore = monotime() + 1;
return;
}
- set_nodelay(newsock);
+ if (c->host_port != PORT_STREAMLOCAL)
+ set_nodelay(newsock);
nc = channel_new(rtype, nextstate, newsock, newsock, -1,
c->local_window_max, c->local_maxpacket, 0, rtype, 1);
nc->listening_port = c->listening_port;
@@ -1988,6 +2014,8 @@ channel_handler_init_20(void)
channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open;
channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener;
+ channel_pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener;
+ channel_pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting;
@@ -1998,6 +2026,8 @@ channel_handler_init_20(void)
channel_post[SSH_CHANNEL_OPEN] = &channel_post_open;
channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener;
channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener;
+ channel_post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener;
+ channel_post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener;
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener;
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
@@ -2638,7 +2668,7 @@ channel_input_port_open(int type, u_int32_t seq, void *ctxt)
originator_string = xstrdup("unknown (remote did not supply name)");
}
packet_check_eom();
- c = channel_connect_to(host, host_port,
+ c = channel_connect_to_port(host, host_port,
"connected socket", originator_string);
free(originator_string);
free(host);
@@ -2705,20 +2735,20 @@ channel_set_af(int af)
*/
static const char *
channel_fwd_bind_addr(const char *listen_addr, int *wildcardp,
- int is_client, int gateway_ports)
+ int is_client, struct ForwardOptions *fwd_opts)
{
const char *addr = NULL;
int wildcard = 0;
if (listen_addr == NULL) {
/* No address specified: default to gateway_ports setting */
- if (gateway_ports)
+ if (fwd_opts->gateway_ports)
wildcard = 1;
- } else if (gateway_ports || is_client) {
+ } else if (fwd_opts->gateway_ports || is_client) {
if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
*listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
- (!is_client && gateway_ports == 1)) {
+ (!is_client && fwd_opts->gateway_ports == 1)) {
wildcard = 1;
/*
* Notify client if they requested a specific listen
@@ -2752,9 +2782,8 @@ channel_fwd_bind_addr(const char *listen_addr, int *wildcardp,
}
static int
-channel_setup_fwd_listener(int type, const char *listen_addr,
- u_short listen_port, int *allocated_listen_port,
- const char *host_to_connect, u_short port_to_connect, int gateway_ports)
+channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd,
+ int *allocated_listen_port, struct ForwardOptions *fwd_opts)
{
Channel *c;
int sock, r, success = 0, wildcard = 0, is_client;
@@ -2764,7 +2793,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr,
in_port_t *lport_p;
host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
- listen_addr : host_to_connect;
+ fwd->listen_host : fwd->connect_host;
is_client = (type == SSH_CHANNEL_PORT_LISTENER);
if (host == NULL) {
@@ -2777,9 +2806,9 @@ channel_setup_fwd_listener(int type, const char *listen_addr,
}
/* Determine the bind address, cf. channel_fwd_bind_addr() comment */
- addr = channel_fwd_bind_addr(listen_addr, &wildcard,
- is_client, gateway_ports);
- debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s",
+ addr = channel_fwd_bind_addr(fwd->listen_host, &wildcard,
+ is_client, fwd_opts);
+ debug3("%s: type %d wildcard %d addr %s", __func__,
type, wildcard, (addr == NULL) ? "NULL" : addr);
/*
@@ -2790,15 +2819,14 @@ channel_setup_fwd_listener(int type, const char *listen_addr,
hints.ai_family = IPv4or6;
hints.ai_flags = wildcard ? AI_PASSIVE : 0;
hints.ai_socktype = SOCK_STREAM;
- snprintf(strport, sizeof strport, "%d", listen_port);
+ snprintf(strport, sizeof strport, "%d", fwd->listen_port);
if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
if (addr == NULL) {
/* This really shouldn't happen */
packet_disconnect("getaddrinfo: fatal error: %s",
ssh_gai_strerror(r));
} else {
- error("channel_setup_fwd_listener: "
- "getaddrinfo(%.64s): %s", addr,
+ error("%s: getaddrinfo(%.64s): %s", __func__, addr,
ssh_gai_strerror(r));
}
return 0;
@@ -2822,13 +2850,13 @@ channel_setup_fwd_listener(int type, const char *listen_addr,
* If allocating a port for -R forwards, then use the
* same port for all address families.
*/
- if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 &&
+ if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 &&
allocated_listen_port != NULL && *allocated_listen_port > 0)
*lport_p = htons(*allocated_listen_port);
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
- error("channel_setup_fwd_listener: getnameinfo failed");
+ error("%s: getnameinfo failed", __func__);
continue;
}
/* Create a port to listen for the host. */
@@ -2865,10 +2893,10 @@ channel_setup_fwd_listener(int type, const char *listen_addr,
}
/*
- * listen_port == 0 requests a dynamically allocated port -
+ * fwd->listen_port == 0 requests a dynamically allocated port -
* record what we got.
*/
- if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 &&
+ if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 &&
allocated_listen_port != NULL &&
*allocated_listen_port == 0) {
*allocated_listen_port = get_sock_port(sock, 1);
@@ -2881,24 +2909,98 @@ channel_setup_fwd_listener(int type, const char *listen_addr,
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
0, "port listener", 1);
c->path = xstrdup(host);
- c->host_port = port_to_connect;
+ c->host_port = fwd->connect_port;
c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
- if (listen_port == 0 && allocated_listen_port != NULL &&
+ if (fwd->listen_port == 0 && allocated_listen_port != NULL &&
!(datafellows & SSH_BUG_DYNAMIC_RPORT))
c->listening_port = *allocated_listen_port;
else
- c->listening_port = listen_port;
+ c->listening_port = fwd->listen_port;
success = 1;
}
if (success == 0)
- error("channel_setup_fwd_listener: cannot listen to port: %d",
- listen_port);
+ error("%s: cannot listen to port: %d", __func__,
+ fwd->listen_port);
freeaddrinfo(aitop);
return success;
}
-int
-channel_cancel_rport_listener(const char *host, u_short port)
+static int
+channel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd,
+ struct ForwardOptions *fwd_opts)
+{
+ struct sockaddr_un sunaddr;
+ const char *path;
+ Channel *c;
+ int port, sock;
+ mode_t omask;
+
+ switch (type) {
+ case SSH_CHANNEL_UNIX_LISTENER:
+ if (fwd->connect_path != NULL) {
+ if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) {
+ error("Local connecting path too long: %s",
+ fwd->connect_path);
+ return 0;
+ }
+ path = fwd->connect_path;
+ port = PORT_STREAMLOCAL;
+ } else {
+ if (fwd->connect_host == NULL) {
+ error("No forward host name.");
+ return 0;
+ }
+ if (strlen(fwd->connect_host) >= NI_MAXHOST) {
+ error("Forward host name too long.");
+ return 0;
+ }
+ path = fwd->connect_host;
+ port = fwd->connect_port;
+ }
+ break;
+ case SSH_CHANNEL_RUNIX_LISTENER:
+ path = fwd->listen_path;
+ port = PORT_STREAMLOCAL;
+ break;
+ default:
+ error("%s: unexpected channel type %d", __func__, type);
+ return 0;
+ }
+
+ if (fwd->listen_path == NULL) {
+ error("No forward path name.");
+ return 0;
+ }
+ if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) {
+ error("Local listening path too long: %s", fwd->listen_path);
+ return 0;
+ }
+
+ debug3("%s: type %d path %s", __func__, type, fwd->listen_path);
+
+ /* Start a Unix domain listener. */
+ omask = umask(fwd_opts->streamlocal_bind_mask);
+ sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG,
+ fwd_opts->streamlocal_bind_unlink);
+ umask(omask);
+ if (sock < 0)
+ return 0;
+
+ debug("Local forwarding listening on path %s.", fwd->listen_path);
+
+ /* Allocate a channel number for the socket. */
+ c = channel_new("unix listener", type, sock, sock, -1,
+ CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
+ 0, "unix listener", 1);
+ c->path = xstrdup(path);
+ c->host_port = port;
+ c->listening_port = PORT_STREAMLOCAL;
+ c->listening_addr = xstrdup(fwd->listen_path);
+ return 1;
+}
+
+static int
+channel_cancel_rport_listener_tcpip(const char *host, u_short port)
{
u_int i;
int found = 0;
@@ -2917,13 +3019,44 @@ channel_cancel_rport_listener(const char *host, u_short port)
return (found);
}
+static int
+channel_cancel_rport_listener_streamlocal(const char *path)
+{
+ u_int i;
+ int found = 0;
+
+ for (i = 0; i < channels_alloc; i++) {
+ Channel *c = channels[i];
+ if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER)
+ continue;
+ if (c->path == NULL)
+ continue;
+ if (strcmp(c->path, path) == 0) {
+ debug2("%s: close channel %d", __func__, i);
+ channel_free(c);
+ found = 1;
+ }
+ }
+
+ return (found);
+}
+
int
-channel_cancel_lport_listener(const char *lhost, u_short lport,
- int cport, int gateway_ports)
+channel_cancel_rport_listener(struct Forward *fwd)
+{
+ if (fwd->listen_path != NULL)
+ return channel_cancel_rport_listener_streamlocal(fwd->listen_path);
+ else
+ return channel_cancel_rport_listener_tcpip(fwd->listen_host, fwd->listen_port);
+}
+
+static int
+channel_cancel_lport_listener_tcpip(const char *lhost, u_short lport,
+ int cport, struct ForwardOptions *fwd_opts)
{
u_int i;
int found = 0;
- const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, gateway_ports);
+ const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts);
for (i = 0; i < channels_alloc; i++) {
Channel *c = channels[i];
@@ -2952,24 +3085,68 @@ channel_cancel_lport_listener(const char *lhost, u_short lport,
return (found);
}
+static int
+channel_cancel_lport_listener_streamlocal(const char *path)
+{
+ u_int i;
+ int found = 0;
+
+ if (path == NULL) {
+ error("%s: no path specified.", __func__);
+ return 0;
+ }
+
+ for (i = 0; i < channels_alloc; i++) {
+ Channel *c = channels[i];
+ if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER)
+ continue;
+ if (c->listening_addr == NULL)
+ continue;
+ if (strcmp(c->listening_addr, path) == 0) {
+ debug2("%s: close channel %d", __func__, i);
+ channel_free(c);
+ found = 1;
+ }
+ }
+
+ return (found);
+}
+
+int
+channel_cancel_lport_listener(struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts)
+{
+ if (fwd->listen_path != NULL)
+ return channel_cancel_lport_listener_streamlocal(fwd->listen_path);
+ else
+ return channel_cancel_lport_listener_tcpip(fwd->listen_host, fwd->listen_port, cport, fwd_opts);
+}
+
/* protocol local port fwd, used by ssh (and sshd in v1) */
int
-channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port,
- const char *host_to_connect, u_short port_to_connect, int gateway_ports)
+channel_setup_local_fwd_listener(struct Forward *fwd, struct ForwardOptions *fwd_opts)
{
- return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
- listen_host, listen_port, NULL, host_to_connect, port_to_connect,
- gateway_ports);
+ if (fwd->listen_path != NULL) {
+ return channel_setup_fwd_listener_streamlocal(
+ SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts);
+ } else {
+ return channel_setup_fwd_listener_tcpip(SSH_CHANNEL_PORT_LISTENER,
+ fwd, NULL, fwd_opts);
+ }
}
/* protocol v2 remote port fwd, used by sshd */
int
-channel_setup_remote_fwd_listener(const char *listen_address,
- u_short listen_port, int *allocated_listen_port, int gateway_ports)
+channel_setup_remote_fwd_listener(struct Forward *fwd,
+ int *allocated_listen_port, struct ForwardOptions *fwd_opts)
{
- return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,
- listen_address, listen_port, allocated_listen_port,
- NULL, 0, gateway_ports);
+ if (fwd->listen_path != NULL) {
+ return channel_setup_fwd_listener_streamlocal(
+ SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);
+ } else {
+ return channel_setup_fwd_listener_tcpip(
+ SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port,
+ fwd_opts);
+ }
}
/*
@@ -3000,27 +3177,32 @@ channel_rfwd_bind_host(const char *listen_host)
* channel_update_permitted_opens().
*/
int
-channel_request_remote_forwarding(const char *listen_host, u_short listen_port,
- const char *host_to_connect, u_short port_to_connect)
+channel_request_remote_forwarding(struct Forward *fwd)
{
int type, success = 0, idx = -1;
/* Send the forward request to the remote side. */
if (compat20) {
packet_start(SSH2_MSG_GLOBAL_REQUEST);
- packet_put_cstring("tcpip-forward");
- packet_put_char(1); /* boolean: want reply */
- packet_put_cstring(channel_rfwd_bind_host(listen_host));
- packet_put_int(listen_port);
+ if (fwd->listen_path != NULL) {
+ packet_put_cstring("streamlocal-forward@openssh.com");
+ packet_put_char(1); /* boolean: want reply */
+ packet_put_cstring(fwd->listen_path);
+ } else {
+ packet_put_cstring("tcpip-forward");
+ packet_put_char(1); /* boolean: want reply */
+ packet_put_cstring(channel_rfwd_bind_host(fwd->listen_host));
+ packet_put_int(fwd->listen_port);
+ }
packet_send();
packet_write_wait();
/* Assume that server accepts the request */
success = 1;
- } else {
+ } else if (fwd->listen_path == NULL) {
packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
- packet_put_int(listen_port);
- packet_put_cstring(host_to_connect);
- packet_put_int(port_to_connect);
+ packet_put_int(fwd->listen_port);
+ packet_put_cstring(fwd->connect_host);
+ packet_put_int(fwd->connect_port);
packet_send();
packet_write_wait();
@@ -3037,24 +3219,43 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port,
packet_disconnect("Protocol error for port forward request:"
"received packet type %d.", type);
}
+ } else {
+ logit("Warning: Server does not support remote stream local forwarding.");
}
if (success) {
/* Record that connection to this host/port is permitted. */
permitted_opens = xrealloc(permitted_opens,
num_permitted_opens + 1, sizeof(*permitted_opens));
idx = num_permitted_opens++;
- permitted_opens[idx].host_to_connect = xstrdup(host_to_connect);
- permitted_opens[idx].port_to_connect = port_to_connect;
- permitted_opens[idx].listen_host = listen_host ?
- xstrdup(listen_host) : NULL;
- permitted_opens[idx].listen_port = listen_port;
+ if (fwd->connect_path != NULL) {
+ permitted_opens[idx].host_to_connect =
+ xstrdup(fwd->connect_path);
+ permitted_opens[idx].port_to_connect =
+ PORT_STREAMLOCAL;
+ } else {
+ permitted_opens[idx].host_to_connect =
+ xstrdup(fwd->connect_host);
+ permitted_opens[idx].port_to_connect =
+ fwd->connect_port;
+ }
+ if (fwd->listen_path != NULL) {
+ permitted_opens[idx].listen_host = NULL;
+ permitted_opens[idx].listen_path =
+ xstrdup(fwd->listen_path);
+ permitted_opens[idx].listen_port = PORT_STREAMLOCAL;
+ } else {
+ permitted_opens[idx].listen_host =
+ fwd->listen_host ? xstrdup(fwd->listen_host) : NULL;
+ permitted_opens[idx].listen_path = NULL;
+ permitted_opens[idx].listen_port = fwd->listen_port;
+ }
}
return (idx);
}
static int
open_match(ForwardPermission *allowed_open, const char *requestedhost,
- u_short requestedport)
+ int requestedport)
{
if (allowed_open->host_to_connect == NULL)
return 0;
@@ -3067,14 +3268,14 @@ open_match(ForwardPermission *allowed_open, const char *requestedhost,
}
/*
- * Note that in he listen host/port case
+ * Note that in the listen host/port case
* we don't support FWD_PERMIT_ANY_PORT and
* need to translate between the configured-host (listen_host)
* and what we've sent to the remote server (channel_rfwd_bind_host)
*/
static int
-open_listen_match(ForwardPermission *allowed_open, const char *requestedhost,
- u_short requestedport, int translate)
+open_listen_match_tcpip(ForwardPermission *allowed_open,
+ const char *requestedhost, u_short requestedport, int translate)
{
const char *allowed_host;
@@ -3094,12 +3295,26 @@ open_listen_match(ForwardPermission *allowed_open, const char *requestedhost,
return 1;
}
+static int
+open_listen_match_streamlocal(ForwardPermission *allowed_open,
+ const char *requestedpath)
+{
+ if (allowed_open->host_to_connect == NULL)
+ return 0;
+ if (allowed_open->listen_port != PORT_STREAMLOCAL)
+ return 0;
+ if (allowed_open->listen_path == NULL ||
+ strcmp(allowed_open->listen_path, requestedpath) != 0)
+ return 0;
+ return 1;
+}
+
/*
* Request cancellation of remote forwarding of connection host:port from
* local side.
*/
-int
-channel_request_rforward_cancel(const char *host, u_short port)
+static int
+channel_request_rforward_cancel_tcpip(const char *host, u_short port)
{
int i;
@@ -3107,7 +3322,7 @@ channel_request_rforward_cancel(const char *host, u_short port)
return -1;
for (i = 0; i < num_permitted_opens; i++) {
- if (open_listen_match(&permitted_opens[i], host, port, 0))
+ if (open_listen_match_tcpip(&permitted_opens[i], host, port, 0))
break;
}
if (i >= num_permitted_opens) {
@@ -3121,15 +3336,68 @@ channel_request_rforward_cancel(const char *host, u_short port)
packet_put_int(port);
packet_send();
- permitted_opens[i].port_to_connect = 0;
permitted_opens[i].listen_port = 0;
+ permitted_opens[i].port_to_connect = 0;
free(permitted_opens[i].host_to_connect);
permitted_opens[i].host_to_connect = NULL;
free(permitted_opens[i].listen_host);
permitted_opens[i].listen_host = NULL;
+ permitted_opens[i].listen_path = NULL;
+
+ return 0;
+}
+
+/*
+ * Request cancellation of remote forwarding of Unix domain socket
+ * path from local side.
+ */
+static int
+channel_request_rforward_cancel_streamlocal(const char *path)
+{
+ int i;
+
+ if (!compat20)
+ return -1;
+
+ for (i = 0; i < num_permitted_opens; i++) {
+ if (open_listen_match_streamlocal(&permitted_opens[i], path))
+ break;
+ }
+ if (i >= num_permitted_opens) {
+ debug("%s: requested forward not found", __func__);
+ return -1;
+ }
+ packet_start(SSH2_MSG_GLOBAL_REQUEST);
+ packet_put_cstring("cancel-streamlocal-forward@openssh.com");
+ packet_put_char(0);
+ packet_put_cstring(path);
+ packet_send();
+
+ permitted_opens[i].listen_port = 0;
+ permitted_opens[i].port_to_connect = 0;
+ free(permitted_opens[i].host_to_connect);
+ permitted_opens[i].host_to_connect = NULL;
+ permitted_opens[i].listen_host = NULL;
+ free(permitted_opens[i].listen_path);
+ permitted_opens[i].listen_path = NULL;
return 0;
}
+
+/*
+ * Request cancellation of remote forwarding of a connection from local side.
+ */
+int
+channel_request_rforward_cancel(struct Forward *fwd)
+{
+ if (fwd->listen_path != NULL) {
+ return (channel_request_rforward_cancel_streamlocal(
+ fwd->listen_path));
+ } else {
+ return (channel_request_rforward_cancel_tcpip(fwd->listen_host,
+ fwd->listen_port ? fwd->listen_port : fwd->allocated_port));
+ }
+}
/*
* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates
@@ -3137,36 +3405,35 @@ channel_request_rforward_cancel(const char *host, u_short port)
* message if there was an error).
*/
int
-channel_input_port_forward_request(int is_root, int gateway_ports)
+channel_input_port_forward_request(int is_root, struct ForwardOptions *fwd_opts)
{
- u_short port, host_port;
int success = 0;
- char *hostname;
+ struct Forward fwd;
/* Get arguments from the packet. */
- port = packet_get_int();
- hostname = packet_get_string(NULL);
- host_port = packet_get_int();
+ memset(&fwd, 0, sizeof(fwd));
+ fwd.listen_port = packet_get_int();
+ fwd.connect_host = packet_get_string(NULL);
+ fwd.connect_port = packet_get_int();
#ifndef HAVE_CYGWIN
/*
* Check that an unprivileged user is not trying to forward a
* privileged port.
*/
- if (port < IPPORT_RESERVED && !is_root)
+ if (fwd.listen_port < IPPORT_RESERVED && !is_root)
packet_disconnect(
"Requested forwarding of port %d but user is not root.",
- port);
- if (host_port == 0)
+ fwd.listen_port);
+ if (fwd.connect_port == 0)
packet_disconnect("Dynamic forwarding denied.");
#endif
/* Initiate forwarding */
- success = channel_setup_local_fwd_listener(NULL, port, hostname,
- host_port, gateway_ports);
+ success = channel_setup_local_fwd_listener(&fwd, fwd_opts);
/* Free the argument string. */
- free(hostname);
+ free(fwd.connect_host);
return (success ? 0 : -1);
}
@@ -3193,6 +3460,7 @@ channel_add_permitted_opens(char *host, int port)
permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host);
permitted_opens[num_permitted_opens].port_to_connect = port;
permitted_opens[num_permitted_opens].listen_host = NULL;
+ permitted_opens[num_permitted_opens].listen_path = NULL;
permitted_opens[num_permitted_opens].listen_port = 0;
num_permitted_opens++;
@@ -3227,6 +3495,8 @@ channel_update_permitted_opens(int idx, int newport)
permitted_opens[idx].host_to_connect = NULL;
free(permitted_opens[idx].listen_host);
permitted_opens[idx].listen_host = NULL;
+ free(permitted_opens[idx].listen_path);
+ permitted_opens[idx].listen_path = NULL;
}
}
@@ -3241,6 +3511,7 @@ channel_add_adm_permitted_opens(char *host, int port)
= xstrdup(host);
permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port;
permitted_adm_opens[num_adm_permitted_opens].listen_host = NULL;
+ permitted_adm_opens[num_adm_permitted_opens].listen_path = NULL;
permitted_adm_opens[num_adm_permitted_opens].listen_port = 0;
return ++num_adm_permitted_opens;
}
@@ -3262,6 +3533,7 @@ channel_clear_permitted_opens(void)
for (i = 0; i < num_permitted_opens; i++) {
free(permitted_opens[i].host_to_connect);
free(permitted_opens[i].listen_host);
+ free(permitted_opens[i].listen_path);
}
free(permitted_opens);
permitted_opens = NULL;
@@ -3276,6 +3548,7 @@ channel_clear_adm_permitted_opens(void)
for (i = 0; i < num_adm_permitted_opens; i++) {
free(permitted_adm_opens[i].host_to_connect);
free(permitted_adm_opens[i].listen_host);
+ free(permitted_adm_opens[i].listen_path);
}
free(permitted_adm_opens);
permitted_adm_opens = NULL;
@@ -3319,16 +3592,27 @@ static int
connect_next(struct channel_connect *cctx)
{
int sock, saved_errno;
- char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+ struct sockaddr_un *sunaddr;
+ char ntop[NI_MAXHOST], strport[MAX(NI_MAXSERV,sizeof(sunaddr->sun_path))];
for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
- if (cctx->ai->ai_family != AF_INET &&
- cctx->ai->ai_family != AF_INET6)
- continue;
- if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
- ntop, sizeof(ntop), strport, sizeof(strport),
- NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
- error("connect_next: getnameinfo failed");
+ switch (cctx->ai->ai_family) {
+ case AF_UNIX:
+ /* unix:pathname instead of host:port */
+ sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr;
+ strlcpy(ntop, "unix", sizeof(ntop));
+ strlcpy(strport, sunaddr->sun_path, sizeof(strport));
+ break;
+ case AF_INET:
+ case AF_INET6:
+ if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
+ ntop, sizeof(ntop), strport, sizeof(strport),
+ NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
+ error("connect_next: getnameinfo failed");
+ continue;
+ }
+ break;
+ default:
continue;
}
if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
@@ -3351,10 +3635,11 @@ connect_next(struct channel_connect *cctx)
errno = saved_errno;
continue; /* fail -- try next */
}
+ if (cctx->ai->ai_family != AF_UNIX)
+ set_nodelay(sock);
debug("connect_next: host %.100s ([%.100s]:%s) "
"in progress, fd=%d", cctx->host, ntop, strport, sock);
cctx->ai = cctx->ai->ai_next;
- set_nodelay(sock);
return sock;
}
return -1;
@@ -3364,14 +3649,18 @@ static void
channel_connect_ctx_free(struct channel_connect *cctx)
{
free(cctx->host);
- if (cctx->aitop)
- freeaddrinfo(cctx->aitop);
+ if (cctx->aitop) {
+ if (cctx->aitop->ai_family == AF_UNIX)
+ free(cctx->aitop);
+ else
+ freeaddrinfo(cctx->aitop);
+ }
memset(cctx, 0, sizeof(*cctx));
}
-/* Return CONNECTING channel to remote host, port */
+/* Return CONNECTING channel to remote host:port or local socket path */
static Channel *
-connect_to(const char *host, u_short port, char *ctype, char *rname)
+connect_to(const char *name, int port, char *ctype, char *rname)
{
struct addrinfo hints;
int gaierr;
@@ -3381,23 +3670,51 @@ connect_to(const char *host, u_short port, char *ctype, char *rname)
Channel *c;
memset(&cctx, 0, sizeof(cctx));
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = IPv4or6;
- hints.ai_socktype = SOCK_STREAM;
- snprintf(strport, sizeof strport, "%d", port);
- if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) {
- error("connect_to %.100s: unknown host (%s)", host,
- ssh_gai_strerror(gaierr));
- return NULL;
+
+ if (port == PORT_STREAMLOCAL) {
+ struct sockaddr_un *sunaddr;
+ struct addrinfo *ai;
+
+ if (strlen(name) > sizeof(sunaddr->sun_path)) {
+ error("%.100s: %.100s", name, strerror(ENAMETOOLONG));
+ return (NULL);
+ }
+
+ /*
+ * Fake up a struct addrinfo for AF_UNIX connections.
+ * channel_connect_ctx_free() must check ai_family
+ * and use free() not freeaddirinfo() for AF_UNIX.
+ */
+ ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr));
+ memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr));
+ ai->ai_addr = (struct sockaddr *)(ai + 1);
+ ai->ai_addrlen = sizeof(*sunaddr);
+ ai->ai_family = AF_UNIX;
+ ai->ai_socktype = SOCK_STREAM;
+ ai->ai_protocol = PF_UNSPEC;
+ sunaddr = (struct sockaddr_un *)ai->ai_addr;
+ sunaddr->sun_family = AF_UNIX;
+ strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path));
+ cctx.aitop = ai;
+ } else {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = IPv4or6;
+ hints.ai_socktype = SOCK_STREAM;
+ snprintf(strport, sizeof strport, "%d", port);
+ if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) != 0) {
+ error("connect_to %.100s: unknown host (%s)", name,
+ ssh_gai_strerror(gaierr));
+ return NULL;
+ }
}
- cctx.host = xstrdup(host);
+ cctx.host = xstrdup(name);
cctx.port = port;
cctx.ai = cctx.aitop;
if ((sock = connect_next(&cctx)) == -1) {
error("connect to %.100s port %d failed: %s",
- host, port, strerror(errno));
+ name, port, strerror(errno));
channel_connect_ctx_free(&cctx);
return NULL;
}
@@ -3414,7 +3731,7 @@ channel_connect_by_listen_address(const char *listen_host,
int i;
for (i = 0; i < num_permitted_opens; i++) {
- if (open_listen_match(&permitted_opens[i], listen_host,
+ if (open_listen_match_tcpip(&permitted_opens[i], listen_host,
listen_port, 1)) {
return connect_to(
permitted_opens[i].host_to_connect,
@@ -3426,9 +3743,26 @@ channel_connect_by_listen_address(const char *listen_host,
return NULL;
}
+Channel *
+channel_connect_by_listen_path(const char *path, char *ctype, char *rname)
+{
+ int i;
+
+ for (i = 0; i < num_permitted_opens; i+