From bcbd3fb71e1a729f5b499a1452da7d07deeebf39 Mon Sep 17 00:00:00 2001 From: rl1987 Date: Mon, 21 May 2018 17:33:28 +0200 Subject: [PATCH] Second phase of SOCKS5 --- src/or/proto_socks.c | 157 +++++++++++++++++++++++++++++++++++--- src/or/socks_request_st.h | 2 + src/test/test_socks.c | 3 +- 3 files changed, 151 insertions(+), 11 deletions(-) diff --git a/src/or/proto_socks.c b/src/or/proto_socks.c index 8fdb722350..84ea58778e 100644 --- a/src/or/proto_socks.c +++ b/src/or/proto_socks.c @@ -453,11 +453,135 @@ process_socks5_userpass_auth(socks_request_t *req) return res; } +static int +parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req, + size_t datalen, size_t *drain_out) +{ + int res = 1; + tor_addr_t destaddr; + socks5_client_request_t *trunnel_req = NULL; + ssize_t parsed = socks5_client_request_parse(&trunnel_req, raw_data, datalen); + if (parsed == -1) { + log_warn(LD_APP, "socks5: parsing failed - invalid client request"); + res = -1; + goto end; + } else if (parsed == -2) { + res = 0; + goto end; + } + + tor_assert(parsed >= 0); + *drain_out = (size_t)parsed; + + if (socks5_client_request_get_version(trunnel_req) != 5) { + res = -1; + goto end; + } + + req->command = socks5_client_request_get_command(trunnel_req); + + req->port = socks5_client_request_get_dest_port(trunnel_req); + + uint8_t atype = socks5_client_request_get_atype(trunnel_req); + req->socks5_atyp = atype; + + switch (atype) { + case 1: { + uint32_t ipv4 = socks5_client_request_get_dest_addr_ipv4(trunnel_req); + tor_addr_from_ipv4h(&destaddr, ipv4); + + tor_addr_to_str(req->address, &destaddr, sizeof(req->address), 1); + } break; + case 3: { + const struct domainname_st *dns_name = + socks5_client_request_getconst_dest_addr_domainname(trunnel_req); + + const char *hostname = domainname_getconstarray_name(dns_name); + + strlcpy(req->address, hostname, sizeof(req->address)); + } break; + case 4: { + const char *ipv6 = + (const char *)socks5_client_request_getarray_dest_addr_ipv6(trunnel_req); + tor_addr_from_ipv6_bytes(&destaddr, ipv6); + + tor_addr_to_str(req->address, &destaddr, sizeof(req->address), 1); + } break; + default: { + res = -1; + } break; + } + + end: + socks5_client_request_free(trunnel_req); + return res; +} + +static int +process_socks5_client_request(socks_request_t *req, + int log_sockstype, + int safe_socks) +{ + int res = 1; + + if (req->command != SOCKS_COMMAND_CONNECT && + req->command != SOCKS_COMMAND_RESOLVE && + req->command != SOCKS_COMMAND_RESOLVE_PTR) { + socks_request_set_socks5_error(req,SOCKS5_COMMAND_NOT_SUPPORTED); + res = -1; + goto end; + } + + if (req->command == SOCKS_COMMAND_RESOLVE_PTR && + !string_is_valid_ipv4_address(req->address) && + !string_is_valid_ipv6_address(req->address)) { + socks_request_set_socks5_error(req, SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED); + log_warn(LD_APP, "socks5 received RESOLVE_PTR command with " + "hostname type. Rejecting."); + + res = -1; + goto end; + } + + if (!string_is_valid_dest(req->address)) { + socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); + + log_warn(LD_PROTOCOL, + "Your application (using socks5 to port %d) gave Tor " + "a malformed hostname: %s. Rejecting the connection.", + req->port, escaped_safe_str_client(req->address)); + + res = -1; + goto end;; + } + + if (req->socks5_atyp == 1 || req->socks5_atyp == 4) { + if (req->command != SOCKS_COMMAND_RESOLVE_PTR && + !addressmap_have_mapping(req->address,0)) { + log_unsafe_socks_warning(5, req->address, req->port, safe_socks); + if (safe_socks) { + socks_request_set_socks5_error(req, SOCKS5_NOT_ALLOWED); + res = -1; + goto end; + } + } + } + + if (log_sockstype) + log_notice(LD_APP, + "Your application (using socks5 to port %d) instructed " + "Tor to take care of the DNS resolution itself if " + "necessary. This is good.", req->port); + + end: + return res; +} + static int handle_socks_message(const uint8_t *raw_data, size_t datalen, socks_request_t *req, int log_sockstype, int safe_socks, size_t *drain_out) { - int res = 0; + int res = 1; uint8_t socks_version = raw_data[0]; @@ -497,8 +621,8 @@ handle_socks_message(const uint8_t *raw_data, size_t datalen, socks_request_t *r goto end; } /* RFC1929 SOCKS5 username/password subnegotiation. */ - if ((!req->got_auth && raw_data[0] == 1) || - req->auth_type == SOCKS_USER_PASS) { + if (!req->got_auth && (raw_data[0] == 1 || + req->auth_type == SOCKS_USER_PASS)) { int parse_status = parse_socks5_userpass_auth(raw_data, req, datalen, drain_out); @@ -540,6 +664,23 @@ handle_socks_message(const uint8_t *raw_data, size_t datalen, socks_request_t *r res = 0; goto end; + } else { + int parse_status = parse_socks5_client_request(raw_data, req, + datalen, drain_out); + if (parse_status != 1) { + socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); + res = parse_status; + goto end; + } + + int process_status = process_socks5_client_request(req, + log_sockstype, + safe_socks); + + if (process_status != 1) { + res = process_status; + goto end; + } } } else { *drain_out = datalen; @@ -590,11 +731,10 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req, goto end; } - buf_pullup(buf, datalen, &head, &datalen); // XXX - do { n_drain = 0; - //buf_pullup(buf, want_length, &head, &datalen); + buf_pullup(buf, MAX(want_length, buf_datalen(buf)), + &head, &datalen); tor_assert(head && datalen >= 2); want_length = 0; @@ -605,10 +745,7 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req, buf_clear(buf); else if (n_drain > 0) buf_drain(buf, n_drain); - - datalen = buf_datalen(buf); - } while (res == 0 && head && want_length < buf_datalen(buf) && - buf_datalen(buf) >= 2); + } while (res == 0 && head && buf_datalen(buf) >= 2); end: return res; diff --git a/src/or/socks_request_st.h b/src/or/socks_request_st.h index c650a57739..bdef21a0da 100644 --- a/src/or/socks_request_st.h +++ b/src/or/socks_request_st.h @@ -53,6 +53,8 @@ struct socks_request_t { /** The negotiated password value if any (for socks5). This value is NOT * nul-terminated; see passwordlen for its length. */ char *password; + + uint8_t socks5_atyp; /* SOCKS5 address type */ }; #endif diff --git a/src/test/test_socks.c b/src/test/test_socks.c index cf34e9d435..9645ea32a0 100644 --- a/src/test/test_socks.c +++ b/src/test/test_socks.c @@ -646,7 +646,8 @@ test_socks_5_malformed_commands(void *ptr) tt_int_op(5,OP_EQ,socks->socks_version); tt_int_op(10,OP_EQ,socks->replylen); tt_int_op(5,OP_EQ,socks->reply[0]); - tt_int_op(SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED,OP_EQ,socks->reply[1]); + /* trunnel parsing will fail with -1 */ + tt_int_op(SOCKS5_GENERAL_ERROR,OP_EQ,socks->reply[1]); tt_int_op(1,OP_EQ,socks->reply[3]); done: