address/resolve: Require square brackets on IPv6 address:ports

When parsing addreses via Tor's internal address:port parsing and
DNS lookup APIs, require IPv6 addresses with ports to have square
brackets.

But allow IPv6 addresses without ports, whether or not they have
square brackets.

Fixes bug 30721; bugfix on 0.2.1.5-alpha.
This commit is contained in:
teor 2019-06-13 21:21:19 +10:00 committed by Nick Mathewson
parent 308d300213
commit adb8538e7b
3 changed files with 96 additions and 27 deletions

View File

@ -1,6 +1,10 @@
o Minor bugfixes (networking): o Minor bugfixes (networking, IP addresses):
- When parsing addreses via Tor's internal DNS lookup API, reject IPv4 - When parsing addreses via Tor's internal DNS lookup API, reject IPv4
addresses in square brackets, and accept IPv6 addresses in square addresses in square brackets, and accept IPv6 addresses in square
brackets. This change completes the work started in 23082, making brackets. This change completes the work started in 23082, making
address parsing consistent between tor's internal DNS lookup and address address parsing consistent between tor's internal DNS lookup and address
parsing APIs. Fixes bug 30721; bugfix on 0.2.1.5-alpha. parsing APIs. Fixes bug 30721; bugfix on 0.2.1.5-alpha.
- When parsing addreses via Tor's internal address:port parsing and
DNS lookup APIs, require IPv6 addresses with ports to have square
brackets. But allow IPv6 addresses without ports, whether or not they
have square brackets. Fixes bug 30721; bugfix on 0.2.1.5-alpha.

View File

@ -1188,27 +1188,28 @@ fmt_addr32(uint32_t addr)
} }
/** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string /** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string
* may be an IPv4 address, an IPv6 address, or an IPv6 address surrounded by * may be an IPv4 address, or an IPv6 address surrounded by square brackets.
* square brackets.
* *
* Return an address family on success, or -1 if an invalid address string is * If <b>allow_ipv6_without_brackets</b> is true, also allow IPv6 addresses
* without brackets.
*
* Always rejects IPv4 addresses with brackets.
*
* Returns an address family on success, or -1 if an invalid address string is
* provided. */ * provided. */
int static int
tor_addr_parse(tor_addr_t *addr, const char *src) tor_addr_parse_impl(tor_addr_t *addr, const char *src,
bool allow_ipv6_without_brackets)
{ {
/* Holds substring of IPv6 address after removing square brackets */ /* Holds substring of IPv6 address after removing square brackets */
char *tmp = NULL; char *tmp = NULL;
int result; int result = -1;
struct in_addr in_tmp; struct in_addr in_tmp;
struct in6_addr in6_tmp; struct in6_addr in6_tmp;
int brackets_detected = 0; int brackets_detected = 0;
tor_assert(addr && src); tor_assert(addr && src);
/* Clear the address before starting, to avoid returning uninitialised data.
*/
memset(addr, 0, sizeof(tor_addr_t));
size_t len = strlen(src); size_t len = strlen(src);
if (len && src[0] == '[' && src[len - 1] == ']') { if (len && src[0] == '[' && src[len - 1] == ']') {
@ -1216,21 +1217,46 @@ tor_addr_parse(tor_addr_t *addr, const char *src)
src = tmp = tor_strndup(src+1, strlen(src)-2); src = tmp = tor_strndup(src+1, strlen(src)-2);
} }
/* Try to parse an IPv6 address if it has brackets, or if IPv6 addresses
* without brackets are allowed */
if (brackets_detected || allow_ipv6_without_brackets) {
if (tor_inet_pton(AF_INET6, src, &in6_tmp) > 0) { if (tor_inet_pton(AF_INET6, src, &in6_tmp) > 0) {
result = AF_INET6; result = AF_INET6;
tor_addr_from_in6(addr, &in6_tmp); tor_addr_from_in6(addr, &in6_tmp);
} else if (!brackets_detected && }
tor_inet_pton(AF_INET, src, &in_tmp) > 0) { }
/* Try to parse an IPv4 address without brackets */
if (!brackets_detected) {
if (tor_inet_pton(AF_INET, src, &in_tmp) > 0) {
result = AF_INET; result = AF_INET;
tor_addr_from_in(addr, &in_tmp); tor_addr_from_in(addr, &in_tmp);
} else { }
result = -1; }
/* Clear the address on error, to avoid returning uninitialised or partly
* parsed data.
*/
if (result == -1) {
memset(addr, 0, sizeof(tor_addr_t));
} }
tor_free(tmp); tor_free(tmp);
return result; return result;
} }
/** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string
* may be an IPv4 address, an IPv6 address, or an IPv6 address surrounded by
* square brackets.
*
* Returns an address family on success, or -1 if an invalid address string is
* provided. */
int
tor_addr_parse(tor_addr_t *addr, const char *src)
{
return tor_addr_parse_impl(addr, src, 1);
}
#ifdef HAVE_IFADDRS_TO_SMARTLIST #ifdef HAVE_IFADDRS_TO_SMARTLIST
/* /*
* Convert a linked list consisting of <b>ifaddrs</b> structures * Convert a linked list consisting of <b>ifaddrs</b> structures
@ -1737,6 +1763,7 @@ tor_addr_port_parse(int severity, const char *addrport,
int retval = -1; int retval = -1;
int r; int r;
char *addr_tmp = NULL; char *addr_tmp = NULL;
bool has_port;
tor_assert(addrport); tor_assert(addrport);
tor_assert(address_out); tor_assert(address_out);
@ -1746,15 +1773,19 @@ tor_addr_port_parse(int severity, const char *addrport,
if (r < 0) if (r < 0)
goto done; goto done;
if (!*port_out) { has_port = !! *port_out;
/* If there's no port, use the default port, or fail if there is no default
*/
if (!has_port) {
if (default_port >= 0) if (default_port >= 0)
*port_out = default_port; *port_out = default_port;
else else
goto done; goto done;
} }
/* make sure that address_out is an IP address */ /* Make sure that address_out is an IP address.
if (tor_addr_parse(address_out, addr_tmp) < 0) * If there is no port in addrport, allow IPv6 addresses without brackets. */
if (tor_addr_parse_impl(address_out, addr_tmp, !has_port) < 0)
goto done; goto done;
retval = 0; retval = 0;

