=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/channels.c,v retrieving revision 1.105 retrieving revision 1.106 diff -u -r1.105 -r1.106 --- src/usr.bin/ssh/channels.c 2001/04/10 12:15:23 1.105 +++ src/usr.bin/ssh/channels.c 2001/04/11 13:56:13 1.106 @@ -40,7 +40,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.105 2001/04/10 12:15:23 markus Exp $"); +RCSID("$OpenBSD: channels.c,v 1.106 2001/04/11 13:56:13 markus Exp $"); #include #include @@ -576,6 +576,39 @@ return 0; /* need more */ } +/* try to decode a http connect header */ +int +channel_decode_https(Channel *c, fd_set * readset, fd_set * writeset) +{ + u_char *p, *host, *buf; + int port, ret; + char httpok[] = "HTTP/1.0 200\r\n\r\n"; + + debug2("channel %d: decode https connect", c->self); + ret = channel_decode_helper(c, strlen("connect "), '\r'); + if (ret <= 0) + return ret; + p = buffer_ptr(&c->input); + buf = xmalloc(ret+1); + host = xmalloc(ret); + memcpy(buf, p, ret); + buf[ret] = '\0'; + if (sscanf(buf, "CONNECT %[^:]:%u HTTP/", host, &port) != 2) { + debug("channel %d: cannot parse http header", c->self); + return -1; + } + debug("channel %d: dynamic request: https host %s port %u", + c->self, host, port); + strlcpy(c->path, host, sizeof(c->path)); + c->host_port = port; + xfree(host); + xfree(buf); + buffer_consume(&c->input, ret+4); + buffer_append(&c->output, httpok, strlen(httpok)); + + return 1; +} + /* try to decode a socks4 header */ int channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) @@ -601,7 +634,7 @@ 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); + debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); if (len > have) fatal("channel %d: decode socks4: len %d > have %d", c->self, len, have); @@ -613,10 +646,8 @@ strlcpy(c->path, host, sizeof(c->path)); c->host_port = ntohs(s4_req.dest_port); - debug("channel %d: dynamic request: " - "socks%x://%s@%s:%u/command?%u", - c->self, s4_req.version, username, host, c->host_port, - s4_req.command); + debug("channel %d: dynamic request: socks4 host %s port %u command %u", + c->self, host, c->host_port, s4_req.command); if (s4_req.command != 1) { debug("channel %d: cannot handle: socks4 cn %d", @@ -631,6 +662,115 @@ 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 + +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)); + 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 */ void channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) @@ -651,17 +791,15 @@ /* try to guess the protocol */ p = buffer_ptr(&c->input); switch (p[0]) { - case 0x04: - ret = channel_decode_socks4(c, readset, writeset); - break; -#if 0 case 'C': ret = channel_decode_https(c, readset, writeset); break; + case 0x04: + ret = channel_decode_socks4(c, readset, writeset); + break; case 0x05: ret = channel_decode_socks5(c, readset, writeset); break; -#endif default: ret = -1; break;