diff options
author | Markus Friedl <markus@cvs.openbsd.org> | 2003-07-02 14:51:17 +0000 |
---|---|---|
committer | Markus Friedl <markus@cvs.openbsd.org> | 2003-07-02 14:51:17 +0000 |
commit | 29ebacc0b66b26eb95385e94a5d114cf4b417a32 (patch) | |
tree | c35e3a180f3bc4dcb0771c75a6c447da95e25d65 /usr.bin/ssh/channels.c | |
parent | 6e14a59edc291fb2ea7f912bf4dfae58ef0e5b5d (diff) |
(re)add socks5 suppport to -D; ok djm@
now ssh(1) can act both as a socks 4 and socks 5 server and
dynamically forward ports.
Diffstat (limited to 'usr.bin/ssh/channels.c')
-rw-r--r-- | usr.bin/ssh/channels.c | 120 |
1 files changed, 117 insertions, 3 deletions
diff --git a/usr.bin/ssh/channels.c b/usr.bin/ssh/channels.c index 66e49d5ea59..48d3752f502 100644 --- a/usr.bin/ssh/channels.c +++ b/usr.bin/ssh/channels.c @@ -39,7 +39,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.192 2003/07/02 12:56:34 markus Exp $"); +RCSID("$OpenBSD: channels.c,v 1.193 2003/07/02 14:51:16 markus Exp $"); #include "ssh.h" #include "ssh1.h" @@ -54,7 +54,7 @@ RCSID("$OpenBSD: channels.c,v 1.192 2003/07/02 12:56:34 markus Exp $"); #include "key.h" #include "authfd.h" #include "pathnames.h" - +#include "bufaux.h" /* -- channel core */ @@ -940,6 +940,117 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) return 1; } +/* try to decode a socks5 header */ +#define SSH_SOCKS5_AUTHDONE 0x1000 +#define SSH_SOCKS5_NOAUTH 0x00 +#define SSH_SOCKS5_IPV4 0x01 +#define SSH_SOCKS5_DOMAIN 0x03 +#define SSH_SOCKS5_IPV6 0x04 +#define SSH_SOCKS5_CONNECT 0x01 +#define SSH_SOCKS5_SUCCESS 0x00 + +static int +channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) +{ + struct { + u_int8_t version; + u_int8_t command; + u_int8_t reserved; + u_int8_t atyp; + } s5_req, s5_rsp; + u_int16_t dest_port; + u_char *p, dest_addr[255+1]; + int i, have, found, nmethods, addrlen, af; + + debug2("channel %d: decode socks5", c->self); + p = buffer_ptr(&c->input); + if (p[0] != 0x05) + return -1; + have = buffer_len(&c->input); + if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { + /* format: ver | nmethods | methods */ + if (have < 2) + return 0; + nmethods = p[1]; + 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 ) { + found = 1; + break; + } + } + if (!found) { + debug("channel %d: method SSH_SOCKS5_NOAUTH not found", + c->self); + return -1; + } + buffer_consume(&c->input, nmethods + 2); + buffer_put_char(&c->output, 0x05); /* version */ + buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ + FD_SET(c->sock, writeset); + c->flags |= SSH_SOCKS5_AUTHDONE; + debug2("channel %d: socks5 auth done", c->self); + return 0; /* need more */ + } + 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)); + if (s5_req.version != 0x05 || + s5_req.command != SSH_SOCKS5_CONNECT || + s5_req.reserved != 0x00) { + debug("channel %d: only socks5 connect supported", c->self); + return -1; + } + switch(s5_req.atyp){ + case SSH_SOCKS5_IPV4: + addrlen = 4; + af = AF_INET; + break; + case SSH_SOCKS5_DOMAIN: + addrlen = p[sizeof(s5_req)]; + af = -1; + break; + case SSH_SOCKS5_IPV6: + addrlen = 16; + af = AF_INET6; + break; + default: + debug("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); + return -1; + } + if (have < 4 + addrlen + 2) + return 0; + buffer_consume(&c->input, sizeof(s5_req)); + if (s5_req.atyp == SSH_SOCKS5_DOMAIN) + buffer_consume(&c->input, 1); /* host string length */ + buffer_get(&c->input, (char *)&dest_addr, addrlen); + buffer_get(&c->input, (char *)&dest_port, 2); + dest_addr[addrlen] = '\0'; + if (s5_req.atyp == SSH_SOCKS5_DOMAIN) + strlcpy(c->path, dest_addr, sizeof(c->path)); + else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) + return -1; + c->host_port = ntohs(dest_port); + + debug("channel %d: dynamic request: socks5 host %s port %u command %u", + c->self, c->path, c->host_port, s5_req.command); + + s5_rsp.version = 0x05; + s5_rsp.command = SSH_SOCKS5_SUCCESS; + s5_rsp.reserved = 0; /* ignored */ + s5_rsp.atyp = SSH_SOCKS5_IPV4; + ((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)); + return 1; +} + /* dynamic port forwarding */ static void channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) @@ -952,7 +1063,7 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) debug2("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 < 4) { + if (have < 3) { /* need more */ FD_SET(c->sock, readset); return; @@ -963,6 +1074,9 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) case 0x04: ret = channel_decode_socks4(c, readset, writeset); break; + case 0x05: + ret = channel_decode_socks5(c, readset, writeset); + break; default: ret = -1; break; |