Merge branch 'bugs25036_25055_clean_033' into maint-0.3.3

This commit is contained in:
Nick Mathewson 2018-03-28 07:49:34 -04:00
commit d416e208e4
6 changed files with 138 additions and 40 deletions

7
changes/bugs_25036_25055 Normal file
View File

@ -0,0 +1,7 @@
o Minor bugfixes (networking):
- Tor will not reject IPv6 address strings from TorBrowser when they
are passed as hostnames in SOCKS5 requests. Fixes bug 25036,
bugfix on Tor 0.3.1.2.
- string_is_valid_hostname() will not consider IP strings to be valid
hostnames. Fixes bug 25055; bugfix on Tor 0.2.5.5.

View File

@ -1071,6 +1071,36 @@ string_is_valid_ipv6_address(const char *string)
return (tor_inet_pton(AF_INET6,string,&addr) == 1); return (tor_inet_pton(AF_INET6,string,&addr) == 1);
} }
/** Return true iff <b>string</b> is a valid destination address,
* i.e. either a DNS hostname or IPv4/IPv6 address string.
*/
int
string_is_valid_dest(const char *string)
{
char *tmp = NULL;
int retval;
size_t len;
if (string == NULL)
return 0;
len = strlen(string);
if (len == 0)
return 0;
if (string[0] == '[' && string[len - 1] == ']')
string = tmp = tor_strndup(string + 1, len - 2);
retval = string_is_valid_ipv4_address(string) ||
string_is_valid_ipv6_address(string) ||
string_is_valid_nonrfc_hostname(string);
tor_free(tmp);
return retval;
}
/** Return true iff <b>string</b> matches a pattern of DNS names /** Return true iff <b>string</b> matches a pattern of DNS names
* that we allow Tor clients to connect to. * that we allow Tor clients to connect to.
* *
@ -1078,37 +1108,51 @@ string_is_valid_ipv6_address(const char *string)
* with misconfigured zones that have been encountered in the wild. * with misconfigured zones that have been encountered in the wild.
*/ */
int int
string_is_valid_hostname(const char *string) string_is_valid_nonrfc_hostname(const char *string)
{ {
int result = 1; int result = 1;
int has_trailing_dot;
char *last_label;
smartlist_t *components; smartlist_t *components;
if (!string || strlen(string) == 0)
return 0;
if (string_is_valid_ipv4_address(string))
return 0;
components = smartlist_new(); components = smartlist_new();
smartlist_split_string(components,string,".",0,0); smartlist_split_string(components,string,".",0,0);
if (BUG(smartlist_len(components) == 0))
return 0; // LCOV_EXCL_LINE should be impossible given the earlier checks.
/* Allow a single terminating '.' used rarely to indicate domains
* are FQDNs rather than relative. */
last_label = (char *)smartlist_get(components,
smartlist_len(components) - 1);
has_trailing_dot = (last_label[0] == '\0');
if (has_trailing_dot) {
smartlist_pop_last(components);
tor_free(last_label);
last_label = NULL;
}
SMARTLIST_FOREACH_BEGIN(components, char *, c) { SMARTLIST_FOREACH_BEGIN(components, char *, c) {
if ((c[0] == '-') || (*c == '_')) { if ((c[0] == '-') || (*c == '_')) {
result = 0; result = 0;
break; break;
} }
/* Allow a single terminating '.' used rarely to indicate domains
* are FQDNs rather than relative. */
if ((c_sl_idx > 0) && (c_sl_idx + 1 == c_sl_len) && !*c) {
continue;
}
do { do {
if ((*c >= 'a' && *c <= 'z') || result = (TOR_ISALNUM(*c) || (*c == '-') || (*c == '_'));
(*c >= 'A' && *c <= 'Z') || c++;
(*c >= '0' && *c <= '9') ||
(*c == '-') || (*c == '_'))
c++;
else
result = 0;
} while (result && *c); } while (result && *c);
if (result == 0) {
break;
}
} SMARTLIST_FOREACH_END(c); } SMARTLIST_FOREACH_END(c);
SMARTLIST_FOREACH_BEGIN(components, char *, c) { SMARTLIST_FOREACH_BEGIN(components, char *, c) {

View File

@ -233,7 +233,8 @@ const char *find_str_at_start_of_line(const char *haystack,
const char *needle); const char *needle);
int string_is_C_identifier(const char *string); int string_is_C_identifier(const char *string);
int string_is_key_value(int severity, const char *string); int string_is_key_value(int severity, const char *string);
int string_is_valid_hostname(const char *string); int string_is_valid_dest(const char *string);
int string_is_valid_nonrfc_hostname(const char *string);
int string_is_valid_ipv4_address(const char *string); int string_is_valid_ipv4_address(const char *string);
int string_is_valid_ipv6_address(const char *string); int string_is_valid_ipv6_address(const char *string);

View File

@ -393,7 +393,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
req->port = ntohs(get_uint16(data+5+len)); req->port = ntohs(get_uint16(data+5+len));
*drain_out = 5+len+2; *drain_out = 5+len+2;
if (!string_is_valid_hostname(req->address)) { if (!string_is_valid_dest(req->address)) {
socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
log_warn(LD_PROTOCOL, log_warn(LD_PROTOCOL,
@ -518,7 +518,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
log_debug(LD_APP,"socks4: Everything is here. Success."); log_debug(LD_APP,"socks4: Everything is here. Success.");
strlcpy(req->address, startaddr ? startaddr : tmpbuf, strlcpy(req->address, startaddr ? startaddr : tmpbuf,
sizeof(req->address)); sizeof(req->address));
if (!string_is_valid_hostname(req->address)) { if (!string_is_valid_dest(req->address)) {
log_warn(LD_PROTOCOL, log_warn(LD_PROTOCOL,
"Your application (using socks4 to port %d) gave Tor " "Your application (using socks4 to port %d) gave Tor "
"a malformed hostname: %s. Rejecting the connection.", "a malformed hostname: %s. Rejecting the connection.",

View File

@ -347,17 +347,35 @@ test_socks_5_supported_commands(void *ptr)
socks_request_clear(socks); socks_request_clear(socks);
/* SOCKS 5 should NOT reject RESOLVE [F0] reject for IPv6 address /* SOCKS 5 should NOT reject RESOLVE [F0] request for IPv6 address
* string if SafeSocks is enabled. */ * string if SafeSocks is enabled. */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF0\x00\x03\x29");
ADD_DATA(buf, "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]");
ADD_DATA(buf, "\x01\x02");
tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, 1),
OP_EQ, 1);
tt_str_op("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", OP_EQ,
socks->address);
tt_int_op(258, OP_EQ, socks->port);
tt_int_op(0, OP_EQ, buf_datalen(buf));
socks_request_clear(socks);
/* Also allow bracket-less form. */
ADD_DATA(buf, "\x05\x01\x00"); ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF0\x00\x03\x27"); ADD_DATA(buf, "\x05\xF0\x00\x03\x27");
ADD_DATA(buf, "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); ADD_DATA(buf, "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
ADD_DATA(buf, "\x01\x02"); ADD_DATA(buf, "\x01\x02");
tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, 1), tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, 1),
OP_EQ, -1); OP_EQ, 1);
tt_str_op("2001:0db8:85a3:0000:0000:8a2e:0370:7334", OP_EQ, socks->address); tt_str_op("2001:0db8:85a3:0000:0000:8a2e:0370:7334", OP_EQ,
socks->address);
tt_int_op(258, OP_EQ, socks->port); tt_int_op(258, OP_EQ, socks->port);
tt_int_op(0, OP_EQ, buf_datalen(buf)); tt_int_op(0, OP_EQ, buf_datalen(buf));

View File

@ -5541,48 +5541,75 @@ test_util_max_mem(void *arg)
; ;
} }
static void
test_util_dest_validation_edgecase(void *arg)
{
(void)arg;
tt_assert(!string_is_valid_dest(NULL));
tt_assert(!string_is_valid_dest(""));
done:
return;
}
static void static void
test_util_hostname_validation(void *arg) test_util_hostname_validation(void *arg)
{ {
(void)arg; (void)arg;
// Lets try valid hostnames first. // Lets try valid hostnames first.
tt_assert(string_is_valid_hostname("torproject.org")); tt_assert(string_is_valid_nonrfc_hostname("torproject.org"));
tt_assert(string_is_valid_hostname("ocw.mit.edu")); tt_assert(string_is_valid_nonrfc_hostname("ocw.mit.edu"));
tt_assert(string_is_valid_hostname("i.4cdn.org")); tt_assert(string_is_valid_nonrfc_hostname("i.4cdn.org"));
tt_assert(string_is_valid_hostname("stanford.edu")); tt_assert(string_is_valid_nonrfc_hostname("stanford.edu"));
tt_assert(string_is_valid_hostname("multiple-words-with-hypens.jp")); tt_assert(string_is_valid_nonrfc_hostname("multiple-words-with-hypens.jp"));
// Subdomain name cannot start with '-' or '_'. // Subdomain name cannot start with '-' or '_'.
tt_assert(!string_is_valid_hostname("-torproject.org")); tt_assert(!string_is_valid_nonrfc_hostname("-torproject.org"));
tt_assert(!string_is_valid_hostname("subdomain.-domain.org")); tt_assert(!string_is_valid_nonrfc_hostname("subdomain.-domain.org"));
tt_assert(!string_is_valid_hostname("-subdomain.domain.org")); tt_assert(!string_is_valid_nonrfc_hostname("-subdomain.domain.org"));
tt_assert(!string_is_valid_hostname("___abc.org")); tt_assert(!string_is_valid_nonrfc_hostname("___abc.org"));
// Hostnames cannot contain non-alphanumeric characters. // Hostnames cannot contain non-alphanumeric characters.
tt_assert(!string_is_valid_hostname("%%domain.\\org.")); tt_assert(!string_is_valid_nonrfc_hostname("%%domain.\\org."));
tt_assert(!string_is_valid_hostname("***x.net")); tt_assert(!string_is_valid_nonrfc_hostname("***x.net"));
tt_assert(!string_is_valid_hostname("\xff\xffxyz.org")); tt_assert(!string_is_valid_nonrfc_hostname("\xff\xffxyz.org"));
tt_assert(!string_is_valid_hostname("word1 word2.net")); tt_assert(!string_is_valid_nonrfc_hostname("word1 word2.net"));
// Test workaround for nytimes.com stupidity, technically invalid, // Test workaround for nytimes.com stupidity, technically invalid,
// but we allow it since they are big, even though they are failing to // but we allow it since they are big, even though they are failing to
// comply with a ~30 year old standard. // comply with a ~30 year old standard.
tt_assert(string_is_valid_hostname("core3_euw1.fabrik.nytimes.com")); tt_assert(string_is_valid_nonrfc_hostname("core3_euw1.fabrik.nytimes.com"));
// Firefox passes FQDNs with trailing '.'s directly to the SOCKS proxy, // Firefox passes FQDNs with trailing '.'s directly to the SOCKS proxy,
// which is redundant since the spec states DOMAINNAME addresses are fully // which is redundant since the spec states DOMAINNAME addresses are fully
// qualified. While unusual, this should be tollerated. // qualified. While unusual, this should be tollerated.
tt_assert(string_is_valid_hostname("core9_euw1.fabrik.nytimes.com.")); tt_assert(string_is_valid_nonrfc_hostname("core9_euw1.fabrik.nytimes.com."));
tt_assert(!string_is_valid_hostname("..washingtonpost.is.better.com")); tt_assert(!string_is_valid_nonrfc_hostname(
tt_assert(!string_is_valid_hostname("so.is..ft.com")); "..washingtonpost.is.better.com"));
tt_assert(!string_is_valid_hostname("...")); tt_assert(!string_is_valid_nonrfc_hostname("so.is..ft.com"));
tt_assert(!string_is_valid_nonrfc_hostname("..."));
// XXX: do we allow single-label DNS names? // XXX: do we allow single-label DNS names?
// We shouldn't for SOCKS (spec says "contains a fully-qualified domain name" // We shouldn't for SOCKS (spec says "contains a fully-qualified domain name"
// but only test pathologically malformed traling '.' cases for now. // but only test pathologically malformed traling '.' cases for now.
tt_assert(!string_is_valid_hostname(".")); tt_assert(!string_is_valid_nonrfc_hostname("."));
tt_assert(!string_is_valid_hostname("..")); tt_assert(!string_is_valid_nonrfc_hostname(".."));
// IP address strings are not hostnames.
tt_assert(!string_is_valid_nonrfc_hostname("8.8.8.8"));
tt_assert(!string_is_valid_nonrfc_hostname("[2a00:1450:401b:800::200e]"));
tt_assert(!string_is_valid_nonrfc_hostname("2a00:1450:401b:800::200e"));
// We allow alphanumeric TLDs. For discussion, see ticket #25055.
tt_assert(string_is_valid_nonrfc_hostname("lucky.13"));
tt_assert(string_is_valid_nonrfc_hostname("luck.y13"));
tt_assert(string_is_valid_nonrfc_hostname("luck.y13."));
// We allow punycode TLDs. For examples, see
// http://data.iana.org/TLD/tlds-alpha-by-domain.txt
tt_assert(string_is_valid_nonrfc_hostname("example.xn--l1acc"));
done: done:
return; return;
@ -6208,6 +6235,7 @@ struct testcase_t util_tests[] = {
&passthrough_setup, (void*)"1" }, &passthrough_setup, (void*)"1" },
UTIL_TEST(max_mem, 0), UTIL_TEST(max_mem, 0),
UTIL_TEST(hostname_validation, 0), UTIL_TEST(hostname_validation, 0),
UTIL_TEST(dest_validation_edgecase, 0),
UTIL_TEST(ipv4_validation, 0), UTIL_TEST(ipv4_validation, 0),
UTIL_TEST(writepid, 0), UTIL_TEST(writepid, 0),
UTIL_TEST(get_avail_disk_space, 0), UTIL_TEST(get_avail_disk_space, 0),