[tor-commits] [tor/master] Second phase of SOCKS5

nickm at torproject.org nickm at torproject.org
Sun Jul 15 21:07:27 UTC 2018


commit bcbd3fb71e1a729f5b499a1452da7d07deeebf39
Author: rl1987 <rl1987 at sdf.lonestar.org>
Date:   Mon May 21 17:33:28 2018 +0200

    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 8fdb72235..84ea58778 100644
--- a/src/or/proto_socks.c
+++ b/src/or/proto_socks.c
@@ -454,10 +454,134 @@ process_socks5_userpass_auth(socks_request_t *req)
 }
 
 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 c650a5773..bdef21a0d 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 cf34e9d43..9645ea32a 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:





More information about the tor-commits mailing list