View File

@ -258,32 +258,66 @@ tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out)
uint16_t portval = 0; uint16_t portval = 0;
char *tmp = NULL; char *tmp = NULL;
int rv = 0; int rv = 0;
int result;
tor_assert(s); tor_assert(s);
tor_assert(addr_out); tor_assert(addr_out);
s = eat_whitespace(s); s = eat_whitespace(s);
/* Try parsing s as an address:port first, so we don't have to duplicate
* the logic that rejects IPv6:Port with no square brackets. */
rv = tor_addr_port_parse(LOG_WARN, s, &addr, &portval, 0);
/* That was easy, no DNS required. */
if (rv == 0)
goto success;
/* Now let's check for malformed IPv6 addresses and ports:
* tor_addr_port_parse() requires squared brackes if there is a port,
* and we want tor_addr_port_lookup() to have the same requirement.
* But we strip the port using tor_addr_port_split(), so tor_addr_lookup()
* only sees the address, and will accept it without square brackets. */
int family = tor_addr_parse(&addr, s);
/* If tor_addr_parse() succeeds where tor_addr_port_parse() failed, we need
* to reject this address as malformed. */
if (family >= 0) {
/* Double-check it's an IPv6 address. If not, we have a parsing bug.
*/
tor_assertf_nonfatal(family == AF_INET6,
"Wrong family: %d (should be IPv6: %d) which "
"failed IP:port parsing, but passed IP parsing. "
"input string: '%s'; parsed address: '%s'.",
family, AF_INET6, s, fmt_addr(&addr));
goto err;
}
/* Now we have a hostname. Let's split off the port, if any. */
rv = tor_addr_port_split(LOG_WARN, s, &tmp, &portval); rv = tor_addr_port_split(LOG_WARN, s, &tmp, &portval);
if (rv < 0) if (rv < 0)
goto err; goto err;
/* And feed the hostname to the lookup function. */
if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0) if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0)
goto err; goto err;
tor_free(tmp);
success:
if (port_out) if (port_out)
*port_out = portval; *port_out = portval;
tor_addr_copy(addr_out, &addr); tor_addr_copy(addr_out, &addr);
result = 0;
goto done;
return 0;
err: err:
/* Clear the address and port on error */ /* Clear the address and port on error */
memset(addr_out, 0, sizeof(tor_addr_t)); memset(addr_out, 0, sizeof(tor_addr_t));
if (port_out) if (port_out)
*port_out = 0; *port_out = 0;
result = -1;
/* We have set the result, now it's time to clean up */
done:
tor_free(tmp); tor_free(tmp);
return -1; return result;
} }
#ifdef USE_SANDBOX_GETADDRINFO #ifdef USE_SANDBOX_GETADDRINFO