Always resolve localhost to loopback address

This always resolves "localhost" to a loopback address which
has security benefits such as preventing a malicious dns server
redirecting local connections and allows software to assume
it is a secure hostname.

This is being adopted by web browsers:

- https://w3c.github.io/webappsec-secure-contexts/
- https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/RC9dSw-O3fE/E3_0XaT0BAAJ
- 8da2a80724
- https://bugs.webkit.org/show_bug.cgi?id=171934
- https://tools.ietf.org/html/draft-west-let-localhost-be-localhost-06
This commit is contained in:
Patrick Griffis
2019-02-05 08:53:44 -05:00
committed by Philip Withnall
parent 01acb8907f
commit ea99872e45
3 changed files with 257 additions and 5 deletions

View File

@@ -112,6 +112,12 @@ do_lookup_by_name (GTask *task,
else
g_task_return_pointer (task, g_list_copy_deep (self->ipv6_results, copy_object, NULL), NULL);
}
else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT)
{
/* This is only the minimal implementation needed for some tests */
g_assert (self->ipv4_error == NULL && self->ipv6_error == NULL && self->ipv6_results == NULL);
g_task_return_pointer (task, g_list_copy_deep (self->ipv4_results, copy_object, NULL), NULL);
}
else
g_assert_not_reached ();
}
@@ -130,6 +136,22 @@ lookup_by_name_with_flags_async (GResolver *resolver,
g_object_unref (task);
}
static GList *
lookup_by_name (GResolver *resolver,
const gchar *hostname,
GCancellable *cancellable,
GError **error)
{
GList *result = NULL;
GTask *task = g_task_new (resolver, cancellable, NULL, NULL);
g_task_set_task_data (task, GUINT_TO_POINTER (G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT), NULL);
g_task_run_in_thread_sync (task, do_lookup_by_name);
result = g_task_propagate_pointer (task, error);
g_object_unref (task);
return result;
}
static GList *
lookup_by_name_with_flags_finish (GResolver *resolver,
GAsyncResult *result,
@@ -160,6 +182,7 @@ mock_resolver_class_init (MockResolverClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
resolver_class->lookup_by_name_with_flags_async = lookup_by_name_with_flags_async;
resolver_class->lookup_by_name_with_flags_finish = lookup_by_name_with_flags_finish;
resolver_class->lookup_by_name = lookup_by_name;
object_class->finalize = mock_resolver_finalize;
}

View File

@@ -418,6 +418,131 @@ test_loopback_sync (void)
g_object_unref (addr);
}
static void
test_localhost_sync (void)
{
GSocketConnectable *addr; /* owned */
GSocketAddressEnumerator *enumerator; /* owned */
GSocketAddress *a; /* owned */
GError *error = NULL;
GResolver *original_resolver; /* owned */
MockResolver *mock_resolver; /* owned */
GList *ipv4_results = NULL; /* owned */
/* This test ensures that variations of the "localhost" hostname always resolve to a loopback address */
/* Set up a DNS resolver that returns nonsense for "localhost" */
original_resolver = g_resolver_get_default ();
mock_resolver = mock_resolver_new ();
g_resolver_set_default (G_RESOLVER (mock_resolver));
ipv4_results = g_list_append (ipv4_results, g_inet_address_new_from_string ("123.123.123.123"));
mock_resolver_set_ipv4_results (mock_resolver, ipv4_results);
addr = g_network_address_new ("localhost.", 616);
enumerator = g_socket_connectable_enumerate (addr);
/* IPv6 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "::1", 616);
g_object_unref (a);
/* IPv4 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "127.0.0.1", 616);
g_object_unref (a);
/* End of results. */
g_assert_null (g_socket_address_enumerator_next (enumerator, NULL, &error));
g_assert_no_error (error);
g_object_unref (enumerator);
g_object_unref (addr);
addr = g_network_address_new (".localhost", 616);
enumerator = g_socket_connectable_enumerate (addr);
/* IPv6 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "::1", 616);
g_object_unref (a);
/* IPv4 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "127.0.0.1", 616);
g_object_unref (a);
/* End of results. */
g_assert_null (g_socket_address_enumerator_next (enumerator, NULL, &error));
g_assert_no_error (error);
g_object_unref (enumerator);
g_object_unref (addr);
addr = g_network_address_new ("foo.localhost", 616);
enumerator = g_socket_connectable_enumerate (addr);
/* IPv6 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "::1", 616);
g_object_unref (a);
/* IPv4 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "127.0.0.1", 616);
g_object_unref (a);
/* End of results. */
g_assert_null (g_socket_address_enumerator_next (enumerator, NULL, &error));
g_assert_no_error (error);
g_object_unref (enumerator);
g_object_unref (addr);
addr = g_network_address_new (".localhost.", 616);
enumerator = g_socket_connectable_enumerate (addr);
/* IPv6 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "::1", 616);
g_object_unref (a);
/* IPv4 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "127.0.0.1", 616);
g_object_unref (a);
/* End of results. */
g_assert_null (g_socket_address_enumerator_next (enumerator, NULL, &error));
g_assert_no_error (error);
g_object_unref (enumerator);
g_object_unref (addr);
addr = g_network_address_new ("invalid", 616);
enumerator = g_socket_connectable_enumerate (addr);
/* IPv4 address. */
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
g_assert_no_error (error);
assert_socket_address_matches (a, "123.123.123.123", 616);
g_object_unref (a);
/* End of results. */
g_assert_null (g_socket_address_enumerator_next (enumerator, NULL, &error));
g_assert_no_error (error);
g_object_unref (enumerator);
g_object_unref (addr);
g_resolver_set_default (original_resolver);
g_list_free_full (ipv4_results, (GDestroyNotify) g_object_unref);
g_object_unref (original_resolver);
g_object_unref (mock_resolver);
}
typedef struct {
GList/*<owned GSocketAddress> */ *addrs; /* owned */
GMainLoop *loop; /* owned */
@@ -536,6 +661,51 @@ test_loopback_async (void)
g_object_unref (addr);
}
static void
test_localhost_async (void)
{
GSocketConnectable *addr; /* owned */
GSocketAddressEnumerator *enumerator; /* owned */
AsyncData data = { 0, };
GResolver *original_resolver; /* owned */
MockResolver *mock_resolver; /* owned */
GList *ipv4_results = NULL; /* owned */
/* This test ensures that variations of the "localhost" hostname always resolve to a loopback address */
/* Set up a DNS resolver that returns nonsense for "localhost" */
original_resolver = g_resolver_get_default ();
mock_resolver = mock_resolver_new ();
g_resolver_set_default (G_RESOLVER (mock_resolver));
ipv4_results = g_list_append (ipv4_results, g_inet_address_new_from_string ("123.123.123.123"));
mock_resolver_set_ipv4_results (mock_resolver, ipv4_results);
addr = g_network_address_new ("localhost", 610);
enumerator = g_socket_connectable_enumerate (addr);
/* Get all the addresses. */
data.addrs = NULL;
data.delay_ms = 1;
data.loop = g_main_loop_new (NULL, FALSE);
g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
g_main_loop_run (data.loop);
/* Check results. */
g_assert_cmpuint (g_list_length (data.addrs), ==, 2);
assert_socket_address_matches (data.addrs->data, "::1", 610);
assert_socket_address_matches (data.addrs->next->data, "127.0.0.1", 610);
g_resolver_set_default (original_resolver);
g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
g_list_free_full (ipv4_results, (GDestroyNotify) g_object_unref);
g_object_unref (original_resolver);
g_object_unref (mock_resolver);
g_object_unref (enumerator);
g_object_unref (addr);
g_main_loop_unref (data.loop);
}
static void
test_to_string (void)
{
@@ -1054,6 +1224,8 @@ main (int argc, char *argv[])
g_test_add_func ("/network-address/loopback/basic", test_loopback_basic);
g_test_add_func ("/network-address/loopback/sync", test_loopback_sync);
g_test_add_func ("/network-address/loopback/async", test_loopback_async);
g_test_add_func ("/network-address/localhost/async", test_localhost_async);
g_test_add_func ("/network-address/localhost/sync", test_localhost_sync);
g_test_add_func ("/network-address/to-string", test_to_string);
g_test_add ("/network-address/happy-eyeballs/basic", HappyEyeballsFixture, NULL,