summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkus Friedl <markus@cvs.openbsd.org>2001-04-07 08:55:19 +0000
committerMarkus Friedl <markus@cvs.openbsd.org>2001-04-07 08:55:19 +0000
commit4faf0362553f873a23fc2f65344b5809f216c3f1 (patch)
tree8b373795e9205291d607c0aea813f64ad1c24410
parente44c669b3f0e7d910f3c631106a03fe09ce51303 (diff)
allow the ssh client act as a SOCKS4 proxy (dynamic local portforwarding).
work by Dan Kaminsky <dankamin@cisco.com> and me. thanks to Dan for this great patch: use 'ssh -D 1080 host' and make netscape use localhost:1080 as a socks proxy.
-rw-r--r--usr.bin/ssh/buffer.c4
-rw-r--r--usr.bin/ssh/channels.c228
-rw-r--r--usr.bin/ssh/channels.h5
-rw-r--r--usr.bin/ssh/readconf.c17
-rw-r--r--usr.bin/ssh/ssh.c13
5 files changed, 213 insertions, 54 deletions
diff --git a/usr.bin/ssh/buffer.c b/usr.bin/ssh/buffer.c
index 68696fd3f74..377d0c09f40 100644
--- a/usr.bin/ssh/buffer.c
+++ b/usr.bin/ssh/buffer.c
@@ -12,7 +12,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: buffer.c,v 1.11 2001/04/05 21:02:46 markus Exp $");
+RCSID("$OpenBSD: buffer.c,v 1.12 2001/04/07 08:55:15 markus Exp $");
#include "xmalloc.h"
#include "buffer.h"
@@ -156,5 +156,5 @@ buffer_dump(Buffer *buffer)
for (i = buffer->offset; i < buffer->end; i++)
fprintf(stderr, " %02x", ucp[i]);
- fprintf(stderr, "\n");
+ fprintf(stderr, "\r\n");
}
diff --git a/usr.bin/ssh/channels.c b/usr.bin/ssh/channels.c
index 1585b960bf8..2d5b80a2cff 100644
--- a/usr.bin/ssh/channels.c
+++ b/usr.bin/ssh/channels.c
@@ -40,7 +40,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.102 2001/04/06 21:00:10 markus Exp $");
+RCSID("$OpenBSD: channels.c,v 1.103 2001/04/07 08:55:17 markus Exp $");
#include <openssl/rsa.h>
#include <openssl/dsa.h>
@@ -51,6 +51,7 @@ RCSID("$OpenBSD: channels.c,v 1.102 2001/04/06 21:00:10 markus Exp $");
#include "packet.h"
#include "xmalloc.h"
#include "buffer.h"
+#include "bufaux.h"
#include "uidswap.h"
#include "log.h"
#include "misc.h"
@@ -133,6 +134,8 @@ static int have_hostname_in_open = 0;
/* AF_UNSPEC or AF_INET or AF_INET6 */
extern int IPv4or6;
+void port_open_helper(Channel *c, char *rtype);
+
/* Sets specific protocol options. */
void
@@ -539,6 +542,89 @@ channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset)
}
}
+#define SSH_SOCKS_HEAD 1+1+2+4
+
+void
+channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
+{
+ u_char *p, *host;
+ int len, i, done, have;
+ char username[256];
+ struct {
+ u_int8_t version;
+ u_int8_t command;
+ u_int16_t dest_port;
+ struct in_addr dest_ip;
+ } s4_req, s4_rsp;
+
+ have = buffer_len(&c->input);
+
+ debug("channel %d: pre_dynamic have: %d", c->self, have);
+ /*buffer_dump(&c->input);*/
+
+ /* Check if the fixed size part of the packet is in buffer. */
+ if (have < SSH_SOCKS_HEAD + 1) {
+ /* need more */
+ FD_SET(c->sock, readset);
+ return;
+ }
+ /* Check for end of username */
+ p = buffer_ptr(&c->input);
+ done = 0;
+ for (i = SSH_SOCKS_HEAD; i < have; i++) {
+ if (p[i] == '\0') {
+ done = 1;
+ break;
+ }
+ }
+ if (!done) {
+ /* need more */
+ FD_SET(c->sock, readset);
+ return;
+ }
+ buffer_get(&c->input, (char *)&s4_req.version, 1);
+ buffer_get(&c->input, (char *)&s4_req.command, 1);
+ buffer_get(&c->input, (char *)&s4_req.dest_port, 2);
+ buffer_get(&c->input, (char *)&s4_req.dest_ip, 4);
+ p = buffer_ptr(&c->input);
+ len = strlen(p);
+ have = buffer_len(&c->input);
+ debug2("channel %d: pre_dynamic user: %s/%d", c->self, p, len);
+ if (len > have)
+ fatal("channel %d: pre_dynamic: len %d > have %d",
+ c->self, len, have);
+ strlcpy(username, p, sizeof(username));
+ buffer_consume(&c->input, len);
+ buffer_consume(&c->input, 1); /* trailing '\0' */
+
+ host = inet_ntoa(s4_req.dest_ip);
+ strlcpy(c->path, host, sizeof(c->path));
+ c->host_port = ntohs(s4_req.dest_port);
+
+ debug("channel %d: dynamic request received: "
+ "socks%x://%s@%s:%u/command?%u",
+ c->self, s4_req.version, username, host, c->host_port,
+ s4_req.command);
+
+ if ((s4_req.version != 4) || (s4_req.command != 1)) {
+ debug("channel %d: cannot handle: socks VN %d CN %d",
+ c->self, s4_req.version, s4_req.command);
+ channel_free(c->self);
+ return;
+ }
+
+ s4_rsp.version = 0; /* VN: version of reply code */
+ s4_rsp.command = 90; /* CD: request granted */
+ s4_rsp.dest_port = 0; /* ignored */
+ s4_rsp.dest_ip.s_addr = INADDR_ANY; /* ignored */
+ buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp));
+
+ /* switch to next state */
+ c->type = SSH_CHANNEL_OPENING;
+ port_open_helper(c, "direct-tcpip");
+}
+
+
/* This is our fake X11 server socket. */
void
channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
@@ -591,73 +677,100 @@ channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
}
}
+void
+port_open_helper(Channel *c, char *rtype)
+{
+ int direct;
+ char buf[1024];
+ char *remote_ipaddr = get_peer_ipaddr(c->sock);
+ u_short remote_port = get_peer_port(c->sock);
+
+ direct = (strcmp(rtype, "direct-tcpip") == 0);
+
+ snprintf(buf, sizeof buf,
+ "%s: listening port %d for %.100s port %d, "
+ "connect from %.200s port %d",
+ rtype, c->listening_port, c->path, c->host_port,
+ remote_ipaddr, remote_port);
+
+ xfree(c->remote_name);
+ c->remote_name = xstrdup(buf);
+
+ if (compat20) {
+ packet_start(SSH2_MSG_CHANNEL_OPEN);
+ packet_put_cstring(rtype);
+ packet_put_int(c->self);
+ packet_put_int(c->local_window_max);
+ packet_put_int(c->local_maxpacket);
+ if (direct) {
+ /* target host, port */
+ packet_put_cstring(c->path);
+ packet_put_int(c->host_port);
+ } else {
+ /* listen address, port */
+ packet_put_cstring(c->path);
+ packet_put_int(c->listening_port);
+ }
+ /* originator host and port */
+ packet_put_cstring(remote_ipaddr);
+ packet_put_int(remote_port);
+ packet_send();
+ } else {
+ packet_start(SSH_MSG_PORT_OPEN);
+ packet_put_int(c->self);
+ packet_put_cstring(c->path);
+ packet_put_int(c->host_port);
+ if (have_hostname_in_open)
+ packet_put_cstring(c->remote_name);
+ packet_send();
+ }
+ xfree(remote_ipaddr);
+}
+
/*
* This socket is listening for connections to a forwarded TCP/IP port.
*/
void
channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
{
+ Channel *nc;
struct sockaddr addr;
- int newsock, newch;
+ int newsock, newch, nextstate;
socklen_t addrlen;
- char buf[1024], *remote_ipaddr, *rtype;
- int remote_port;
-
- rtype = (c->type == SSH_CHANNEL_RPORT_LISTENER) ?
- "forwarded-tcpip" : "direct-tcpip";
+ char *rtype;
if (FD_ISSET(c->sock, readset)) {
debug("Connection to port %d forwarding "
"to %.100s port %d requested.",
c->listening_port, c->path, c->host_port);
+
+ rtype = (c->type == SSH_CHANNEL_RPORT_LISTENER) ?
+ "forwarded-tcpip" : "direct-tcpip";
+ nextstate = (c->host_port == 0) ? SSH_CHANNEL_DYNAMIC :
+ SSH_CHANNEL_OPENING;
+
addrlen = sizeof(addr);
newsock = accept(c->sock, &addr, &addrlen);
if (newsock < 0) {
error("accept: %.100s", strerror(errno));
return;
}
- remote_ipaddr = get_peer_ipaddr(newsock);
- remote_port = get_peer_port(newsock);
- snprintf(buf, sizeof buf,
- "listen port %d for %.100s port %d, "
- "connect from %.200s port %d",
- c->listening_port, c->path, c->host_port,
- remote_ipaddr, remote_port);
-
newch = channel_new(rtype,
- SSH_CHANNEL_OPENING, newsock, newsock, -1,
+ nextstate, newsock, newsock, -1,
c->local_window_max, c->local_maxpacket,
- 0, xstrdup(buf), 1);
- if (compat20) {
- packet_start(SSH2_MSG_CHANNEL_OPEN);
- packet_put_cstring(rtype);
- packet_put_int(newch);
- packet_put_int(c->local_window_max);
- packet_put_int(c->local_maxpacket);
- if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
- /* listen address, port */
- packet_put_string(c->path, strlen(c->path));
- packet_put_int(c->listening_port);
- } else {
- /* target host, port */
- packet_put_string(c->path, strlen(c->path));
- packet_put_int(c->host_port);
- }
- /* originator host and port */
- packet_put_cstring(remote_ipaddr);
- packet_put_int(remote_port);
- packet_send();
- } else {
- packet_start(SSH_MSG_PORT_OPEN);
- packet_put_int(newch);
- packet_put_string(c->path, strlen(c->path));
- packet_put_int(c->host_port);
- if (have_hostname_in_open) {
- packet_put_string(buf, strlen(buf));
- }
- packet_send();
+ 0, xstrdup(rtype), 1);
+
+ nc = channel_lookup(newch);
+ if (nc == NULL) {
+ error("xxx: no new channel:");
+ return;
}
- xfree(remote_ipaddr);
+ nc->listening_port = c->listening_port;
+ nc->host_port = c->host_port;
+ strlcpy(nc->path, c->path, sizeof(nc->path));
+
+ if (nextstate != SSH_CHANNEL_DYNAMIC)
+ port_open_helper(nc, rtype);
}
}
@@ -733,6 +846,15 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
if (len <= 0) {
debug("channel %d: read<=0 rfd %d len %d",
c->self, c->rfd, len);
+ if (c->type == SSH_CHANNEL_DYNAMIC) {
+ /*
+ * we are not yet connected to a remote peer,
+ * so the connection-close protocol won't work
+ */
+ debug("channel %d: dynamic: closed", c->self);
+ channel_free(c->self);
+ return -1;
+ }
if (compat13) {
buffer_consume(&c->output, buffer_len(&c->output));
c->type = SSH_CHANNEL_INPUT_DRAINING;
@@ -894,6 +1016,12 @@ channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
}
void
+channel_post_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
+{
+ channel_handle_rfd(c, readset, writeset);
+}
+
+void
channel_handler_init_20(void)
{
channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_20;
@@ -903,6 +1031,7 @@ channel_handler_init_20(void)
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;
+ channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic;
channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_2;
channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener;
@@ -910,6 +1039,7 @@ channel_handler_init_20(void)
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;
+ channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_dynamic;
}
void
@@ -923,6 +1053,7 @@ channel_handler_init_13(void)
channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining;
channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining;
channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting;
+ channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic;
channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1;
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener;
@@ -930,6 +1061,7 @@ channel_handler_init_13(void)
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13;
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
+ channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_dynamic;
}
void
@@ -941,12 +1073,14 @@ channel_handler_init_15(void)
channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener;
channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting;
+ channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic;
channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener;
channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener;
channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener;
channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1;
channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting;
+ channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_dynamic;
}
void
@@ -1500,6 +1634,7 @@ channel_still_open()
case SSH_CHANNEL_RPORT_LISTENER:
case SSH_CHANNEL_CLOSED:
case SSH_CHANNEL_AUTH_SOCKET:
+ case SSH_CHANNEL_DYNAMIC:
case SSH_CHANNEL_CONNECTING: /* XXX ??? */
continue;
case SSH_CHANNEL_LARVAL:
@@ -1551,6 +1686,7 @@ channel_open_message()
case SSH_CHANNEL_LARVAL:
case SSH_CHANNEL_OPENING:
case SSH_CHANNEL_CONNECTING:
+ case SSH_CHANNEL_DYNAMIC:
case SSH_CHANNEL_OPEN:
case SSH_CHANNEL_X11_OPEN:
case SSH_CHANNEL_INPUT_DRAINING:
diff --git a/usr.bin/ssh/channels.h b/usr.bin/ssh/channels.h
index 2cd82148e55..23e6ece839d 100644
--- a/usr.bin/ssh/channels.h
+++ b/usr.bin/ssh/channels.h
@@ -32,7 +32,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/* RCSID("$OpenBSD: channels.h,v 1.29 2001/04/04 20:25:36 markus Exp $"); */
+/* RCSID("$OpenBSD: channels.h,v 1.30 2001/04/07 08:55:17 markus Exp $"); */
#ifndef CHANNELS_H
#define CHANNELS_H
@@ -53,7 +53,8 @@
#define SSH_CHANNEL_LARVAL 10 /* larval session */
#define SSH_CHANNEL_RPORT_LISTENER 11 /* Listening to a R-style port */
#define SSH_CHANNEL_CONNECTING 12
-#define SSH_CHANNEL_MAX_TYPE 13
+#define SSH_CHANNEL_DYNAMIC 13
+#define SSH_CHANNEL_MAX_TYPE 14
/*
* Data structure for channel data. This is iniailized in channel_allocate
diff --git a/usr.bin/ssh/readconf.c b/usr.bin/ssh/readconf.c
index e1d4f28dc8d..f95940c1eb9 100644
--- a/usr.bin/ssh/readconf.c
+++ b/usr.bin/ssh/readconf.c
@@ -12,7 +12,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: readconf.c,v 1.70 2001/04/02 14:20:23 stevesk Exp $");
+RCSID("$OpenBSD: readconf.c,v 1.71 2001/04/07 08:55:17 markus Exp $");
#include "ssh.h"
#include "xmalloc.h"
@@ -110,7 +110,7 @@ typedef enum {
oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs,
oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
- oPreferredAuthentications
+ oDynamicForward, oPreferredAuthentications
} OpCodes;
/* Textual representations of the tokens. */
@@ -172,6 +172,7 @@ static struct {
{ "keepalive", oKeepAlives },
{ "numberofpasswordprompts", oNumberOfPasswordPrompts },
{ "loglevel", oLogLevel },
+ { "dynamicforward", oDynamicForward },
{ "preferredauthentications", oPreferredAuthentications },
{ NULL, 0 }
};
@@ -581,6 +582,18 @@ parse_int:
add_local_forward(options, fwd_port, buf, fwd_host_port);
break;
+ case oDynamicForward:
+ arg = strdelim(&s);
+ if (!arg || *arg == '\0')
+ fatal("%.200s line %d: Missing port argument.",
+ filename, linenum);
+ if (arg[0] < '0' || arg[0] > '9')
+ fatal("%.200s line %d: Badly formatted port number.",
+ filename, linenum);
+ fwd_port = atoi(arg);
+ add_local_forward(options, fwd_port, "socks4", 0);
+ break;
+
case oHost:
*activep = 0;
while ((arg = strdelim(&s)) != NULL && *arg != '\0')
diff --git a/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c
index 92207378ebc..c6a38003772 100644
--- a/usr.bin/ssh/ssh.c
+++ b/usr.bin/ssh/ssh.c
@@ -39,7 +39,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: ssh.c,v 1.107 2001/04/06 21:00:13 markus Exp $");
+RCSID("$OpenBSD: ssh.c,v 1.108 2001/04/07 08:55:18 markus Exp $");
#include <openssl/evp.h>
#include <openssl/err.h>
@@ -170,6 +170,9 @@ usage(void)
fprintf(stderr, " -R listen-port:host:port Forward remote port to local address\n");
fprintf(stderr, " These cause %s to listen for connections on a port, and\n", __progname);
fprintf(stderr, " forward them to the other side by connecting to host:port.\n");
+ fprintf(stderr, " -D port Dynamically forward local port to multiple remote addresses.\n");
+ fprintf(stderr, " Allows SSH to act as an application-layer proxy.\n");
+ fprintf(stderr, " Protocols Supported: SOCKS4\n");
fprintf(stderr, " -C Enable compression.\n");
fprintf(stderr, " -N Do not execute a shell or command.\n");
fprintf(stderr, " -g Allow remote hosts to connect to forwarded ports.\n");
@@ -301,7 +304,7 @@ main(int ac, char **av)
opt = av[optind][1];
if (!opt)
usage();
- if (strchr("eilcmpLRo", opt)) { /* options with arguments */
+ if (strchr("eilcmpLRDo", opt)) { /* options with arguments */
optarg = av[optind] + 2;
if (strcmp(optarg, "") == 0) {
if (optind >= ac - 1)
@@ -467,6 +470,12 @@ main(int ac, char **av)
}
add_local_forward(&options, fwd_port, buf, fwd_host_port);
break;
+
+ case 'D':
+ fwd_port = atoi(optarg);
+ add_local_forward(&options, fwd_port, "socks4", 0);
+ break;
+
case 'C':
options.compression = 1;
break;