Commit 15e368f1 authored by Jussi Laakkonen's avatar Jussi Laakkonen

Merge branch 'jb48769' into 'master'

[connman] Prevent IPv4 only VPN data and DNS leak to IPv6. Fixes JB#48769

See merge request mer-core/connman!257
parents 60dcd1b6 93f0a73d
......@@ -370,7 +370,7 @@ unit_test_service_SOURCES = $(backtrace_sources) $(gdhcp_sources) \
$(gweb_sources) $(shared_sources) \
unit/test-service.c \
src/connman.h src/log.c src/error.c src/task.c \
src/device.c src/network.c src/connection.c \
src/device.c src/network.c \
src/manager.c src/agent-connman.c src/clock.c \
src/timezone.c src/agent.c src/notifier.c \
src/resolver.c src/ipconfig.c src/detect.c \
......
......@@ -155,6 +155,8 @@ static const char *get_string(struct connman_provider *provider,
return data->host_ip[0];
} else if (g_str_equal(key, "VPN.Domain"))
return data->domain;
else if (g_str_equal(key, "Transport"))
return data->service_ident;
return g_hash_table_lookup(data->setting_strings, key);
}
......@@ -270,14 +272,12 @@ static bool provider_is_connected(struct connection_data *data)
static void set_provider_state(struct connection_data *data)
{
enum connman_provider_state state = CONNMAN_PROVIDER_STATE_UNKNOWN;
bool connected;
int err = 0;
DBG("provider %p new state %s", data->provider, data->state);
if (!provider_is_connected(data)) {
g_free(data->service_ident);
data->service_ident = NULL;
}
connected = provider_is_connected(data);
if (g_str_equal(data->state, "ready")) {
state = CONNMAN_PROVIDER_STATE_READY;
......@@ -297,7 +297,7 @@ static void set_provider_state(struct connection_data *data)
}
connman_provider_set_state(data->provider, state);
return;
goto free;
set:
if (data->cb_data)
......@@ -308,6 +308,12 @@ set:
free_config_cb_data(data->cb_data);
data->cb_data = NULL;
free:
if (!connected) {
g_free(data->service_ident);
data->service_ident = NULL;
}
}
static int create_provider(struct connection_data *data, void *user_data)
......@@ -1045,12 +1051,14 @@ static int disconnect_provider(struct connection_data *data)
dbus_pending_call_set_notify(data->disconnect_call, disconnect_reply,
data, NULL);
g_free(data->service_ident);
data->service_ident = NULL;
data->default_route_set = false;
connman_provider_set_state(data->provider,
CONNMAN_PROVIDER_STATE_DISCONNECT);
g_free(data->service_ident);
data->service_ident = NULL;
return -EINPROGRESS;
}
......
......@@ -435,8 +435,11 @@ void __connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
const struct connman_ipconfig_ops *ops);
int __connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
enum connman_ipconfig_method method);
void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig);
void __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig);
void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig,
bool forced);
int __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig,
bool forced);
int __connman_ipconfig_set_ipv6_support(bool enable);
int __connman_ipconfig_init(void);
void __connman_ipconfig_cleanup(void);
......@@ -705,7 +708,8 @@ int __connman_network_disconnect(struct connman_network *network);
int __connman_network_clear_ipconfig(struct connman_network *network,
struct connman_ipconfig *ipconfig);
int __connman_network_enable_ipconfig(struct connman_network *network,
struct connman_ipconfig *ipconfig);
struct connman_ipconfig *ipconfig,
bool force);
const char *__connman_network_get_type(struct connman_network *network);
const char *__connman_network_get_group(struct connman_network *network);
......@@ -761,6 +765,8 @@ void __connman_provider_list(DBusMessageIter *iter, void *user_data);
bool __connman_provider_is_immutable(struct connman_provider *provider);
int __connman_provider_create_and_connect(DBusMessage *msg);
const char * __connman_provider_get_ident(struct connman_provider *provider);
const char * __connman_provider_get_transport_ident(
struct connman_provider *provider);
int __connman_provider_indicate_state(struct connman_provider *provider,
enum connman_provider_state state);
int __connman_provider_indicate_error(struct connman_provider *provider,
......@@ -768,6 +774,8 @@ int __connman_provider_indicate_error(struct connman_provider *provider,
int __connman_provider_connect(struct connman_provider *provider,
const char *dbus_sender);
int __connman_provider_remove_by_path(const char *path);
int __connman_provider_toggle_transport_ipv6(struct connman_provider *provider,
bool disable);
void __connman_provider_cleanup(void);
int __connman_provider_init(void);
......@@ -805,6 +813,8 @@ struct connman_ipconfig *__connman_service_get_ipconfig(
struct connman_service *service, int family);
void __connman_service_notify_ipv4_configuration(
struct connman_service *service);
void __connman_service_notify_ipv6_configuration(
struct connman_service *service);
bool __connman_service_is_connected_state(struct connman_service *service,
enum connman_ipconfig_type type);
const char *__connman_service_get_path(struct connman_service *service);
......
......@@ -2569,8 +2569,6 @@ static struct server_data *create_server(int index,
{
struct server_data *data;
struct addrinfo hints, *rp;
struct connman_service *service;
GSList *vpn_index_list;
int ret;
DBG("index %d server %s", index, server);
......@@ -2645,9 +2643,6 @@ static struct server_data *create_server(int index,
return NULL;
}
service = connman_service_get_default();
vpn_index_list = __connman_service_get_depending_vpn_index(service);
if (protocol == IPPROTO_UDP) {
if (__connman_service_index_is_default(data->index) ||
__connman_service_index_is_split_routing(
......@@ -2656,19 +2651,11 @@ static struct server_data *create_server(int index,
DBG("Adding DNS server %s", data->server);
enable_fallback(false);
} else if (vpn_index_list && g_slist_index(vpn_index_list,
GINT_TO_POINTER(data->index)) != -1) {
data->enabled = true;
DBG("Adding DNS server of depending VPN %s",
data->server);
}
server_list = g_slist_append(server_list, data);
}
if (vpn_index_list)
g_slist_free(vpn_index_list);
return data;
}
......@@ -2896,7 +2883,7 @@ static void dnsproxy_default_changed(struct connman_service *service)
bool server_enabled = false;
GSList *list;
int index;
GSList *vpn_index_list;
int vpn_index;
DBG("service %p", service);
......@@ -2913,7 +2900,12 @@ static void dnsproxy_default_changed(struct connman_service *service)
if (index < 0)
return;
vpn_index_list = __connman_service_get_depending_vpn_index(service);
/*
* In case non-split-routed VPN is set as split routed the DNS servers
* the VPN must be enabled as well, when the transport becomes the
* default service.
*/
vpn_index = __connman_connection_get_vpn_index(index);
for (list = server_list; list; list = list->next) {
struct server_data *data = list->data;
......@@ -2922,10 +2914,8 @@ static void dnsproxy_default_changed(struct connman_service *service)
DBG("Enabling DNS server %s", data->server);
data->enabled = true;
server_enabled = true;
} else if (vpn_index_list && g_slist_index(vpn_index_list,
GINT_TO_POINTER(data->index)) != -1) {
DBG("Enabling DNS server of depending VPN %s",
data->server);
} else if (data->index == vpn_index) {
DBG("Enabling DNS server of VPN %s", data->server);
data->enabled = true;
} else {
DBG("Disabling DNS server %s", data->server);
......@@ -2933,9 +2923,6 @@ static void dnsproxy_default_changed(struct connman_service *service)
}
}
if (vpn_index_list)
g_slist_free(vpn_index_list);
if (!server_enabled)
enable_fallback(true);
......@@ -4031,4 +4018,9 @@ void __connman_dnsproxy_cleanup(void)
g_hash_table_destroy(listener_table);
g_hash_table_destroy(partial_tcp_req_table);
if (ipv4_resolve)
g_resolv_unref(ipv4_resolve);
if (ipv6_resolve)
g_resolv_unref(ipv6_resolve);
}
This diff is collapsed.
......@@ -45,6 +45,13 @@
#define WIFI_BSSID_LEN 6
#define WIFI_BSSID_STR_LEN 18
/*
* As per RFC 4861, a host should transmit up to MAX_RTR_SOLICITATIONS(3)
* Router Solicitation messages, each separated by at least
* RTR_SOLICITATION_INTERVAL(4) seconds to obtain RA for IPv6 auto-configuration.
*/
#define RTR_SOLICITATION_INTERVAL 4
static GSList *network_list = NULL;
static GSList *driver_list = NULL;
......@@ -275,7 +282,8 @@ static int set_connected_dhcp(struct connman_network *network)
}
static int manual_ipv6_set(struct connman_network *network,
struct connman_ipconfig *ipconfig_ipv6)
struct connman_ipconfig *ipconfig_ipv6,
bool force)
{
struct connman_service *service;
int err;
......@@ -289,7 +297,9 @@ static int manual_ipv6_set(struct connman_network *network,
if (!__connman_ipconfig_get_local(ipconfig_ipv6))
__connman_service_read_ip6config(service);
__connman_ipconfig_enable_ipv6(ipconfig_ipv6);
err = __connman_ipconfig_enable_ipv6(ipconfig_ipv6, force);
if (err)
return err;
err = __connman_ipconfig_address_add(ipconfig_ipv6);
if (err < 0) {
......@@ -367,7 +377,7 @@ err:
return err;
}
static void autoconf_ipv6_set(struct connman_network *network);
static void autoconf_ipv6_set(struct connman_network *network, bool force);
static void dhcpv6_callback(struct connman_network *network,
enum __connman_dhcpv6_status status, gpointer data);
......@@ -389,7 +399,7 @@ static void dhcpv6_renew_callback(struct connman_network *network,
stop_dhcpv6(network);
/* restart and do solicit again. */
autoconf_ipv6_set(network);
autoconf_ipv6_set(network, false);
break;
}
}
......@@ -415,7 +425,7 @@ static void dhcpv6_callback(struct connman_network *network,
} else if (status == CONNMAN_DHCPV6_STATUS_RESTART) {
stop_dhcpv6(network);
autoconf_ipv6_set(network);
autoconf_ipv6_set(network, false);
} else
stop_dhcpv6(network);
}
......@@ -438,7 +448,7 @@ static void check_dhcpv6(struct nd_router_advert *reply,
DBG("re-send router solicitation %d",
network->router_solicit_count);
network->router_solicit_count--;
__connman_inet_ipv6_send_rs(network->index, 1,
__connman_inet_ipv6_send_rs(network->index, RTR_SOLICITATION_INTERVAL,
check_dhcpv6, network);
return;
}
......@@ -551,7 +561,7 @@ int __connman_network_refresh_rs_ipv6(struct connman_network *network,
return ret;
}
static void autoconf_ipv6_set(struct connman_network *network)
static void autoconf_ipv6_set(struct connman_network *network, bool force)
{
struct connman_service *service;
struct connman_ipconfig *ipconfig;
......@@ -581,7 +591,8 @@ static void autoconf_ipv6_set(struct connman_network *network)
__connman_ipconfig_enable(ipconfig);
__connman_ipconfig_enable_ipv6(ipconfig);
if (__connman_ipconfig_enable_ipv6(ipconfig, force))
return;
__connman_ipconfig_address_remove(ipconfig);
......@@ -591,7 +602,8 @@ static void autoconf_ipv6_set(struct connman_network *network)
/* Try to get stateless DHCPv6 information, RFC 3736 */
network->router_solicit_count = 3;
__connman_inet_ipv6_send_rs(index, 1, check_dhcpv6, network);
__connman_inet_ipv6_send_rs(index, RTR_SOLICITATION_INTERVAL,
check_dhcpv6, network);
}
static void set_connected(struct connman_network *network)
......@@ -614,8 +626,8 @@ static void set_connected(struct connman_network *network)
DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4,
ipconfig_ipv6);
__connman_network_enable_ipconfig(network, ipconfig_ipv4);
__connman_network_enable_ipconfig(network, ipconfig_ipv6);
__connman_network_enable_ipconfig(network, ipconfig_ipv4, false);
__connman_network_enable_ipconfig(network, ipconfig_ipv6, false);
}
static void set_disconnected(struct connman_network *network)
......@@ -722,8 +734,8 @@ static void set_disconnected(struct connman_network *network)
* automagically.
*/
if (ipv6_method == CONNMAN_IPCONFIG_METHOD_AUTO) {
__connman_ipconfig_disable_ipv6(ipconfig_ipv6);
__connman_ipconfig_enable_ipv6(ipconfig_ipv6);
__connman_ipconfig_disable_ipv6(ipconfig_ipv6, false);
__connman_ipconfig_enable_ipv6(ipconfig_ipv6, false);
}
}
......@@ -1473,10 +1485,10 @@ int __connman_network_connect(struct connman_network *network)
if (!network->device)
return -ENODEV;
network->connecting = true;
__connman_device_disconnect(network->device);
network->connecting = true;
err = network->driver->connect(network);
if (err < 0) {
if (err == -EINPROGRESS)
......@@ -1567,7 +1579,7 @@ int __connman_network_clear_ipconfig(struct connman_network *network,
}
int __connman_network_enable_ipconfig(struct connman_network *network,
struct connman_ipconfig *ipconfig)
struct connman_ipconfig *ipconfig, bool force)
{
int r = 0;
enum connman_ipconfig_type type;
......@@ -1595,19 +1607,20 @@ int __connman_network_enable_ipconfig(struct connman_network *network,
break;
case CONNMAN_IPCONFIG_METHOD_OFF:
__connman_ipconfig_disable_ipv6(ipconfig);
__connman_ipconfig_disable_ipv6(ipconfig, force);
break;
case CONNMAN_IPCONFIG_METHOD_AUTO:
autoconf_ipv6_set(network);
autoconf_ipv6_set(network, force);
break;
case CONNMAN_IPCONFIG_METHOD_FIXED:
case CONNMAN_IPCONFIG_METHOD_MANUAL:
r = manual_ipv6_set(network, ipconfig);
r = manual_ipv6_set(network, ipconfig, force);
break;
case CONNMAN_IPCONFIG_METHOD_DHCP:
__connman_ipconfig_enable_ipv6(ipconfig, force);
r = -ENOSYS;
break;
}
......
......@@ -47,6 +47,7 @@ struct connman_provider {
int family;
struct connman_provider_driver *driver;
void *driver_data;
enum connman_ipconfig_method transport_ipv6_method;
};
void __connman_provider_append_properties(struct connman_provider *provider,
......@@ -121,11 +122,173 @@ void connman_provider_unref_debug(struct connman_provider *provider,
provider_destruct(provider);
}
int __connman_provider_toggle_transport_ipv6(struct connman_provider *provider,
bool disable)
{
struct connman_service *service;
struct connman_service *transport;
struct connman_network *network;
struct connman_ipconfig *service_ipv6config;
struct connman_ipconfig *transport_ipv6config;
const char *transport_ident;
if (!provider)
return -EINVAL;
DBG("provider %p %s", provider, !disable ? "enable" : "disable");
service = provider->vpn_service;
if (!service)
return -EINVAL;
/*
* If a VPN changes from non-split routed to split routed then IPv6 on
* the transport must be re-enabled.
*/
if (__connman_service_is_split_routing(service) && disable)
goto out;
service_ipv6config = __connman_service_get_ip6config(service);
if (service_ipv6config &&
__connman_ipconfig_ipv6_is_enabled(service_ipv6config))
return 0;
/*
* Set system IPv6 status if multiple connected technologies are used
* in order to avoid leaking DNS/data to any IPv6 network. That may
* happen when DNS server returns also AAAA record for a request.
*/
if (!connman_setting_get_bool("SingleConnectedTechnology")) {
if (!__connman_ipconfig_set_ipv6_support(!disable))
connman_warn("cannot disable IPv6 support for "
"provider %p", provider);
}
transport_ident = __connman_provider_get_transport_ident(provider);
transport = connman_service_lookup_from_identifier(transport_ident);
if (!transport) {
DBG("no transport for %p transport ident %s", service,
transport_ident);
return -ENODEV;
}
DBG("transport %p", transport);
network = __connman_service_get_network(transport);
if (!network) {
DBG("no network for transport %p", transport);
return -ENODEV;
}
transport_ipv6config = __connman_service_get_ip6config(transport);
if (!transport_ipv6config) {
DBG("no ipv6config on transport %p", transport);
return 0; // Not enabled?
}
if (disable) {
if (!__connman_ipconfig_ipv6_is_enabled(transport_ipv6config))
return 0;
provider->transport_ipv6_method =
__connman_ipconfig_get_method(
transport_ipv6config);
DBG("set IPv6 ipconfig %p off (old %d)", transport,
provider->transport_ipv6_method);
__connman_network_clear_ipconfig(network,
transport_ipv6config);
__connman_ipconfig_gateway_remove(transport_ipv6config);
__connman_service_ipconfig_indicate_state(transport,
CONNMAN_SERVICE_STATE_DISCONNECT,
CONNMAN_IPCONFIG_TYPE_IPV6);
__connman_ipconfig_address_unset(transport_ipv6config);
/*
* Disables IPv6 on ipconfig and sets the force_disabled
* as true.
*/
connman_network_set_ipv6_method(network,
CONNMAN_IPCONFIG_METHOD_OFF);
__connman_network_enable_ipconfig(network,
transport_ipv6config, true);
__connman_service_ipconfig_indicate_state(transport,
CONNMAN_SERVICE_STATE_IDLE,
CONNMAN_IPCONFIG_TYPE_IPV6);
} else {
if (__connman_ipconfig_ipv6_is_enabled(transport_ipv6config))
return 0;
switch (provider->transport_ipv6_method) {
case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
case CONNMAN_IPCONFIG_METHOD_OFF:
DBG("IPv6 previously disabled on %p", transport);
goto out;
case CONNMAN_IPCONFIG_METHOD_MANUAL:
case CONNMAN_IPCONFIG_METHOD_AUTO:
case CONNMAN_IPCONFIG_METHOD_FIXED:
case CONNMAN_IPCONFIG_METHOD_DHCP:
break;
}
DBG("set IPv6 method %d", provider->transport_ipv6_method);
connman_network_set_ipv6_method(network,
provider->transport_ipv6_method);
__connman_network_enable_ipconfig(network,
transport_ipv6config, true);
}
__connman_service_notify_ipv6_configuration(transport);
__connman_notifier_ipconfig_changed(transport, transport_ipv6config);
out:
if (!disable)
provider->transport_ipv6_method =
CONNMAN_IPCONFIG_METHOD_UNKNOWN;
return 0;
}
static int provider_indicate_state(struct connman_provider *provider,
enum connman_service_state state)
{
int err;
DBG("state %d", state);
switch (state) {
case CONNMAN_SERVICE_STATE_UNKNOWN:
case CONNMAN_SERVICE_STATE_IDLE:
case CONNMAN_SERVICE_STATE_ASSOCIATION:
case CONNMAN_SERVICE_STATE_CONFIGURATION:
break;
case CONNMAN_SERVICE_STATE_READY:
case CONNMAN_SERVICE_STATE_ONLINE:
if (provider->family != AF_INET)
break;
err = __connman_provider_toggle_transport_ipv6(provider, true);
if (err)
DBG("cannot disable IPv6 on provider %p transport",
provider);
break;
case CONNMAN_SERVICE_STATE_DISCONNECT:
case CONNMAN_SERVICE_STATE_FAILURE:
if (provider->family != AF_INET)
break;
err = __connman_provider_toggle_transport_ipv6(provider,
false);
if (err)
DBG("cannot enable IPv6 on provider %p transport",
provider);
break;
}
__connman_service_ipconfig_indicate_state(provider->vpn_service, state,
CONNMAN_IPCONFIG_TYPE_IPV4);
......@@ -248,6 +411,8 @@ static int set_connected(struct connman_provider *provider,
struct connman_service *service = provider->vpn_service;
struct connman_ipconfig *ipconfig;
DBG("provider %p", provider);
if (!service)
return -ENODEV;
......@@ -446,6 +611,18 @@ const char *__connman_provider_get_ident(struct connman_provider *provider)
return provider->identifier;
}
const char * __connman_provider_get_transport_ident(
struct connman_provider *provider)
{
if (!provider)
return NULL;
if (provider->driver && provider->driver->get_property)
return provider->driver->get_property(provider, "Transport");
return NULL;
}
int connman_provider_set_string(struct connman_provider *provider,
const char *key, const char *value)
{
......
......@@ -84,9 +84,22 @@ static void resolvfile_remove_entries(GList *entries)
g_list_free(entries);
}
static int resolvfile_export(void)
static bool already_exported(GList *export_list, const char *str)
{
GList *list;
for (list = export_list; list; list = g_list_next(list)) {
const char *str0 = list->data;
if (g_strcmp0(str0, str) == 0)
return true;
}
return false;
}
static int resolvfile_export(void)
{
GList *list, *export_list;
GString *content;
int fd, err;
unsigned int count;
......@@ -100,36 +113,52 @@ static int resolvfile_export(void)
* MAXDNSRCH/MAXNS entries are used.
*/
for (count = 0, list = g_list_last(resolvfile_list);
export_list = NULL;
for (count = 0, list = g_list_first(resolvfile_list);
list && (count < MAXDNSRCH);
list = g_list_previous(list)) {
list = g_list_next(list)) {
struct resolvfile_entry *entry = list->data;
if (!entry->domain)
continue;
if (already_exported(export_list, entry->domain))
continue;
if (count == 0)
g_string_append_printf(content, "search ");
g_string_append_printf(content, "%s ", entry->domain);
export_list = g_list_append(export_list, entry->domain);
count++;
}
g_list_free(export_list);
if (count)
g_string_append_printf(content, "\n");
for (count = 0, list = g_list_last(resolvfile_list);
export_list = NULL;
for (count = 0, list = g_list_first(resolvfile_list);
list && (count < MAXNS);
list = g_list_previous(list)) {
list = g_list_next(list)) {
struct resolvfile_entry *entry = list->data;
if (!entry->server)
continue;
g_string_append_printf(content, "nameserver %s\n",
entry->server);
if (already_exported(export_list, entry->server))
continue;
g_string_append_printf(content, "nameserver %s\n", entry->server);
export_list = g_list_append(export_list, entry->server);
count++;
}
g_list_free(export_list);
old_umask = umask(022);
......@@ -173,7 +202,7 @@ int __connman_resolvfile_append(int index, const char *domain,
{
struct resolvfile_entry *entry;
DBG("index %d server %s", index, server);
DBG("index %d domain %s server %s", index, domain, server);
if (index < 0)
return -ENOENT;
......@@ -196,7 +225,7 @@ int __connman_resolvfile_remove(int index, const char *domain,
{
GList *list, *matches = NULL;
DBG("index %d server %s", index, server);
DBG("index %d domain %s server %s", index, domain, server);
for (list = resolvfile_list; list; list = g_list_next(list)) {
struct resolvfile_entry *entry = list->data;
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment