From 0490e609f7681fe448faba3e97b79102667a7844 Mon Sep 17 00:00:00 2001 From: mouring Date: Sun, 8 Apr 2001 18:30:26 +0000 Subject: [PATCH] - markus@cvs.openbsd.org 2001/04/07 08:55:18 [buffer.c channels.c channels.h readconf.c ssh.c] allow the ssh client act as a SOCKS4 proxy (dynamic local portforwarding). work by Dan Kaminsky and me. thanks to Dan for this great patch: use 'ssh -D 1080 host' and make netscape use localhost:1080 as a socks proxy. --- ChangeLog | 6 ++ buffer.c | 4 +- channels.c | 228 ++++++++++++++++++++++++++++++++++++++++++----------- channels.h | 5 +- readconf.c | 17 +++- ssh.c | 13 ++- 6 files changed, 219 insertions(+), 54 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6649ae49..57cd282c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,6 +15,12 @@ do gid/groups-swap in addition to uid-swap, should help if /home/group is chmod 750 + chgrp grp /home/group/, work be deraadt and me, thanks to olar@openwall.com is comments. we had many requests for this. + - markus@cvs.openbsd.org 2001/04/07 08:55:18 + [buffer.c channels.c channels.h readconf.c ssh.c] + allow the ssh client act as a SOCKS4 proxy (dynamic local + portforwarding). work by Dan Kaminsky and me. + thanks to Dan for this great patch: use 'ssh -D 1080 host' and make + netscape use localhost:1080 as a socks proxy. 20010408 - OpenBSD CVS Sync diff --git a/buffer.c b/buffer.c index 68696fd3..377d0c09 100644 --- a/buffer.c +++ b/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/channels.c b/channels.c index d5526fb8..0e334db8 100644 --- a/channels.c +++ b/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 #include @@ -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; @@ -893,6 +1015,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) { @@ -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/channels.h b/channels.h index 2cd82148..23e6ece8 100644 --- a/channels.h +++ b/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/readconf.c b/readconf.c index 365f67eb..007056d4 100644 --- a/readconf.c +++ b/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 } }; @@ -583,6 +584,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/ssh.c b/ssh.c index 294bcf39..75094a10 100644 --- a/ssh.c +++ b/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 #include @@ -178,6 +178,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"); @@ -314,7 +317,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) @@ -480,6 +483,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; -- 2.45.2