Chris Novakovic <chris@chrisn.me.uk>
Ryan Schaefer <ryan.schaefer@flukenetworks.com>
Nicolas Cornu <n.cornu@overkiz.com>
-Rahul Jain <rahul.jain@samsung.com
+Rahul Jain <rahul.jain@samsung.com>
Benoît Monin <benoit.monin@gmx.fr>
Jussi Laakkonen <jussi.laakkonen@jolla.com>
Vivien Henriet <v.henriet@overkiz.com>
Colin Wee <cwee@tesla.com>
Valery Kashcheev <v.kascheev@omp.ru>
Alyssa Ross <hi@alyssa.is>
+Ariel D'Alessandro <ariel.dalessandro@collabora.com>
+Lukáš Karas <lukas.karas@centrum.cz>
+Michael Nazzareno Trimarchi <michael@amarulasolutions.com>
+Christian Taedcke <christian.taedcke@lemonbeat.com>
+Matthias Gerstner <mgerstner@suse.de>
+ver 1.41:
+ Fix issue with RTNL netlink message alignment.
+ Fix issue with dnsproxy and timeout for TCP feature.
+ Fix issue with dnsproxy and busy loop in TCP server.
+ Fix issue with WiFi connection with no passphrase.
+ Add support for wpa_supplicant and WPA3-SAE functionality.
+ Add support for D-Bus ObjectManager interface.
+
ver 1.40:
Fix issue with handling WiFi disconnecting status.
Fix issue with handling WiFi auto-connect and iwd backend.
(for IPv6 connectivity). The used URL looks like this
http://ipv{4|6}.connman.net/online/status.html
+When an online check request fails, another one is triggered after a
+longer interval. The intervals follow the square series of numbers
+in a specific range, by default [1, 12], corresponding to the following
+intervals, in seconds: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 and 144.
+
See connman.conf(5) for the EnableOnlineCheck option, if you need to
disable the feature.
+It is also possible to specify other URLs via OnlineCheckIPv4URL and
+OnlineCheckIPv6URL options.
+The range of intervals between two online check requests can be fine-tuned
+via OnlineCheckInitialInterval and OnlineCheckMaxInterval options.
During the online check procedure, ConnMan will temporarily install
a host route to both the ipv4.connman.net and ipv6.connman.net so that
Mailing list archive:
https://lore.kernel.org/connman
+
+IRC:
+ ircs://irc.oftc.net:6697/#connman (for SSL)
+ irc://irc.oftc.net:6667/#connman (for non-SSL)
CFLAGS+=" -Wdeclaration-after-statement"
CFLAGS+=" -Wmissing-declarations"
CFLAGS+=" -Wredundant-decls"
- CFLAGS+=" -Wcast-align"
+ if ( $CC -v 2>/dev/null | grep "gcc version" ); then
+ CFLAGS+=" -Wcast-align"
+ fi
CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
fi
])
request_input_string_return },
{ "OpenConnect.VPNHost", false, "OpenConnect VPN server? ",
request_input_string_return },
+ { "OpenConnect.SecondPassword", false, "VPN one-time password? ",
+ request_input_string_return },
{ "Username", false, "VPN username? ", request_input_string_return },
{ "Password", false, "VPN password? ", request_input_string_return },
{ },
int ssid_result;
int passphrase_result;
int set_tethering;
+ int freq_result;
};
static int tether_update(struct tether_properties *tether)
{
int ret;
- if (tether->ssid_result == 0 && tether->passphrase_result == 0) {
+ if (tether->ssid_result == 0 && tether->passphrase_result == 0 &&
+ tether->freq_result == 0) {
ret = tether_set("wifi", tether->set_tethering);
g_free(tether);
return ret;
}
if (tether->ssid_result != -EINPROGRESS &&
- tether->passphrase_result != -EINPROGRESS) {
+ tether->passphrase_result != -EINPROGRESS &&
+ tether->freq_result != -EINPROGRESS) {
g_free(tether);
return 0;
}
return tether_update(tether);
}
-static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering)
+static int tether_set_freq_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
- struct tether_properties *tether = g_new(struct tether_properties, 1);
+ struct tether_properties *tether = user_data;
+
+ if (!error) {
+ fprintf(stdout, "Wifi access point frequency set\n");
+ tether->freq_result = 0;
+ } else {
+ fprintf(stderr, "Error setting wifi frequency: %s\n", error);
+ tether->freq_result = -EINVAL;
+ }
+
+ return tether_update(tether);
+}
+
+static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering, int freq)
+{
+ struct tether_properties *tether = g_new0(struct tether_properties, 1);
tether->set_tethering = set_tethering;
tether_set_passphrase_return, tether,
"TetheringPassphrase", DBUS_TYPE_STRING, &passphrase);
+ if (freq > 0) {
+ tether->freq_result =__connmanctl_dbus_set_property(connection,
+ "/net/connman/technology/wifi",
+ "net.connman.Technology",
+ tether_set_freq_return, tether,
+ "TetheringFreq", DBUS_TYPE_INT32, &freq);
+ }
+
if (tether->ssid_result != -EINPROGRESS &&
- tether->passphrase_result != -EINPROGRESS) {
+ tether->passphrase_result != -EINPROGRESS &&
+ tether->freq_result != -EINPROGRESS) {
g_free(tether);
return -ENXIO;
}
if (num < 3)
return -EINVAL;
- passphrase = args[num - 1];
- ssid = args[num - 2];
-
set_tethering = parse_boolean(args[2]);
if (strcmp(args[1], "wifi") == 0) {
+ int freq = 0;
- if (num > 5)
+ if (num > 6)
return -E2BIG;
- if (num == 5 && set_tethering == -1)
+ if (num >= 5 && set_tethering == -1)
return -EINVAL;
if (num == 4)
set_tethering = -1;
+ if (num == 6) {
+ freq = atoi(args[num - 1]);
+ num --;
+ }
+
+ passphrase = args[num - 1];
+ ssid = args[num - 2];
+
if (num > 3)
- return tether_set_ssid(ssid, passphrase, set_tethering);
+ return tether_set_ssid(ssid, passphrase, set_tethering, freq);
}
if (num > 3)
int i;
char *rule;
-
for (i = 0; monitor[i].interface; i++) {
if (g_strcmp0(interface, monitor[i].interface) == 0) {
if (monitor[i].enabled == false)
return -EINPROGRESS;
}
-
static int session_connect(void)
{
return __connmanctl_dbus_method_call(connection, "net.connman",
"Disables given technology or offline mode",
lookup_technology_offline },
{ "tether", "<technology> on|off\n"
- " wifi [on|off] <ssid> <passphrase> ",
+ " wifi [on|off] <ssid> <passphrase> [<freq>] ",
NULL, cmd_tether,
"Enable, disable tethering, set SSID and passphrase for wifi",
lookup_tether },
AC_PREREQ(2.60)
-AC_INIT(connman, 1.40)
+AC_INIT(connman, 1.41)
AC_CONFIG_MACRO_DIR([m4])
If this setting is false, the default service will remain in READY state.
Default value is true.
.TP
+.BI OnlineCheckIPv4URL= url, OnlineCheckIPv6URL= url
+Urls (IPv4 and IPv6 respectively) used during the online status check.
+Please refer to the README for more detailed information.
+Default values are http://ipv4.connman.net/online/status.html and
+http://ipv6.connman.net/online/status.html respectively.
+.TP
.BI OnlineCheckInitialInterval= secs, OnlineCheckMaxInterval= secs
Range of intervals between two online check requests.
-When an online check request fails, another one is triggered after a
-longer interval. The intervals follow the power of two series of numbers
-between OnlineCheckInitialInterval and OnlineCheckMaxInterval.
-Default range is [1, 12], corresponding to the following intervals, in
-seconds: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 and 144.
+Please refer to the README for more detailed information.
+Default values are 1 and 12 respectively.
.TP
.BI EnableOnlineToReadyTransition=true\ \fR|\fB\ false
WARNING: Experimental feature!!!
This property is only valid for the WiFi technology,
and is then mapped to the WPA pre-shared key clients
will have to use in order to establish a connection.
+
+ int TetheringFreq [readwrite]
+
+ The tethering access point frequency
+
+ This property is only valid for the WiFi technology and it's
+ optional. Default frequency is 2412 Mhz.
Return the OpenConnect cookie value that is used for
authenticating the VPN session.
+ string OpenConnect.Group
+
+ Choose authentication login group.
+
string OpenConnect.PKCSClientCert
Informational field containing a PKCS#1/PKCS#8/PKCS#12
Password for decrypting PKCS#8/PKCS#12 client
certificate.
+ string OpenConnect.SecondPassword
+
+ Second factor password for authentication.
+
string OpenConnect.ServerCert
Return the OpenConnect server hash used to identify
the final server after possible web authentication
logins, selections and redirections.
+ boolean OpenConnect.UseSecondPassword
+
+ Indicates that second factor password is used
+ for selected authentication group.
+
string OpenConnect.VPNHost
Return the final VPN server to use after possible
The VPN server activated route. These routes
are pushed to connman by VPN server.
+ string AuthErrorLimit
+
+ This value defines the amount of authentication errors
+ that are allowed before informing VPN agent to clear
+ the credentials in case there was a previous successful
+ VPN connection made within one hour. This is to be used
+ with providers that allow only one login from one
+ account at a time to prevent clearing of credentials
+ when networks are rapidly changed. This value is used
+ as an integer and if unset this default to "1" for all
+ except OpenVPN that uses value "10". Setting value "0"
+ disables the feature for the provider.
+
There can be other properties also but as the VPN
technologies are so different, they have different
kind of options that they need, so not all options
dhcp_client->retry_times = 0;
option = dhcp_get_option(&packet, pkt_len, DHCP_SERVER_ID);
+ if (!option)
+ return TRUE;
+
dhcp_client->server_ip = get_be32(option);
dhcp_client->requested_ip = ntohl(packet.yiaddr);
if (dhcp_client->state == REBOOTING) {
option = dhcp_get_option(&packet, pkt_len,
DHCP_SERVER_ID);
+ if (!option)
+ return TRUE;
dhcp_client->server_ip = get_be32(option);
}
#define G_SUPPLICANT_KEYMGMT_WPA_EAP (1 << 7)
#define G_SUPPLICANT_KEYMGMT_WPA_EAP_256 (1 << 8)
#define G_SUPPLICANT_KEYMGMT_WPS (1 << 9)
+#define G_SUPPLICANT_KEYMGMT_SAE (1 << 10)
#define G_SUPPLICANT_PROTO_WPA (1 << 0)
#define G_SUPPLICANT_PROTO_RSN (1 << 1)
G_SUPPLICANT_PEER_GROUP_FAILED,
} GSupplicantPeerState;
+typedef enum {
+ G_SUPPLICANT_MFP_NONE,
+ G_SUPPLICANT_MFP_OPTIONAL,
+ G_SUPPLICANT_MFP_REQUIRED,
+} GSupplicantMfpOptions;
+
struct _GSupplicantSSID {
const void *ssid;
unsigned int ssid_len;
dbus_bool_t use_wps;
const char *pin_wps;
const char *bgscan;
+ unsigned int keymgmt;
+ GSupplicantMfpOptions ieee80211w;
};
typedef struct _GSupplicantSSID GSupplicantSSID;
GSupplicantInterface *g_supplicant_peer_get_group_interface(GSupplicantPeer *peer);
bool g_supplicant_peer_is_client(GSupplicantPeer *peer);
bool g_supplicant_peer_has_requested_connection(GSupplicantPeer *peer);
+unsigned int g_supplicant_network_get_keymgmt(GSupplicantNetwork *network);
struct _GSupplicantCallbacks {
void (*system_ready) (void);
{ "wpa-eap", G_SUPPLICANT_KEYMGMT_WPA_EAP },
{ "wpa-eap-sha256", G_SUPPLICANT_KEYMGMT_WPA_EAP_256 },
{ "wps", G_SUPPLICANT_KEYMGMT_WPS },
+ { "sae", G_SUPPLICANT_KEYMGMT_SAE },
{ }
};
unsigned int wps_capabilities;
GHashTable *bss_table;
GHashTable *config_table;
+ unsigned int keymgmt;
};
struct _GSupplicantPeer {
const char *g_supplicant_network_get_mode(GSupplicantNetwork *network)
{
if (!network)
- return G_SUPPLICANT_MODE_UNKNOWN;
+ return NULL;
return mode2string(network->mode);
}
const char *g_supplicant_network_get_security(GSupplicantNetwork *network)
{
if (!network)
- return G_SUPPLICANT_SECURITY_UNKNOWN;
+ return NULL;
return security2string(network->security);
}
return peer->connection_requested;
}
+unsigned int g_supplicant_network_get_keymgmt(GSupplicantNetwork *network)
+{
+ if (!network)
+ return 0;
+
+ return network->keymgmt;
+}
+
static void merge_network(GSupplicantNetwork *network)
{
GString *str;
else if (g_strcmp0(mode, "1") == 0)
g_string_append_printf(str, "_adhoc");
- if (g_strcmp0(key_mgmt, "WPA-PSK") == 0)
+ if ((g_strcmp0(key_mgmt, "WPA-PSK") == 0) ||
+ (g_strcmp0(key_mgmt, "SAE") == 0))
g_string_append_printf(str, "_psk");
group = g_string_free(str, FALSE);
network->name = create_name(bss->ssid, bss->ssid_len);
network->mode = bss->mode;
network->security = bss->security;
+ network->keymgmt = bss->keymgmt;
network->ssid_len = bss->ssid_len;
memcpy(network->ssid, bss->ssid, bss->ssid_len);
network->signal = bss->signal;
if (bss->keymgmt &
(G_SUPPLICANT_KEYMGMT_WPA_PSK |
G_SUPPLICANT_KEYMGMT_WPA_FT_PSK |
- G_SUPPLICANT_KEYMGMT_WPA_PSK_256))
+ G_SUPPLICANT_KEYMGMT_WPA_PSK_256 |
+ G_SUPPLICANT_KEYMGMT_SAE))
bss->psk = TRUE;
if (bss->ieee8021x)
g_free(proto);
}
+static void add_network_ieee80211w(DBusMessageIter *dict, GSupplicantSSID *ssid,
+ GSupplicantMfpOptions ieee80211w)
+{
+ supplicant_dbus_dict_append_basic(dict, "ieee80211w", DBUS_TYPE_UINT32,
+ &ieee80211w);
+}
+
static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
{
+ GSupplicantMfpOptions ieee80211w;
char *key_mgmt;
switch (ssid->security) {
add_network_security_ciphers(dict, ssid);
break;
case G_SUPPLICANT_SECURITY_PSK:
- key_mgmt = "WPA-PSK";
+ if (ssid->keymgmt & G_SUPPLICANT_KEYMGMT_SAE) {
+ if (ssid->keymgmt & G_SUPPLICANT_KEYMGMT_WPA_PSK) {
+ /*
+ * WPA3-Personal transition mode: supports both
+ * WPA2-Personal (PSK) and WPA3-Personal (SAE)
+ */
+ key_mgmt = "SAE WPA-PSK";
+ ieee80211w = G_SUPPLICANT_MFP_OPTIONAL;
+ } else {
+ key_mgmt = "SAE";
+ ieee80211w = G_SUPPLICANT_MFP_REQUIRED;
+ }
+ add_network_ieee80211w(dict, ssid, ieee80211w);
+ } else {
+ key_mgmt = "WPA-PSK";
+ }
add_network_security_psk(dict, ssid);
add_network_security_ciphers(dict, ssid);
add_network_security_proto(dict, ssid);
enum connman_service_type connman_technology_get_type
(struct connman_technology *technology);
-bool connman_technology_get_wifi_tethering(const char **ssid,
- const char **psk);
+
+bool connman_technology_get_wifi_tethering(const struct connman_technology *technology,
+ const char **ssid, const char **psk, int *freq);
+
bool connman_technology_is_tethering_allowed(enum connman_service_type type);
struct connman_technology_driver {
void (*remove_interface) (struct connman_technology *technology,
int index);
int (*set_tethering) (struct connman_technology *technology,
- const char *identifier, const char *passphrase,
const char *bridge, bool enabled);
int (*set_regdom) (struct connman_technology *technology,
const char *alpha2);
}
static int bluetooth_tech_set_tethering(struct connman_technology *technology,
- const char *identifier, const char *passphrase,
const char *bridge, bool enabled)
{
GHashTableIter hash_iter;
}
static int eth_tech_set_tethering(struct connman_technology *technology,
- const char *identifier, const char *passphrase,
const char *bridge, bool enabled)
{
if (!connman_technology_is_tethering_allowed(
}
static int gadget_tech_set_tethering(struct connman_technology *technology,
- const char *identifier, const char *passphrase,
const char *bridge, bool enabled)
{
DBG("bridge %s enabled %d", bridge, enabled);
static void update_network_connected(struct iwd_network *iwdn)
{
struct iwd_device *iwdd;
- int index;
iwdd = g_hash_table_lookup(devices, iwdn->device);
if (!iwdd)
return;
- index = connman_inet_ifindex(iwdd->name);
- if (index < 0)
- return;
-
- DBG("interface name %s index %d", iwdd->name, index);
- connman_network_set_index(iwdn->network, index);
+ DBG("interface name %s index %d", iwdd->name,
+ connman_network_get_index(iwdn->network));
connman_network_set_connected(iwdn->network, true);
}
if (!iwds)
return -EIO;
+ connman_network_set_associating(network, false);
+
if (!g_dbus_proxy_method_call(iwds->proxy, "Disconnect",
NULL, cm_network_disconnect_cb, g_strdup(iwdn->path), g_free))
return -EIO;
- return -EINPROGRESS;
+ return 0;
}
struct auto_connect_cb_data {
const char *path = user_data;
struct iwd_station *iwds;
- iwds = g_hash_table_lookup(networks, path);
+ iwds = g_hash_table_lookup(stations, path);
if (!iwds)
return;
}
static int cm_tech_tethering(struct connman_technology *technology,
- const char *identifier, const char *passphrase,
const char *bridge, bool enabled)
{
GHashTableIter iter;
gpointer key, value;
int err = 0, res;
+ const char *ssid;
+ const char *psk;
+ int freq;
+
+ connman_technology_get_wifi_tethering(technology, &ssid, &psk, &freq);
g_hash_table_iter_init(&iter, devices);
continue;
if (!enabled && !g_strcmp0("ap", iwdd->mode)) {
- res = cm_change_tethering(iwdd, technology, identifier,
- passphrase, bridge, enabled);
+ res = cm_change_tethering(iwdd, technology, ssid,
+ psk, bridge, enabled);
if (res)
connman_warn("%s switching to Station mode failed",
iwdd->path);
}
if (enabled && !g_strcmp0("station", iwdd->mode)) {
- err = cm_change_tethering(iwdd, technology, identifier,
- passphrase, bridge, enabled);
+ err = cm_change_tethering(iwdd, technology, ssid,
+ psk, bridge, enabled);
if (err)
connman_warn("%s switching to AccessPoint mode failed",
iwdd->path);
{
struct iwd_device *iwdd;
char *identifier;
+ int index;
iwdd = g_hash_table_lookup(devices, iwdn->device);
if (!iwdd)
identifier = create_identifier(path, iwdn->type);
iwdn->network = connman_network_create(identifier,
CONNMAN_NETWORK_TYPE_WIFI);
+
+ index = connman_inet_ifindex(iwdd->name);
+ if (index >= 0)
+ connman_network_set_index(iwdn->network, index);
+
connman_network_set_data(iwdn->network, iwdn);
connman_network_set_name(iwdn->network, iwdn->name);
}
iwdn->iwdd = iwdd;
- connman_network_set_available(iwdn->network, true);
+ if (connman_network_get_strength(iwdn->network))
+ connman_network_set_available(iwdn->network, true);
+
connman_network_set_group(iwdn->network, identifier);
g_free(identifier);
struct iwd_adapter *iwda;
GSList *modes, *list;
- iwda = g_try_new0(struct iwd_adapter, 1);
-
- if (!iwda) {
- connman_error("Out of memory creating IWD adapter");
- return;
- }
-
+ iwda = g_new0(struct iwd_adapter, 1);
iwda->path = g_strdup(path);
g_hash_table_replace(adapters, iwda->path, iwda);
const char *path = g_dbus_proxy_get_path(proxy);
struct iwd_device *iwdd;
- iwdd = g_try_new0(struct iwd_device, 1);
-
- if (!iwdd) {
- connman_error("Out of memory creating IWD device");
- return;
- }
-
+ iwdd = g_new0(struct iwd_device, 1);
iwdd->path = g_strdup(path);
g_hash_table_replace(devices, iwdd->path, iwdd);
return get_reply_on_error(message, EINVAL);
passwd = connman_network_get_string(iwdn->network, "WiFi.Passphrase");
+ if (!passwd)
+ return get_reply_on_error(message, ENOKEY);
return g_dbus_create_reply(message, DBUS_TYPE_STRING, &passwd,
DBUS_TYPE_INVALID);
const char *path = g_dbus_proxy_get_path(proxy);
struct iwd_network *iwdn;
- iwdn = g_try_new0(struct iwd_network, 1);
-
- if (!iwdn) {
- connman_error("Out of memory creating IWD network");
- return;
- }
-
+ iwdn = g_new0(struct iwd_network, 1);
iwdn->path = g_strdup(path);
g_hash_table_replace(networks, iwdn->path, iwdn);
const char *path = g_dbus_proxy_get_path(proxy);
struct iwd_known_network *iwdkn;
- iwdkn = g_try_new0(struct iwd_known_network, 1);
- if (!iwdkn) {
- connman_error("Out of memory creating IWD known network");
- return;
- }
-
+ iwdkn = g_new0(struct iwd_known_network, 1);
iwdkn->path = g_strdup(path);
g_hash_table_replace(known_networks, iwdkn->path, iwdkn);
const char *path = g_dbus_proxy_get_path(proxy);
struct iwd_station *iwds;
- iwds = g_try_new0(struct iwd_station, 1);
- if (!iwds) {
- connman_error("Out of memory creating IWD station");
- return;
- }
-
+ iwds = g_new0(struct iwd_station, 1);
iwds->path = g_strdup(path);
g_hash_table_replace(stations, iwds->path, iwds);
const char *path = g_dbus_proxy_get_path(proxy);
struct iwd_ap *iwdap;
- iwdap = g_try_new0(struct iwd_ap, 1);
- if (!iwdap) {
- connman_error("Out of memory creating IWD access point");
- return;
- }
+ iwdap = g_new0(struct iwd_ap, 1);
iwdap->index = -1;
iwdap->path = g_strdup(path);
DBusMessageIter dict;
const char *ssid, *psk;
uint8_t *tlv_msg;
- int length;
+ int length, freq;
- if (!connman_technology_get_wifi_tethering(&ssid, &psk))
+ if (!connman_technology_get_wifi_tethering(NULL, &ssid, &psk, &freq))
return get_reply_on_error(message, ENOTSUP);
tlv_msg = encode_to_tlv(ssid, psk, &length);
* Connection Manager
*
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ * Copyright (C) 2019-2021 Jolla Ltd. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".AlreadyConnected") == 0)
return -EISCONN;
+ if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".NoCarrier") == 0)
+ return -ENOLINK;
+
if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".OperationCanceled") == 0)
return -ECANCELED;
if (dbus_set_error_from_message(&error, reply)) {
int err = errorstr2val(error.name);
+ switch (err) {
+ case -EINPROGRESS:
+ break;
/*
* ECANCELED means that user has canceled authentication
* dialog. That's not really an error, it's part of a normal
* workflow. We also take it as a request to turn autoconnect
* off, in case if it was on.
*/
- if (err == -ECANCELED) {
+ case -ECANCELED:
DBG("%s connect canceled", data->path);
connman_provider_set_autoconnect(data->provider, false);
- } else if (err != -EINPROGRESS) {
+ break;
+ case -ENOLINK: /* vpnd reports that connmand is not online. */
+ case -EISCONN:
+ case -ECONNREFUSED:
+ default:
connman_error("Connect reply: %s (%s)", error.message,
error.name);
DBG("data %p cb_data %p", data, cb_data);
if (data->disconnect_call) {
DBG("already disconnecting");
- return -EINVAL;
+ return -EALREADY;
}
message = dbus_message_new_method_call(VPN_SERVICE, data->path,
int assoc_code;
};
+struct wifi_network {
+ unsigned int keymgmt;
+};
+
struct disconnect_data {
struct wifi_data *wifi;
struct connman_network *network;
static void start_autoscan(struct connman_device *device);
static int tech_set_tethering(struct connman_technology *technology,
- const char *identifier, const char *passphrase,
const char *bridge, bool enabled);
static int p2p_tech_probe(struct connman_technology *technology)
for (list = wifi->networks; list; list = list->next) {
struct connman_network *network = list->data;
+ g_free(connman_network_get_data(network));
connman_device_remove_network(device, network);
connman_network_unref(network);
}
static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
{
+ struct wifi_network *network_data = connman_network_get_data(network);
const char *security;
memset(ssid, 0, sizeof(*ssid));
ssid->scan_ssid = 1;
security = connman_network_get_string(network, "WiFi.Security");
ssid->security = network_security(security);
+ ssid->keymgmt = network_data->keymgmt;
+ ssid->ieee80211w = G_SUPPLICANT_MFP_OPTIONAL;
ssid->passphrase = connman_network_get_string(network,
"WiFi.Passphrase");
wifi->tethering = false;
ret = tech_set_tethering(wifi->tethering_param->technology,
- wifi->tethering_param->ssid->ssid,
- wifi->tethering_param->ssid->passphrase,
wifi->bridge, true);
if ((ret == -EOPNOTSUPP) && (wifi_technology)) {
struct connman_network *network;
GSupplicantInterface *interface;
struct wifi_data *wifi;
+ struct wifi_network *network_data;
const char *name, *identifier, *security, *group, *mode;
const unsigned char *ssid;
unsigned int ssid_len;
}
wifi->networks = g_slist_prepend(wifi->networks, network);
+
+ network_data = g_new0(struct wifi_network, 1);
+ connman_network_set_data(network, network_data);
}
+ network_data = connman_network_get_data(network);
+ network_data->keymgmt =
+ g_supplicant_network_get_keymgmt(supplicant_network);
+
if (name && name[0] != '\0')
connman_network_set_name(network, name);
wifi->networks = g_slist_remove(wifi->networks, connman_network);
+ g_free(connman_network_get_data(connman_network));
connman_device_remove_network(wifi->device, connman_network);
connman_network_unref(connman_network);
}
wifi_technology = NULL;
}
-static GSupplicantSSID *ssid_ap_init(const char *ssid, const char *passphrase)
+static GSupplicantSSID *ssid_ap_init(const struct connman_technology *technology)
{
GSupplicantSSID *ap;
+ const char *ssid, *passphrase;
+ int freq;
+ bool ret;
ap = g_try_malloc0(sizeof(GSupplicantSSID));
if (!ap)
return NULL;
+ ret = connman_technology_get_wifi_tethering(technology,
+ &ssid, &passphrase,
+ &freq);
+ if (ret == false)
+ return NULL;
+
ap->mode = G_SUPPLICANT_MODE_MASTER;
ap->ssid = ssid;
ap->ssid_len = strlen(ssid);
ap->scan_ssid = 0;
- ap->freq = 2412;
+ if (freq)
+ ap->freq = freq;
+ else
+ ap->freq = 2412;
if (!passphrase || strlen(passphrase) == 0) {
ap->security = G_SUPPLICANT_SECURITY_NONE;
}
static int enable_wifi_tethering(struct connman_technology *technology,
- const char *bridge, const char *identifier,
- const char *passphrase, bool available)
+ const char *bridge, bool available)
{
GList *list;
GSupplicantInterface *interface;
info->wifi = wifi;
info->technology = technology;
info->wifi->bridge = bridge;
- info->ssid = ssid_ap_init(identifier, passphrase);
+ info->ssid = ssid_ap_init(technology);
if (!info->ssid)
goto failed;
info->ifname = g_strdup(ifname);
wifi->tethering_param->technology = technology;
- wifi->tethering_param->ssid = ssid_ap_init(identifier, passphrase);
+ wifi->tethering_param->ssid = ssid_ap_init(technology);
if (!wifi->tethering_param->ssid)
goto failed;
}
static int tech_set_tethering(struct connman_technology *technology,
- const char *identifier, const char *passphrase,
const char *bridge, bool enabled)
{
GList *list;
}
DBG("trying tethering for available devices");
- err = enable_wifi_tethering(technology, bridge, identifier, passphrase,
- true);
+ err = enable_wifi_tethering(technology, bridge, true);
if (err < 0) {
DBG("trying tethering for any device");
- err = enable_wifi_tethering(technology, bridge, identifier,
- passphrase, false);
+ err = enable_wifi_tethering(technology, bridge, false);
}
return err;
retry = true;
}
+out:
report_error->callback(report_error->user_context, retry,
report_error->user_data);
-out:
g_free(report_error);
}
static void provision_service_wifi(struct connman_config_service *config,
struct connman_service *service,
- struct connman_network *network,
- const void *ssid, unsigned int ssid_len)
+ struct connman_network *network)
{
if (config->eap)
__connman_service_set_string(service, "EAP", config->eap);
config->timeservers);
if (type == CONNMAN_SERVICE_TYPE_WIFI) {
- provision_service_wifi(config, service, network,
- ssid, ssid_len);
+ provision_service_wifi(config, service, network);
}
__connman_service_mark_dirty();
connection = conn;
- return 0;
+ return g_dbus_attach_object_manager(conn);
}
void __connman_dbus_cleanup(void)
ulen = strlen(name) + 1;
if ((uptr + ulen) > uncomp_end)
goto out;
- strncpy(uptr, name, ulen);
+ memcpy(uptr, name, ulen);
debug("pos %d ulen %d left %d name %s", pos, ulen,
(int)(uncomp_end - (uptr + ulen)), uptr);
if (offset < 0)
return offset;
+ if (reply_len < 0)
+ return -EINVAL;
+ if (reply_len < offset + 1)
+ return -EINVAL;
+ if ((size_t)reply_len < sizeof(struct domain_hdr))
+ return -EINVAL;
hdr = (void *)(reply + offset);
dns_id = reply[offset] | reply[offset + 1] << 8;
*/
if (req->append_domain && ntohs(hdr->qdcount) == 1) {
uint16_t domain_len = 0;
- uint16_t header_len;
+ uint16_t header_len, payload_len;
uint16_t dns_type, dns_class;
uint8_t host_len, dns_type_pos;
char uncompressed[NS_MAXDNAME], *uptr;
char *ptr, *eom = (char *)reply + reply_len;
+ char *domain;
/*
* ptr points to the first char of the hostname.
* ->hostname.domain.net
*/
header_len = offset + sizeof(struct domain_hdr);
+ if (reply_len < header_len)
+ return -EINVAL;
+ payload_len = reply_len - header_len;
+
ptr = (char *)reply + header_len;
host_len = *ptr;
+ domain = ptr + 1 + host_len;
+ if (domain > eom)
+ return -EINVAL;
+
if (host_len > 0)
- domain_len = strnlen(ptr + 1 + host_len,
- reply_len - header_len);
+ domain_len = strnlen(domain, eom - domain);
/*
* If the query type is anything other than A or AAAA,
*/
dns_type_pos = host_len + 1 + domain_len + 1;
+ if (ptr + (dns_type_pos + 3) > eom)
+ return -EINVAL;
dns_type = ptr[dns_type_pos] << 8 |
ptr[dns_type_pos + 1];
dns_class = ptr[dns_type_pos + 2] << 8 |
int new_len, fixed_len;
char *answers;
+ if (len > payload_len)
+ return -EINVAL;
/*
* First copy host (without domain name) into
* tmp buffer.
* Copy type and class fields of the question.
*/
ptr += len + domain_len + 1;
+ if (ptr + NS_QFIXEDSZ > eom)
+ return -EINVAL;
memcpy(uptr, ptr, NS_QFIXEDSZ);
/*
uptr += NS_QFIXEDSZ;
answers = uptr;
fixed_len = answers - uncompressed;
+ if (ptr + offset > eom)
+ return -EINVAL;
/*
* We then uncompress the result to buffer
err = sendto(sk, req->resp, req->resplen, 0,
&req->sa, req->sa_len);
} else {
+ uint16_t tcp_len = htons(req->resplen - 2);
+ /* correct TCP message length */
+ memcpy(req->resp, &tcp_len, sizeof(tcp_len));
sk = req->client_sk;
err = send(sk, req->resp, req->resplen, MSG_NOSIGNAL);
}
len = recv(sk, buf, sizeof(buf), 0);
- if (len >= 12)
- forward_dns_reply(buf, len, IPPROTO_UDP, data);
+ forward_dns_reply(buf, len, IPPROTO_UDP, data);
return TRUE;
}
}
}
+ /*
+ * Remove the G_IO_OUT flag from the watch, otherwise we end
+ * up in a busy loop, because the socket is constantly writable.
+ *
+ * There seems to be no better way in g_io to do that than
+ * re-adding the watch.
+ */
+ g_source_remove(server->watch);
+ server->watch = g_io_add_watch(server->channel,
+ G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+ tcp_server_event, server);
+
server->connected = true;
server_list = g_slist_append(server_list, server);
- if (server->timeout > 0) {
- g_source_remove(server->timeout);
- server->timeout = 0;
- }
-
for (list = request_list; list; ) {
struct request_data *req = list->data;
int status;
for (list = server_list; list; list = list->next) {
struct server_data *data = list->data;
GList *dom_list;
- char *dom;
+ char *dom = NULL;
bool dom_found = false;
if (data->index < 0)
return 0;
}
-static inline void ipv6_addr_advert_mult(const struct in6_addr *addr,
- struct in6_addr *advert)
-{
- ipv6_addr_set(advert, htonl(0xFF020000), 0, htonl(0x2),
- htonl(0xFF000000) | addr->s6_addr32[3]);
-}
-
#define MSG_SIZE_SEND 1452
static int inc_len(int len, int inc)
ifname = connman_inet_ifname(ipconfig->index);
+ if (!ifname)
+ return;
+
set_ipv6_state(ifname, false);
g_free(ifname);
ifname = connman_inet_ifname(ipconfig->index);
+ if (!ifname)
+ return;
+
if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_AUTO)
set_ipv6_privacy(ifname, ipconfig->ipv6_privacy_config);
#define DEFAULT_INPUT_REQUEST_TIMEOUT (120 * 1000)
#define DEFAULT_BROWSER_LAUNCH_TIMEOUT (300 * 1000)
+#define DEFAULT_ONLINE_CHECK_IPV4_URL "http://ipv4.connman.net/online/status.html"
+#define DEFAULT_ONLINE_CHECK_IPV6_URL "http://ipv6.connman.net/online/status.html"
+
/*
* We set the integer to 1 sec so that we have a chance to get
* necessary IPv6 router advertisement messages that might have
char *vendor_class_id;
bool enable_online_check;
bool enable_online_to_ready_transition;
+ char *online_check_ipv4_url;
+ char *online_check_ipv6_url;
unsigned int online_check_initial_interval;
unsigned int online_check_max_interval;
bool auto_connect_roaming_services;
.vendor_class_id = NULL,
.enable_online_check = true,
.enable_online_to_ready_transition = false,
+ .online_check_ipv4_url = NULL,
+ .online_check_ipv6_url = NULL,
.online_check_initial_interval = DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL,
.online_check_max_interval = DEFAULT_ONLINE_CHECK_MAX_INTERVAL,
.auto_connect_roaming_services = false,
#define CONF_VENDOR_CLASS_ID "VendorClassID"
#define CONF_ENABLE_ONLINE_CHECK "EnableOnlineCheck"
#define CONF_ENABLE_ONLINE_TO_READY_TRANSITION "EnableOnlineToReadyTransition"
+#define CONF_ONLINE_CHECK_IPV4_URL "OnlineCheckIPv4URL"
+#define CONF_ONLINE_CHECK_IPV6_URL "OnlineCheckIPv6URL"
#define CONF_ONLINE_CHECK_INITIAL_INTERVAL "OnlineCheckInitialInterval"
#define CONF_ONLINE_CHECK_MAX_INTERVAL "OnlineCheckMaxInterval"
#define CONF_AUTO_CONNECT_ROAMING_SERVICES "AutoConnectRoamingServices"
CONF_VENDOR_CLASS_ID,
CONF_ENABLE_ONLINE_CHECK,
CONF_ENABLE_ONLINE_TO_READY_TRANSITION,
+ CONF_ONLINE_CHECK_IPV4_URL,
+ CONF_ONLINE_CHECK_IPV6_URL,
CONF_ONLINE_CHECK_INITIAL_INTERVAL,
CONF_ONLINE_CHECK_MAX_INTERVAL,
CONF_AUTO_CONNECT_ROAMING_SERVICES,
connman_settings.auto_connect =
parse_service_types(default_auto_connect, CONF_ARRAY_SIZE(default_auto_connect));
+ g_strfreev(str_list);
+
g_clear_error(&error);
str_list = __connman_config_get_string_list(config, "General",
g_clear_error(&error);
+ string = __connman_config_get_string(config, "General",
+ CONF_ONLINE_CHECK_IPV4_URL, &error);
+ if (!error)
+ connman_settings.online_check_ipv4_url = string;
+ else
+ connman_settings.online_check_ipv4_url =
+ g_strdup(DEFAULT_ONLINE_CHECK_IPV4_URL);
+
+ g_clear_error(&error);
+
+ string = __connman_config_get_string(config, "General",
+ CONF_ONLINE_CHECK_IPV6_URL, &error);
+ if (!error)
+ connman_settings.online_check_ipv6_url = string;
+ else
+ connman_settings.online_check_ipv6_url =
+ g_strdup(DEFAULT_ONLINE_CHECK_IPV6_URL);
+
+
+ g_clear_error(&error);
+
integer = g_key_file_get_integer(config, "General",
CONF_ONLINE_CHECK_INITIAL_INTERVAL, &error);
if (!error && integer >= 0)
if (g_str_equal(key, CONF_VENDOR_CLASS_ID))
return connman_settings.vendor_class_id;
+ if (g_str_equal(key, CONF_ONLINE_CHECK_IPV4_URL))
+ return connman_settings.online_check_ipv4_url;
+
+ if (g_str_equal(key, CONF_ONLINE_CHECK_IPV6_URL))
+ return connman_settings.online_check_ipv6_url;
+
if (g_strcmp0(key, "wifi") == 0) {
if (!option_wifi)
return "nl80211,wext";
g_strfreev(connman_settings.blacklisted_interfaces);
g_strfreev(connman_settings.tethering_technologies);
g_free(connman_settings.vendor_class_id);
+ g_free(connman_settings.online_check_ipv4_url);
+ g_free(connman_settings.online_check_ipv6_url);
g_free(option_debug);
g_free(option_wifi);
# Default value is true.
# EnableOnlineCheck = false
+# Urls (IPv4 and IPv6 respectively) used during the online status check.
+# Please refer to the README for more detailed information.
+# Default values are http://ipv4.connman.net/online/status.html and
+# http://ipv6.connman.net/online/status.html respectively.
+# OnlineCheckIPv4URL= http://ipv4.connman.net/online/status.html
+# OnlineCheckIPv6URL= http://ipv6.connman.net/online/status.html
+
# Range of intervals between two online check requests.
-# When an online check request fails, another one is triggered after a
-# longer interval. The intervals follow the power of two series of numbers
-# between OnlineCheckInitialInterval and OnlineCheckMaxInterval.
-# Default range is [1, 12], corresponding to the following intervals, in
-# seconds: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 and 144.
+# Please refer to the README for more detailed information.
+# Default values are 1 and 12 respectively.
# OnlineCheckInitialInterval = 1
# OnlineCheckMaxInterval = 12
{ GDBUS_SIGNAL("PeersChanged",
GDBUS_ARGS({ "changed", "a(oa{sv})" },
{ "removed", "ao" })) },
+ { GDBUS_SIGNAL("TetheringClientsChanged",
+ GDBUS_ARGS({ "registered", "as" },
+ { "removed", "as" })) },
{ },
};
if (!network->device)
return -ENODEV;
- __connman_device_disconnect(network->device);
-
network->connecting = true;
err = network->driver->connect(network);
static GIOChannel *channel = NULL;
static guint channel_watch = 0;
-struct rtnl_request {
- struct nlmsghdr hdr;
- struct rtgenmsg msg;
-};
-#define RTNL_REQUEST_SIZE (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
+#define RTNL_REQUEST_SIZE (NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(struct rtgenmsg)))
static GSList *request_list = NULL;
static guint32 request_seq = 0;
-static struct rtnl_request *find_request(guint32 seq)
+static struct nlmsghdr *find_request(guint32 seq)
{
GSList *list;
for (list = request_list; list; list = list->next) {
- struct rtnl_request *req = list->data;
+ struct nlmsghdr *hdr = list->data;
- if (req->hdr.nlmsg_seq == seq)
- return req;
+ if (hdr->nlmsg_seq == seq)
+ return hdr;
}
return NULL;
}
-static int send_request(struct rtnl_request *req)
+static int send_request(struct nlmsghdr *hdr)
{
struct sockaddr_nl addr;
int sk;
DBG("%s len %d type %d flags 0x%04x seq %d",
- type2string(req->hdr.nlmsg_type),
- req->hdr.nlmsg_len, req->hdr.nlmsg_type,
- req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+ type2string(hdr->nlmsg_type),
+ hdr->nlmsg_len, hdr->nlmsg_type,
+ hdr->nlmsg_flags, hdr->nlmsg_seq);
sk = g_io_channel_unix_get_fd(channel);
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
- return sendto(sk, req, req->hdr.nlmsg_len, 0,
+ return sendto(sk, hdr, hdr->nlmsg_len, 0,
(struct sockaddr *) &addr, sizeof(addr));
}
-static int queue_request(struct rtnl_request *req)
+static int queue_request(struct nlmsghdr *hdr)
{
- request_list = g_slist_append(request_list, req);
+ request_list = g_slist_append(request_list, hdr);
if (g_slist_length(request_list) > 1)
return 0;
- return send_request(req);
+ return send_request(hdr);
}
static int process_response(guint32 seq)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
DBG("seq %d", seq);
- req = find_request(seq);
- if (req) {
- request_list = g_slist_remove(request_list, req);
- g_free(req);
+ hdr = find_request(seq);
+ if (hdr) {
+ request_list = g_slist_remove(request_list, hdr);
+ g_free(hdr);
}
- req = g_slist_nth_data(request_list, 0);
- if (!req)
+ hdr = g_slist_nth_data(request_list, 0);
+ if (!hdr)
return 0;
- return send_request(req);
+ return send_request(hdr);
}
static void rtnl_message(void *buf, size_t len)
static int send_getlink(void)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
+ struct rtgenmsg *msg;
DBG("");
- req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (!req)
- return -ENOMEM;
+ hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+ hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+ hdr->nlmsg_type = RTM_GETLINK;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = request_seq++;
- req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
- req->hdr.nlmsg_type = RTM_GETLINK;
- req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- req->hdr.nlmsg_pid = 0;
- req->hdr.nlmsg_seq = request_seq++;
- req->msg.rtgen_family = AF_INET;
+ msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+ msg->rtgen_family = AF_INET;
- return queue_request(req);
+ return queue_request(hdr);
}
static int send_getaddr(void)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
+ struct rtgenmsg *msg;
DBG("");
- req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (!req)
- return -ENOMEM;
+ hdr = g_malloc0(RTNL_REQUEST_SIZE);
- req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
- req->hdr.nlmsg_type = RTM_GETADDR;
- req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- req->hdr.nlmsg_pid = 0;
- req->hdr.nlmsg_seq = request_seq++;
- req->msg.rtgen_family = AF_INET;
+ hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+ hdr->nlmsg_type = RTM_GETADDR;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = request_seq++;
- return queue_request(req);
+ msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+ msg->rtgen_family = AF_INET;
+
+ return queue_request(hdr);
}
static int send_getroute(void)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
+ struct rtgenmsg *msg;
DBG("");
- req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (!req)
- return -ENOMEM;
+ hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+ hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+ hdr->nlmsg_type = RTM_GETROUTE;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = request_seq++;
- req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
- req->hdr.nlmsg_type = RTM_GETROUTE;
- req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- req->hdr.nlmsg_pid = 0;
- req->hdr.nlmsg_seq = request_seq++;
- req->msg.rtgen_family = AF_INET;
+ msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+ msg->rtgen_family = AF_INET;
- return queue_request(req);
+ return queue_request(hdr);
}
static gboolean update_timeout_cb(gpointer user_data)
update_list = NULL;
for (list = request_list; list; list = list->next) {
- struct rtnl_request *req = list->data;
+ struct nlmsghdr *hdr= list->data;
DBG("%s len %d type %d flags 0x%04x seq %d",
- type2string(req->hdr.nlmsg_type),
- req->hdr.nlmsg_len, req->hdr.nlmsg_type,
- req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+ type2string(hdr->nlmsg_type),
+ hdr->nlmsg_len, hdr->nlmsg_type,
+ hdr->nlmsg_flags, hdr->nlmsg_seq);
- g_free(req);
+ g_free(hdr);
list->data = NULL;
}
static GList *service_list = NULL;
static GHashTable *service_hash = NULL;
+static GHashTable *passphrase_requested = NULL;
static GSList *counter_list = NULL;
static unsigned int autoconnect_id = 0;
static unsigned int vpn_autoconnect_id = 0;
return __connman_service_get_index(service) == index;
}
+static void start_wispr_when_connected(struct connman_service *service)
+{
+ if (!connman_setting_get_bool("EnableOnlineCheck")) {
+ connman_info("Online check disabled. "
+ "Default service remains in READY state.");
+ return;
+ }
+
+ if (__connman_service_is_connected_state(service,
+ CONNMAN_IPCONFIG_TYPE_IPV4))
+ __connman_service_wispr_start(service,
+ CONNMAN_IPCONFIG_TYPE_IPV4);
+
+ if (__connman_service_is_connected_state(service,
+ CONNMAN_IPCONFIG_TYPE_IPV6))
+ __connman_service_wispr_start(service,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
+}
+
static void default_changed(void)
{
struct connman_service *service = connman_service_get_default();
connman_setting_get_bool("AllowDomainnameUpdates"))
__connman_utsname_set_domainname(service->domainname);
- if (__connman_service_is_connected_state(service,
- CONNMAN_IPCONFIG_TYPE_IPV4))
- __connman_service_wispr_start(service,
- CONNMAN_IPCONFIG_TYPE_IPV4);
-
- if (__connman_service_is_connected_state(service,
- CONNMAN_IPCONFIG_TYPE_IPV6))
- __connman_service_wispr_start(service,
- CONNMAN_IPCONFIG_TYPE_IPV6);
+ start_wispr_when_connected(service);
/*
* Connect VPN automatically when new default service
nameserver_add_all(service, CONNMAN_IPCONFIG_TYPE_ALL);
dns_configuration_changed(service);
- if (__connman_service_is_connected_state(service,
- CONNMAN_IPCONFIG_TYPE_IPV4))
- __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4);
-
- if (__connman_service_is_connected_state(service,
- CONNMAN_IPCONFIG_TYPE_IPV6))
- __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV6);
+ start_wispr_when_connected(service);
service_save(service);
} else if (g_str_equal(name, "Timeservers.Configuration")) {
bool ignore[MAX_CONNMAN_SERVICE_TYPES] = { };
bool autoconnecting = false;
GList *list;
+ int index;
DBG("preferred %d sessions %d reason %s", preferred, active_count,
reason2string(reason));
continue;
}
+ index = __connman_service_get_index(service);
+ if (g_hash_table_lookup(passphrase_requested,
+ GINT_TO_POINTER(index)))
+ return true;
+
if (service->pending ||
is_connecting(service->state) ||
is_connected(service->state)) {
struct connman_service *def_service;
def_service = connman_service_get_default();
- if (!def_service)
+ if (!def_service || def_service != service ||
+ def_service->state != CONNMAN_SERVICE_STATE_ONLINE)
return;
- if (def_service == service &&
- def_service->state == CONNMAN_SERVICE_STATE_ONLINE) {
- def_service->state = CONNMAN_SERVICE_STATE_READY;
- __connman_notifier_leave_online(def_service->type);
- state_changed(def_service);
- }
+ downgrade_state(def_service);
}
static void switch_default_service(struct connman_service *default_service,
return service_compare(transport, service);
}
+static gint service_compare_preferred(struct connman_service *service_a,
+ struct connman_service *service_b)
+{
+ unsigned int *tech_array;
+ int i;
+
+ tech_array = connman_setting_get_uint_list("PreferredTechnologies");
+ if (tech_array) {
+ for (i = 0; tech_array[i]; i++) {
+ if (tech_array[i] == service_a->type)
+ return -1;
+
+ if (tech_array[i] == service_b->type)
+ return 1;
+ }
+ }
+ return 0;
+}
+
static gint service_compare(gconstpointer a, gconstpointer b)
{
struct connman_service *service_a = (void *) a;
if (service_a->order < service_b->order)
return 1;
+
+ rval = service_compare_preferred(service_a, service_b);
+ if (rval)
+ return rval;
}
if (state_a != state_b) {
return 1;
if (service_a->type != service_b->type) {
- unsigned int *tech_array;
- int i;
-
- tech_array = connman_setting_get_uint_list(
- "PreferredTechnologies");
- if (tech_array) {
- for (i = 0; tech_array[i]; i++) {
- if (tech_array[i] == service_a->type)
- return -1;
+ int rval;
- if (tech_array[i] == service_b->type)
- return 1;
- }
- }
+ rval = service_compare_preferred(service_a, service_b);
+ if (rval)
+ return rval;
if (service_a->type == CONNMAN_SERVICE_TYPE_ETHERNET)
return -1;
struct connman_device *device;
const char *security;
int err = 0;
+ int index;
DBG("RequestInput return, %p", service);
err = __connman_service_set_passphrase(service, passphrase);
done:
+ index = __connman_service_get_index(service);
+ g_hash_table_remove(passphrase_requested,
+ GINT_TO_POINTER(index));
+
if (err >= 0) {
/* We forget any previous error. */
set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
struct connman_service *new_service,
enum connman_service_state new_state)
{
- unsigned int *tech_array;
- int i;
-
- if (!default_service || default_service == new_service ||
- default_service->state != new_state)
+ if (!default_service || default_service == new_service)
return 0;
- tech_array = connman_setting_get_uint_list("PreferredTechnologies");
- if (tech_array) {
-
- for (i = 0; tech_array[i] != 0; i += 1) {
- if (default_service->type == tech_array[i])
- return -EALREADY;
-
- if (new_service->type == tech_array[i]) {
- switch_default_service(default_service,
- new_service);
- __connman_connection_update_gateway();
- return 0;
- }
- }
+ if (service_compare_preferred(default_service, new_service) > 0) {
+ switch_default_service(default_service,
+ new_service);
+ __connman_connection_update_gateway();
+ return 0;
}
return -EALREADY;
break;
case CONNMAN_SERVICE_STATE_IDLE:
+ if (old_state == CONNMAN_SERVICE_STATE_FAILURE &&
+ service->connect_reason ==
+ CONNMAN_SERVICE_CONNECT_REASON_NATIVE &&
+ service->error ==
+ CONNMAN_SERVICE_ERROR_INVALID_KEY) {
+ __connman_service_clear_error(service);
+ service_complete(service);
+ }
+
if (old_state != CONNMAN_SERVICE_STATE_DISCONNECT)
__connman_service_disconnect(service);
service->new_service = false;
- default_changed();
-
def_service = connman_service_get_default();
service_update_preferred_order(def_service, service, new_state);
+ default_changed();
+
__connman_service_set_favorite(service, true);
reply_pending(service, 0);
break;
case CONNMAN_SERVICE_STATE_FAILURE:
- if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) {
+ if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER ||
+ service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
connman_agent_report_error(service, service->path,
error2string(service->error),
report_error_cb,
int __connman_service_connect(struct connman_service *service,
enum connman_service_connect_reason reason)
{
+ int index;
int err;
DBG("service %p state %s connect reason %s -> %s",
service->provider)
connman_provider_disconnect(service->provider);
- if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) {
+ if (reason == CONNMAN_SERVICE_CONNECT_REASON_USER ||
+ reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
if (err == -ENOKEY || err == -EPERM) {
DBusMessage *pending = NULL;
const char *dbus_sender = get_dbus_sender(service);
if (service->hidden && err != -EINPROGRESS)
service->pending = pending;
+ if (err == -EINPROGRESS) {
+ index = __connman_service_get_index(service);
+ g_hash_table_replace(passphrase_requested,
+ GINT_TO_POINTER(index),
+ GINT_TO_POINTER(true));
+ }
+
return err;
}
}
if (__connman_network_get_weakness(network))
return service;
+ index = connman_network_get_index(network);
+
if (service->path) {
update_from_network(service, network);
+
+ if (service->ipconfig_ipv4)
+ __connman_ipconfig_set_index(service->ipconfig_ipv4,
+ index);
+
+ if (service->ipconfig_ipv6)
+ __connman_ipconfig_set_index(service->ipconfig_ipv6,
+ index);
+
__connman_connection_update_gateway();
return service;
}
update_from_network(service, network);
- index = connman_network_get_index(network);
-
if (!service->ipconfig_ipv4)
service->ipconfig_ipv4 = create_ip4config(service, index,
CONNMAN_IPCONFIG_METHOD_DHCP);
+ else
+ __connman_ipconfig_set_index(service->ipconfig_ipv4, index);
if (!service->ipconfig_ipv6)
service->ipconfig_ipv6 = create_ip6config(service, index);
+ else
+ __connman_ipconfig_set_index(service->ipconfig_ipv6, index);
service_register(service);
service_schedule_added(service);
service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, service_free);
+ passphrase_requested = g_hash_table_new(g_direct_hash, g_direct_equal);
+
services_notify = g_new0(struct _services_notify, 1);
services_notify->remove = g_hash_table_new_full(g_str_hash,
g_str_equal, g_free, NULL);
g_hash_table_destroy(service_hash);
service_hash = NULL;
+ g_hash_table_destroy(passphrase_requested);
+ passphrase_requested = NULL;
+
g_slist_free(counter_list);
counter_list = NULL;
*/
char *tethering_ident;
char *tethering_passphrase;
+ int tethering_freq;
bool enable_persistent; /* Save the tech state */
g_free(enc);
}
+ if (technology->tethering_freq == 0)
+ technology->tethering_freq = 2412;
+
+ g_key_file_set_integer(keyfile, identifier,
+ "Tethering.Freq",
+ technology->tethering_freq);
+
done:
g_free(identifier);
if (!driver || !driver->set_tethering)
continue;
- err = driver->set_tethering(technology, ident, passphrase,
- bridge, enabled);
+ err = driver->set_tethering(technology, bridge, enabled);
if (result == -EINPROGRESS)
continue;
return technology->type;
}
-bool connman_technology_get_wifi_tethering(const char **ssid,
- const char **psk)
+bool connman_technology_get_wifi_tethering(const struct connman_technology *technology,
+ const char **ssid, const char **psk,
+ int *freq)
{
- struct connman_technology *technology;
+ bool force = true;
if (!ssid || !psk)
return false;
*ssid = *psk = NULL;
- technology = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+ /* Workaround for the neard plugin */
+ if (!technology) {
+ technology = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+ force = false;
+ }
+
if (!technology)
return false;
- if (!technology->tethering)
+ if (!force && !technology->tethering)
return false;
*ssid = technology->tethering_ident;
*psk = technology->tethering_passphrase;
+ *freq = technology->tethering_freq;
return true;
}
identifier, "Tethering.Passphrase", NULL);
if (enc)
technology->tethering_passphrase = g_strcompress(enc);
+
+ technology->tethering_freq = g_key_file_get_integer(keyfile,
+ identifier, "Tethering.Freq", NULL);
+
done:
g_free(identifier);
DBUS_TYPE_STRING,
&technology->tethering_passphrase);
+ connman_dbus_dict_append_basic(&dict, "TetheringFreq",
+ DBUS_TYPE_INT32,
+ &technology->tethering_freq);
+
connman_dbus_dict_close(iter, &dict);
}
DBUS_TYPE_STRING,
&technology->tethering_passphrase);
}
+ } else if (g_str_equal(name, "TetheringFreq")) {
+ dbus_int32_t freq;
+
+ if (type != DBUS_TYPE_INT32)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value, &freq);
+
+ if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+ return __connman_error_not_supported(msg);
+
+ if (freq >= 0) {
+ technology->tethering_freq = freq;
+ technology_save(technology);
+
+ connman_dbus_property_changed_basic(technology->path,
+ CONNMAN_TECHNOLOGY_INTERFACE,
+ "TetheringFreq",
+ DBUS_TYPE_INT32,
+ &technology->tethering_freq);
+ }
} else if (g_str_equal(name, "Powered")) {
dbus_bool_t enable;
#include "connman.h"
-#define STATUS_URL_IPV4 "http://ipv4.connman.net/online/status.html"
-#define STATUS_URL_IPV6 "http://ipv6.connman.net/online/status.html"
-
struct connman_wispr_message {
bool has_error;
const char *current_element;
static GHashTable *wispr_portal_list = NULL;
+static char *online_check_ipv4_url = NULL;
+static char *online_check_ipv6_url = NULL;
static bool enable_online_to_ready_transition = false;
static void connman_wispr_message_init(struct connman_wispr_message *msg)
if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4) {
g_web_set_address_family(wp_context->web, AF_INET);
- wp_context->status_url = STATUS_URL_IPV4;
+ wp_context->status_url = online_check_ipv4_url;
} else {
g_web_set_address_family(wp_context->web, AF_INET6);
- wp_context->status_url = STATUS_URL_IPV6;
+ wp_context->status_url = online_check_ipv6_url;
}
for (i = 0; nameservers[i]; i++)
g_direct_equal, NULL,
free_connman_wispr_portal);
+ online_check_ipv4_url =
+ connman_setting_get_string("OnlineCheckIPv4URL");
+ online_check_ipv6_url =
+ connman_setting_get_string("OnlineCheckIPv6URL");
+
enable_online_to_ready_transition =
connman_setting_get_bool("EnableOnlineToReadyTransition");
{
enum iptables_command cmd = IPTABLES_COMMAND_UNKNOWN;
char *table = NULL, *chain = NULL, *rule = NULL, *tmp;
- int err, c, i;
+ int err = -EINVAL, c, i;
opterr = 0;
{
enum iptables_command cmd = IPTABLES_COMMAND_UNKNOWN;
char *table = NULL, *chain = NULL, *rule = NULL, *tmp;
- int err, c, i;
+ int err = -EINVAL, c, i;
opterr = 0;
#define IP6T_SO_GET_INFO (IP6T_BASE_CTL)
#define IP6T_SO_GET_ENTRIES (IP6T_BASE_CTL + 1)
-int xt_match_parse(int c, char **argv, int invert, unsigned int *flags,
+int static xt_match_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
return 0;
}
-int xt_target_parse(int c, char **argv, int invert, unsigned int *flags,
+int static xt_target_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **targetinfo)
{
return 0;
*
* Copyright (C) 2010,2013 BMW Car IT GmbH.
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ * Copyright (C) 2019-2021 Jolla Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
static DBusConnection *connection;
struct l2tp_private_data {
+ struct vpn_provider *provider;
struct connman_task *task;
char *if_name;
vpn_provider_connect_cb_t cb;
void *user_data;
};
+static void l2tp_connect_done(struct l2tp_private_data *data, int err)
+{
+ vpn_provider_connect_cb_t cb;
+ void *user_data;
+
+ if (!data || !data->cb)
+ return;
+
+ /* Ensure that callback is called only once */
+ cb = data->cb;
+ user_data = data->user_data;
+ data->cb = NULL;
+ data->user_data = NULL;
+ cb(data->provider, user_data, err);
+}
+
+static void free_private_data(struct l2tp_private_data *data)
+{
+ if (vpn_provider_get_plugin_data(data->provider) == data)
+ vpn_provider_set_plugin_data(data->provider, NULL);
+
+ l2tp_connect_done(data, EIO);
+ vpn_provider_unref(data->provider);
+ g_free(data->if_name);
+ g_free(data);
+}
+
static DBusMessage *l2tp_get_sec(struct connman_task *task,
DBusMessage *msg, void *user_data)
{
char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
char *ifname = NULL, *nameservers = NULL;
struct connman_ipaddress *ipaddress = NULL;
+ struct l2tp_private_data *data;
+
+ data = vpn_provider_get_plugin_data(provider);
dbus_message_iter_init(msg, &iter);
vpn_provider_set_string_hide_value(provider, "L2TP.Password",
NULL);
+ l2tp_connect_done(data, EACCES);
return VPN_STATE_AUTH_FAILURE;
}
- if (strcmp(reason, "connect"))
+ if (strcmp(reason, "connect")) {
+ l2tp_connect_done(data, EIO);
+
+ /*
+ * Stop the task to avoid potential looping of this state when
+ * authentication fails.
+ */
+ if (data && data->task)
+ connman_task_stop(data->task);
+
return VPN_STATE_DISCONNECT;
+ }
dbus_message_iter_recurse(&iter, &dict);
g_free(nameservers);
connman_ipaddress_free(ipaddress);
+ l2tp_connect_done(data, 0);
+
return VPN_STATE_CONNECT;
}
static void l2tp_died(struct connman_task *task, int exit_code, void *user_data)
{
+ struct l2tp_private_data *data = user_data;
char *conf_file;
- vpn_died(task, exit_code, user_data);
+ vpn_died(task, exit_code, data->provider);
conf_file = g_strdup_printf(VPN_STATEDIR "/connman-xl2tpd.conf");
unlink(conf_file);
conf_file = g_strdup_printf(VPN_STATEDIR "/connman-ppp-option.conf");
unlink(conf_file);
g_free(conf_file);
+
+ free_private_data(data);
}
struct request_input_reply {
return -EINPROGRESS;
}
-static int run_connect(struct vpn_provider *provider,
- struct connman_task *task, const char *if_name,
- vpn_provider_connect_cb_t cb, void *user_data,
+static int run_connect(struct l2tp_private_data *data,
const char *username, const char *password)
{
- char *l2tp_name, *pppd_name;
+ struct vpn_provider *provider = data->provider;
+ struct connman_task *task = data->task;
+ char *l2tp_name, *ctrl_name, *pppd_name;
int l2tp_fd, pppd_fd;
int err;
goto done;
}
- pppd_name = g_strdup_printf(VPN_STATEDIR "/connman-ppp-option.conf");
+ ctrl_name = g_strconcat(VPN_STATEDIR, "/connman-xl2tpd-control", NULL);
+
+ if (mkfifo(ctrl_name, S_IRUSR|S_IWUSR) != 0 && errno != EEXIST) {
+ connman_error("Error creating xl2tp control pipe");
+ g_free(l2tp_name);
+ g_free(ctrl_name);
+ close(l2tp_fd);
+ err = -EIO;
+ goto done;
+ }
+
+ pppd_name = g_strconcat(VPN_STATEDIR, "/connman-ppp-option.conf", NULL);
pppd_fd = open(pppd_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
if (pppd_fd < 0) {
connman_error("Error writing pppd config");
g_free(l2tp_name);
+ g_free(ctrl_name);
g_free(pppd_name);
close(l2tp_fd);
err = -EIO;
write_pppd_option(provider, pppd_fd);
connman_task_add_argument(task, "-D", NULL);
+ connman_task_add_argument(task, "-C", ctrl_name);
connman_task_add_argument(task, "-c", l2tp_name);
g_free(l2tp_name);
+ g_free(ctrl_name);
g_free(pppd_name);
close(l2tp_fd);
close(pppd_fd);
- err = connman_task_run(task, l2tp_died, provider,
- NULL, NULL, NULL);
+ err = connman_task_run(task, l2tp_died, data, NULL, NULL, NULL);
if (err < 0) {
connman_error("l2tp failed to start");
err = -EIO;
- goto done;
}
done:
- if (cb)
- cb(provider, user_data, err);
+ if (err)
+ l2tp_connect_done(data, -err);
return err;
}
-static void free_private_data(struct l2tp_private_data *data)
-{
- g_free(data->if_name);
- g_free(data);
-}
-
static void request_input_cb(struct vpn_provider *provider,
const char *username,
const char *password,
vpn_provider_set_string_hide_value(provider, "L2TP.Password",
password);
- run_connect(provider, data->task, data->if_name, data->cb,
- data->user_data, username, password);
-
- free_private_data(data);
+ run_connect(data, username, password);
}
static int l2tp_connect(struct vpn_provider *provider,
vpn_provider_connect_cb_t cb, const char *dbus_sender,
void *user_data)
{
+ struct l2tp_private_data *data;
const char *username, *password;
int err;
+ data = g_try_new0(struct l2tp_private_data, 1);
+ if (!data)
+ return -ENOMEM;
+
+ data->provider = vpn_provider_ref(provider);
+ data->task = task;
+ data->if_name = g_strdup(if_name);
+ data->cb = cb;
+ data->user_data = user_data;
+ vpn_provider_set_plugin_data(provider, data);
+
if (connman_task_set_notify(task, "getsec",
l2tp_get_sec, provider) != 0) {
err = -ENOMEM;
DBG("user %s password %p", username, password);
if (!username || !*username || !password || !*password) {
- struct l2tp_private_data *data;
-
- data = g_try_new0(struct l2tp_private_data, 1);
- if (!data)
- return -ENOMEM;
-
- data->task = task;
- data->if_name = g_strdup(if_name);
- data->cb = cb;
- data->user_data = user_data;
-
err = request_input(provider, request_input_cb, dbus_sender,
data);
- if (err != -EINPROGRESS) {
- free_private_data(data);
- goto done;
- }
+ if (err != -EINPROGRESS)
+ goto error;
+
return err;
}
-done:
- return run_connect(provider, task, if_name, cb, user_data,
- username, password);
+ return run_connect(data, username, password);
error:
- if (cb)
- cb(provider, user_data, err);
+ l2tp_connect_done(data, -err);
+ free_private_data(data);
return err;
}
GIOChannel *err_ch;
enum oc_connect_type connect_type;
bool tried_passphrase;
+ bool group_set;
};
typedef void (*request_input_reply_cb_t) (DBusMessage *reply,
const char *keys[] = { "OpenConnect.PKCSPassword",
"OpenConnect.Username",
"OpenConnect.Password",
+ "OpenConnect.SecondPassword",
"OpenConnect.Cookie",
NULL
};
{
struct process_form_data *form_data = user_data;
struct oc_private_data *data = form_data->data;
+ struct oc_form_opt_select *authgroup_opt;
struct oc_form_opt *opt;
const char *password;
+ const char *group;
+ int i;
g_mutex_lock(&form_data->mutex);
DBG("");
+ /*
+ * Special handling for "GROUP:" field, if present.
+ * Different group selections can make other fields disappear/appear
+ */
+ if (form_data->form->authgroup_opt) {
+ group = vpn_provider_get_string(data->provider, "OpenConnect.Group");
+ authgroup_opt = form_data->form->authgroup_opt;
+
+ if (group && !data->group_set) {
+ for (i = 0; i < authgroup_opt->nr_choices; i++) {
+ struct oc_choice *choice = authgroup_opt->choices[i];
+
+ if (!strcmp(group, choice->label)) {
+ DBG("Switching to auth group: %s", group);
+ openconnect_set_option_value(&authgroup_opt->form,
+ choice->name);
+ data->group_set = true;
+ form_data->status = OC_FORM_RESULT_NEWGROUP;
+ goto out;
+ }
+ }
+
+ connman_warn("Group choice %s not present", group);
+ data->err = -EACCES;
+ clear_provider_credentials(data->provider, true);
+ form_data->status = OC_FORM_RESULT_ERR;
+ goto out;
+ }
+ }
+
switch (data->connect_type) {
case OC_CONNECT_USERPASS:
case OC_CONNECT_COOKIE_WITH_USERPASS:
"OpenConnect.Username");
if (user)
opt->_value = strdup(user);
- } else if (opt->type == OC_FORM_OPT_PASSWORD) {
+ } else if (opt->type == OC_FORM_OPT_PASSWORD &&
+ g_str_has_prefix(opt->name, "password")) {
+
const char *pass = vpn_provider_get_string(
data->provider,
"OpenConnect.Password");
if (pass)
opt->_value = strdup(pass);
+ } else if (opt->type == OC_FORM_OPT_PASSWORD &&
+ g_str_has_prefix(opt->name, "secondary_password")) {
+ const char *pass = vpn_provider_get_string(
+ data->provider,
+ "OpenConnect.SecondPassword");
+ if (pass)
+ opt->_value = strdup(pass);
}
}
const char *vpnhost = NULL;
const char *username = NULL;
const char *password = NULL;
+ const char *second_password = NULL;
const char *pkcspassword = NULL;
const char *key;
DBusMessageIter iter, dict;
dbus_message_iter_get_basic(&value, &password);
vpn_provider_set_string_hide_value(data->provider,
"OpenConnect.Password", password);
+ } else if (g_str_equal(key, "OpenConnect.SecondPassword")) {
+ dbus_message_iter_next(&entry);
+ if (dbus_message_iter_get_arg_type(&entry)
+ != DBUS_TYPE_VARIANT)
+ break;
+ dbus_message_iter_recurse(&entry, &value);
+ if (dbus_message_iter_get_arg_type(&value)
+ != DBUS_TYPE_STRING)
+ break;
+ dbus_message_iter_get_basic(&value, &second_password);
+ vpn_provider_set_string_hide_value(data->provider,
+ "OpenConnect.SecondPassword",
+ second_password);
} else if (g_str_equal(key, "OpenConnect.PKCSPassword")) {
dbus_message_iter_next(&entry);
if (dbus_message_iter_get_arg_type(&entry)
DBusMessageIter dict;
int err;
void *agent;
+ bool use_second_password = false;
if (!data || !cb)
return -ESRCH;
username = vpn_provider_get_string(data->provider,
"OpenConnect.Username");
vpn_agent_append_user_info(&dict, data->provider, username);
+
+ use_second_password = vpn_provider_get_boolean(data->provider,
+ "OpenConnect.UseSecondPassword",
+ false);
+
+ if (use_second_password)
+ request_input_append_to_dict(data->provider, &dict,
+ request_input_append_password,
+ "OpenConnect.SecondPassword");
+
break;
case OC_CONNECT_PUBLICKEY:
return -EINVAL;
const char *certificate;
const char *username;
const char *password;
+ const char *second_password = NULL;
const char *private_key;
int err;
+ bool use_second_password = false;
connman_info("provider %p task %p", provider, task);
"OpenConnect.Username");
password = vpn_provider_get_string(provider,
"OpenConnect.Password");
+
+ use_second_password = vpn_provider_get_boolean(provider,
+ "OpenConnect.UseSecondPassword",
+ false);
+
+ if (use_second_password)
+ second_password = vpn_provider_get_string(provider,
+ "OpenConnect.SecondPassword");
+
if (!username || !password || !g_strcmp0(username, "-") ||
- !g_strcmp0(password, "-"))
+ !g_strcmp0(password, "-") ||
+ (use_second_password && !second_password))
goto request_input;
break;
static DBusConnection *connection;
+enum opt_type {
+ OPT_NONE = 0,
+ OPT_STRING = 1,
+ OPT_BOOL = 2,
+};
+
struct {
- const char *cm_opt;
- const char *ov_opt;
- char has_value;
+ const char *cm_opt;
+ const char *ov_opt;
+ const char *ov_opt_to_null;
+ enum opt_type opt_type;
} ov_options[] = {
- { "Host", "--remote", 1 },
- { "OpenVPN.CACert", "--ca", 1 },
- { "OpenVPN.Cert", "--cert", 1 },
- { "OpenVPN.Key", "--key", 1 },
- { "OpenVPN.MTU", "--tun-mtu", 1 },
- { "OpenVPN.NSCertType", "--ns-cert-type", 1 },
- { "OpenVPN.Proto", "--proto", 1 },
- { "OpenVPN.Port", "--port", 1 },
- { "OpenVPN.AuthUserPass", "--auth-user-pass", 1 },
- { "OpenVPN.AskPass", "--askpass", 1 },
- { "OpenVPN.AuthNoCache", "--auth-nocache", 0 },
- { "OpenVPN.TLSRemote", "--tls-remote", 1 },
- { "OpenVPN.TLSAuth", NULL, 1 },
- { "OpenVPN.TLSAuthDir", NULL, 1 },
- { "OpenVPN.TLSCipher", "--tls-cipher", 1},
- { "OpenVPN.Cipher", "--cipher", 1 },
- { "OpenVPN.Auth", "--auth", 1 },
- { "OpenVPN.CompLZO", "--comp-lzo", 0 },
- { "OpenVPN.RemoteCertTls", "--remote-cert-tls", 1 },
- { "OpenVPN.ConfigFile", "--config", 1 },
- { "OpenVPN.DeviceType", NULL, 1 },
- { "OpenVPN.Verb", "--verb", 1 },
- { "OpenVPN.Ping", "--ping", 1},
- { "OpenVPN.PingExit", "--ping-exit", 1},
- { "OpenVPN.RemapUsr1", "--remap-usr1", 1},
+ { "Host", "--remote", NULL, OPT_STRING},
+ { "OpenVPN.CACert", "--ca", NULL, OPT_STRING},
+ { "OpenVPN.Cert", "--cert", NULL, OPT_STRING},
+ { "OpenVPN.Key", "--key", NULL, OPT_STRING},
+ { "OpenVPN.MTU", "--tun-mtu", NULL, OPT_STRING},
+ { "OpenVPN.NSCertType", "--ns-cert-type", NULL, OPT_STRING},
+ { "OpenVPN.Proto", "--proto", NULL, OPT_STRING},
+ { "OpenVPN.Port", "--port", NULL, OPT_STRING},
+ /*
+ * If the AuthUserPass option is "-", provide the input via management
+ * interface. To facilitate this set the option as NULL.
+ */
+ { "OpenVPN.AuthUserPass", "--auth-user-pass", "-", OPT_STRING},
+ { "OpenVPN.AskPass", "--askpass", NULL, OPT_STRING},
+ { "OpenVPN.AuthNoCache", "--auth-nocache", NULL, OPT_BOOL},
+ { "OpenVPN.TLSRemote", "--tls-remote", NULL, OPT_STRING},
+ { "OpenVPN.TLSAuth", NULL, NULL, OPT_NONE},
+ { "OpenVPN.TLSCipher", "--tls-cipher", NULL, OPT_STRING},
+ { "OpenVPN.TLSAuthDir", NULL, NULL, OPT_NONE},
+ { "OpenVPN.Cipher", "--cipher", NULL, OPT_STRING},
+ { "OpenVPN.Auth", "--auth", NULL, OPT_STRING},
+ /* Is set to adaptive by default if value is omitted */
+ { "OpenVPN.CompLZO", "--comp-lzo", NULL, OPT_STRING},
+ { "OpenVPN.RemoteCertTls", "--remote-cert-tls", NULL, OPT_STRING},
+ { "OpenVPN.ConfigFile", "--config", NULL, OPT_STRING},
+ { "OpenVPN.DeviceType", NULL, NULL, OPT_NONE},
+ { "OpenVPN.Verb", "--verb", NULL, OPT_STRING},
+ { "OpenVPN.Ping", "--ping", NULL, OPT_STRING},
+ { "OpenVPN.PingExit", "--ping-exit", NULL, OPT_STRING},
+ { "OpenVPN.RemapUsr1", "--remap-usr1", NULL, OPT_STRING},
};
struct ov_private_data {
static int task_append_config_data(struct vpn_provider *provider,
struct connman_task *task)
{
- const char *option;
int i;
for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
- if (!ov_options[i].ov_opt)
- continue;
+ const char *ov_opt = ov_options[i].ov_opt;
+ const char *cm_opt = ov_options[i].cm_opt;
+ const char *option = NULL;
+ const char *opt_to_null;
- option = vpn_provider_get_string(provider,
- ov_options[i].cm_opt);
- if (!option)
+ switch (ov_options[i].opt_type) {
+ case OPT_NONE:
continue;
- /*
- * If the AuthUserPass option is "-", provide the input
- * via management interface
- */
- if (!strcmp(ov_options[i].cm_opt, "OpenVPN.AuthUserPass") &&
- !strcmp(option, "-"))
- option = NULL;
+ case OPT_STRING:
+ if (!ov_opt)
+ continue;
+
+ option = vpn_provider_get_string(provider, cm_opt);
+ /*
+ * A string option may be used alone without a value
+ * in which case the default value is used by OpenVPN.
+ */
+ if (!option && !vpn_provider_setting_key_exists(
+ provider, ov_opt))
+ continue;
- if (connman_task_add_argument(task,
- ov_options[i].ov_opt,
- ov_options[i].has_value ? option : NULL) < 0)
+ opt_to_null = ov_options[i].ov_opt_to_null;
+ if (opt_to_null && !g_strcmp0(option, opt_to_null))
+ option = NULL;
+
+ break;
+
+ case OPT_BOOL:
+ if (!ov_opt)
+ continue;
+
+ /* Ignore the boolean toggle if option is disabled. */
+ if (!vpn_provider_get_boolean(provider, cm_opt, false))
+ continue;
+
+ break;
+ }
+
+ if (connman_task_add_argument(task, ov_opt, option))
return -EIO;
}
const char *tmpdir;
struct ov_private_data *data;
+ /*
+ * Explicitly set limit of 10 for authentication errors. This defines
+ * the authentication error message limit from the server before VPN
+ * agent is instructed to clear the credentials. This is effective only
+ * after a successful connection has been made within CONNECT_OK_DIFF
+ * time. User defined value for "AuthErrorLimit" overrides this.
+ */
+ vpn_provider_set_auth_error_limit(provider, 10);
+
data = g_try_new0(struct ov_private_data, 1);
if (!data)
return -ENOMEM;
*
* Copyright (C) 2010,2013-2014 BMW Car IT GmbH.
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ * Copyright (C) 2019-2021 Jolla Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
static DBusConnection *connection;
struct pptp_private_data {
+ struct vpn_provider *provider;
struct connman_task *task;
char *if_name;
vpn_provider_connect_cb_t cb;
void *user_data;
};
+static void pptp_connect_done(struct pptp_private_data *data, int err)
+{
+ vpn_provider_connect_cb_t cb;
+ void *user_data;
+
+ if (!data || !data->cb)
+ return;
+
+ /* Ensure that callback is called only once */
+ cb = data->cb;
+ user_data = data->user_data;
+ data->cb = NULL;
+ data->user_data = NULL;
+ cb(data->provider, user_data, err);
+}
+
+static void free_private_data(struct pptp_private_data *data)
+{
+ if (vpn_provider_get_plugin_data(data->provider) == data)
+ vpn_provider_set_plugin_data(data->provider, NULL);
+
+ pptp_connect_done(data, EIO);
+ vpn_provider_unref(data->provider);
+ g_free(data->if_name);
+ g_free(data);
+}
+
static DBusMessage *pptp_get_sec(struct connman_task *task,
DBusMessage *msg, void *user_data)
{
char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
char *ifname = NULL, *nameservers = NULL;
struct connman_ipaddress *ipaddress = NULL;
+ struct pptp_private_data *data;
+
+ data = vpn_provider_get_plugin_data(provider);
dbus_message_iter_init(msg, &iter);
vpn_provider_set_string_hide_value(provider, "PPTP.Password",
NULL);
+ pptp_connect_done(data, EACCES);
return VPN_STATE_AUTH_FAILURE;
}
- if (strcmp(reason, "connect"))
+ if (strcmp(reason, "connect")) {
+ pptp_connect_done(data, EIO);
+
+ /*
+ * Stop the task to avoid potential looping of this state when
+ * authentication fails.
+ */
+ if (data && data->task)
+ connman_task_stop(data->task);
+
return VPN_STATE_DISCONNECT;
+ }
dbus_message_iter_recurse(&iter, &dict);
g_free(nameservers);
connman_ipaddress_free(ipaddress);
+ pptp_connect_done(data, 0);
return VPN_STATE_CONNECT;
}
}
}
+static void pptp_died(struct connman_task *task, int exit_code,
+ void *user_data)
+{
+ struct pptp_private_data *data = user_data;
+
+ vpn_died(task, exit_code, data->provider);
+
+ free_private_data(data);
+}
+
struct request_input_reply {
struct vpn_provider *provider;
vpn_provider_password_cb_t callback;
return -EINPROGRESS;
}
-static int run_connect(struct vpn_provider *provider,
- struct connman_task *task, const char *if_name,
- vpn_provider_connect_cb_t cb, void *user_data,
- const char *username, const char *password)
+static int run_connect(struct pptp_private_data *data, const char *username,
+ const char *password)
{
+ struct vpn_provider *provider = data->provider;
+ struct connman_task *task = data->task;
GString *pptp_opt_s;
const char *opt_s;
const char *host;
char *str;
int err, i;
- if (!username || !password) {
+ if (!username || !*username || !password || !*password) {
DBG("Cannot connect username %s password %p",
username, password);
err = -EINVAL;
connman_task_add_argument(task, "plugin",
SCRIPTDIR "/libppp-plugin.so");
- err = connman_task_run(task, vpn_died, provider,
- NULL, NULL, NULL);
+ err = connman_task_run(task, pptp_died, data, NULL, NULL, NULL);
if (err < 0) {
connman_error("pptp failed to start");
err = -EIO;
- goto done;
}
done:
- if (cb)
- cb(provider, user_data, err);
+ if (err)
+ pptp_connect_done(data, -err);
return err;
}
-static void free_private_data(struct pptp_private_data *data)
-{
- g_free(data->if_name);
- g_free(data);
-}
-
static void request_input_cb(struct vpn_provider *provider,
const char *username,
const char *password,
{
struct pptp_private_data *data = user_data;
- if (!username || !password)
+ if (!username || !*username || !password || !*password)
DBG("Requesting username %s or password failed, error %s",
username, error);
else if (error)
vpn_provider_set_string_hide_value(provider, "PPTP.Password",
password);
- run_connect(provider, data->task, data->if_name, data->cb,
- data->user_data, username, password);
-
- free_private_data(data);
+ run_connect(data, username, password);
}
static int pptp_connect(struct vpn_provider *provider,
vpn_provider_connect_cb_t cb, const char *dbus_sender,
void *user_data)
{
+ struct pptp_private_data *data;
const char *username, *password;
int err;
+ data = g_try_new0(struct pptp_private_data, 1);
+ if (!data)
+ return -ENOMEM;
+
+ data->provider = vpn_provider_ref(provider);
+ data->task = task;
+ data->if_name = g_strdup(if_name);
+ data->cb = cb;
+ data->user_data = user_data;
+ vpn_provider_set_plugin_data(provider, data);
+
DBG("iface %s provider %p user %p", if_name, provider, user_data);
if (connman_task_set_notify(task, "getsec",
DBG("user %s password %p", username, password);
- if (!username || !password) {
- struct pptp_private_data *data;
-
- data = g_try_new0(struct pptp_private_data, 1);
- if (!data)
- return -ENOMEM;
-
- data->task = task;
- data->if_name = g_strdup(if_name);
- data->cb = cb;
- data->user_data = user_data;
-
+ if (!username || !*username || !password || !*password) {
err = request_input(provider, request_input_cb, dbus_sender,
data);
- if (err != -EINPROGRESS) {
- free_private_data(data);
- goto done;
- }
+ if (err != -EINPROGRESS)
+ goto error;
+
return err;
}
-done:
- return run_connect(provider, task, if_name, cb, user_data,
- username, password);
+ return run_connect(data, username, password);
error:
- if (cb)
- cb(provider, user_data, err);
+ pptp_connect_done(data, -err);
+ free_private_data(data);
return err;
}
* ConnMan VPN daemon
*
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
- * Copyright (C) 2019 Jolla Ltd. All rights reserved.
+ * Copyright (C) 2019-2021 Jolla Ltd. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include <config.h>
#endif
+/*
+ * One hour difference in seconds between connections for checking whether to
+ * treat the authentication error as a real error or as a result of rapid
+ * transport change.
+ */
+#define CONNECT_OK_DIFF ((time_t)60*60)
+#define AUTH_ERROR_LIMIT_DEFAULT 1
+
+#define STATE_INTERVAL_DEFAULT 0
+
+#define CONNMAN_STATE_ONLINE "online"
+#define CONNMAN_STATE_OFFLINE "offline"
+#define CONNMAN_STATE_READY "ready"
+#define CONNMAN_STATE_IDLE "idle"
+
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <connman/log.h>
#include <gweb/gresolv.h>
#include <netdb.h>
+#include <time.h>
#include "../src/connman.h"
#include "connman/agent.h"
struct connman_ipaddress *prev_ipv4_addr;
struct connman_ipaddress *prev_ipv6_addr;
void *plugin_data;
+ unsigned int do_connect_timeout;
unsigned int auth_error_counter;
unsigned int conn_error_counter;
unsigned int signal_watch;
+ unsigned int auth_error_limit;
+ time_t previous_connect_time;
+};
+
+struct vpn_provider_connect_data {
+ DBusConnection *conn;
+ DBusMessage *msg;
+ struct vpn_provider *provider;
};
+static unsigned int get_connman_state_timeout;
+
+static guint connman_signal_watch;
+static guint connman_service_watch;
+
+static bool connman_online;
+static bool state_query_completed;
+
static void append_properties(DBusMessageIter *iter,
struct vpn_provider *provider);
static int vpn_provider_save(struct vpn_provider *provider);
+static void get_connman_state(void);
+
+static void set_state(const char *new_state)
+{
+ if (!new_state || !*new_state)
+ return;
+
+ DBG("old state %s new state %s",
+ connman_online ?
+ CONNMAN_STATE_ONLINE "/" CONNMAN_STATE_READY :
+ CONNMAN_STATE_OFFLINE "/" CONNMAN_STATE_IDLE,
+ new_state);
+
+ /* States "online" and "ready" mean connman is online */
+ if (!g_ascii_strcasecmp(new_state, CONNMAN_STATE_ONLINE) ||
+ !g_ascii_strcasecmp(new_state, CONNMAN_STATE_READY))
+ connman_online = true;
+ /* Everything else means connman is offline */
+ else
+ connman_online = false;
+
+ DBG("set state %s connman_online=%s ", new_state,
+ connman_online ? "true" : "false");
+}
+
static void free_route(gpointer data)
{
struct vpn_route *route = data;
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
+static gboolean do_connect_timeout_function(gpointer data)
+{
+ struct vpn_provider_connect_data *cdata = data;
+ struct vpn_provider *provider = cdata->provider;
+ int err;
+
+ DBG("");
+
+ /* Keep in main loop if connman is not online. */
+ if (!connman_online)
+ return G_SOURCE_CONTINUE;
+
+ provider->do_connect_timeout = 0;
+ err = __vpn_provider_connect(provider, cdata->msg);
+
+ if (err < 0 && err != -EINPROGRESS) {
+ g_dbus_send_message(cdata->conn,
+ __connman_error_failed(cdata->msg, -err));
+ cdata->msg = NULL;
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void do_connect_timeout_free(gpointer data)
+{
+ struct vpn_provider_connect_data *cdata = data;
+
+ if (cdata->msg)
+ g_dbus_send_message(cdata->conn,
+ __connman_error_operation_aborted(cdata->msg));
+
+ dbus_connection_unref(cdata->conn);
+ g_free(data);
+}
+
+static void do_connect_later(struct vpn_provider *provider,
+ DBusConnection *conn, DBusMessage *msg)
+{
+ struct vpn_provider_connect_data *cdata =
+ g_new0(struct vpn_provider_connect_data, 1);
+
+ cdata->conn = dbus_connection_ref(conn);
+ cdata->msg = dbus_message_ref(msg);
+ cdata->provider = provider;
+
+ if (provider->do_connect_timeout)
+ g_source_remove(provider->do_connect_timeout);
+
+ provider->do_connect_timeout =
+ g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1,
+ do_connect_timeout_function, cdata,
+ do_connect_timeout_free);
+}
+
static DBusMessage *do_connect(DBusConnection *conn, DBusMessage *msg,
void *data)
{
DBG("conn %p provider %p", conn, provider);
+ if (!connman_online) {
+ if (state_query_completed) {
+ DBG("%s not started - ConnMan not online/ready",
+ provider->identifier);
+ return __connman_error_failed(msg, ENOLINK);
+ }
+
+ DBG("%s start delayed - ConnMan state not queried",
+ provider->identifier);
+ do_connect_later(provider, conn, msg);
+ return NULL;
+ }
+
+ /* Cancel delayed connection if connmand is online. */
+ if (provider->do_connect_timeout) {
+ g_source_remove(provider->do_connect_timeout);
+ provider->do_connect_timeout = 0;
+ }
+
err = __vpn_provider_connect(provider, msg);
if (err < 0 && err != -EINPROGRESS)
return __connman_error_failed(msg, -err);
if (!provider)
return;
+ DBG("provider %p", provider);
+
provider->auth_error_counter = provider->conn_error_counter = 0;
}
static int vpn_provider_save(struct vpn_provider *provider)
{
GKeyFile *keyfile;
+ const char *value;
DBG("provider %p immutable %s", provider,
provider->immutable ? "yes" : "no");
g_key_file_set_string(keyfile, provider->identifier,
"VPN.Domain", provider->domain);
+ value = vpn_provider_get_string(provider, "AuthErrorLimit");
+ if (value)
+ g_key_file_set_string(keyfile, provider->identifier,
+ "AuthErrorLimit", value);
+
if (provider->user_networks) {
gchar **networks;
gsize network_count;
{
DBG("provider %p", provider);
+ if (provider->do_connect_timeout)
+ g_source_remove(provider->do_connect_timeout);
+
if (provider->notify_id != 0)
g_source_remove(provider->notify_id);
append_nameservers(iter, provider->nameservers);
}
+static time_t get_uptime(void)
+{
+ struct timespec t = { 0 };
+
+ if (clock_gettime(CLOCK_BOOTTIME, &t) == -1) {
+ connman_warn("clock_gettime() error %d, uptime failed", errno);
+ return 0;
+ }
+
+ return t.tv_sec;
+}
+
static int provider_indicate_state(struct vpn_provider *provider,
enum vpn_provider_state state)
{
provider->state = state;
if (state == VPN_PROVIDER_STATE_READY) {
+ provider->previous_connect_time = get_uptime();
+
connman_dbus_property_changed_basic(provider->path,
VPN_CONNECTION_INTERFACE, "Index",
DBUS_TYPE_INT32, &provider->index);
{
switch (error) {
case VPN_PROVIDER_ERROR_UNKNOWN:
+ provider->previous_connect_time = 0;
break;
case VPN_PROVIDER_ERROR_CONNECT_FAILED:
+ provider->previous_connect_time = 0;
++provider->conn_error_counter;
break;
case VPN_PROVIDER_ERROR_LOGIN_FAILED:
++provider->auth_error_counter;
break;
}
+
+ DBG("%p connect errors %d auth errors %d", provider,
+ provider->conn_error_counter,
+ provider->auth_error_counter);
}
int vpn_provider_indicate_error(struct vpn_provider *provider,
DBG("provider %p id %s error %d", provider, provider->identifier,
error);
+ /*
+ * Ignore adding of errors when the VPN is idle or not set. Calls may
+ * happen in a case when networks are rapidly changed and the call to
+ * vpn_died() is done before executing the connect_cb() from the
+ * plugin. Then vpn.c:vpn_died() executes the plugin specific died()
+ * function which may free the plugin private data, containing also
+ * the callback which hasn't yet been called. As a result the provider
+ * might already been reset to idle state when the callback is executed
+ * resulting in unnecessary reset of the previous successful connect
+ * timer and adding of an error for already disconnected VPN.
+ */
+ if (provider->state == VPN_PROVIDER_STATE_IDLE ||
+ provider->state == VPN_PROVIDER_STATE_UNKNOWN)
+ return 0;
+
vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
vpn_provider_add_error(provider, error);
g_free, free_route);
provider->setting_strings = g_hash_table_new_full(g_str_hash,
g_str_equal, g_free, free_setting);
+ provider->auth_error_limit = AUTH_ERROR_LIMIT_DEFAULT;
}
static struct vpn_provider *vpn_provider_new(void)
return setting->immutable;
}
+bool vpn_provider_setting_key_exists(struct vpn_provider *provider,
+ const char *key)
+{
+ return g_hash_table_contains(provider->setting_strings, key);
+}
+
+void vpn_provider_set_auth_error_limit(struct vpn_provider *provider,
+ unsigned int limit)
+{
+ if (!provider)
+ return;
+
+ provider->auth_error_limit = limit;
+}
+
bool __vpn_provider_check_routes(struct vpn_provider *provider)
{
if (!provider)
return provider->path;
}
+/*
+ * This crude heuristic is meant to mitigate an issue with certain VPN
+ * providers that allow only one authentication per account at a time. These
+ * providers require the VPN client shuts down cleanly by sending an exit
+ * notification. In many cases this is not possible as the transport may
+ * already be gone and there is no route to the VPN server. In such case server
+ * may return authentication error to indicate that an other client is active
+ * and reserves the slot.
+ *
+ * By allowing the VPN client to try again with the following conditons the
+ * unnecessary credential resets done by VPN agent can be avoided. VPN client
+ * is allowed to retry if 1) there was a successful connection to the server
+ * within the specified CONNECT_OK_DIFF time and 2) the provider specific
+ * limit for auth errors is not reached the unnecessary credential resets in
+ * this case are avoided.
+ *
+ * This feature is controlled by the provider specific value for
+ * "AuthErrorLimit". Setting the value to 0 feature is disabled. User defined
+ * value is preferred if set, otherwise plugin default set with
+ * vpn_provider_set_auth_error_limit() is used, which defaults to
+ * AUTH_ERROR_LIMIT_DEFAULT.
+ */
+static bool ignore_authentication_errors(struct vpn_provider *provider)
+{
+ const char *val;
+ unsigned int limit;
+ time_t uptime;
+ time_t diff;
+
+ val = vpn_provider_get_string(provider, "AuthErrorLimit");
+ if (val)
+ limit = g_ascii_strtoull(val, NULL, 10);
+ else
+ limit = provider->auth_error_limit;
+
+ if (!limit || !provider->previous_connect_time) {
+ DBG("%p errors %u %s", provider, provider->auth_error_counter,
+ !limit ?
+ "disabled by 0 limit" :
+ "no previous ok conn");
+ return false;
+ }
+
+ uptime = get_uptime();
+ diff = uptime - provider->previous_connect_time;
+
+ DBG("%p errors %u limit %u uptime %jd time diff %jd", provider,
+ provider->auth_error_counter, limit,
+ (intmax_t)uptime, (intmax_t)diff);
+
+ if (diff <= CONNECT_OK_DIFF && provider->auth_error_counter <= limit) {
+ DBG("ignore auth errors");
+ return true;
+ }
+
+ return false;
+}
+
unsigned int vpn_provider_get_authentication_errors(
struct vpn_provider *provider)
{
+ if (ignore_authentication_errors(provider))
+ return 0;
+
return provider->auth_error_counter;
}
g_strfreev(providers);
}
+static gboolean connman_property_changed(DBusConnection *conn,
+ DBusMessage *message,
+ void *user_data)
+{
+ DBusMessageIter iter, value;
+ const char *key;
+ const char *signature = DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING;
+
+ if (!dbus_message_has_signature(message, signature)) {
+ connman_error("vpn connman property signature \"%s\" "
+ "does not match expected \"%s\"",
+ dbus_message_get_signature(message),
+ signature);
+ return TRUE;
+ }
+
+ if (!dbus_message_iter_init(message, &iter))
+ return TRUE;
+
+ dbus_message_iter_get_basic(&iter, &key);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &value);
+
+ DBG("key %s", key);
+
+ if (g_str_equal(key, "State")) {
+ const char *str;
+
+ if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING)
+ return TRUE;
+
+ dbus_message_iter_get_basic(&value, &str);
+ set_state(str);
+ }
+
+ return TRUE;
+}
+
+static void get_connman_state_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = NULL;
+ DBusError error;
+ DBusMessageIter iter, array, dict, value;
+
+ const char *signature = DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+
+ const char *key;
+ const char *str;
+
+ DBG("");
+
+ if (!dbus_pending_call_get_completed(call))
+ goto done;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, reply)) {
+ connman_error("%s", error.message);
+
+ /*
+ * In case of timeout re-add the state function to main
+ * event loop.
+ */
+ if (g_ascii_strcasecmp(error.name, DBUS_ERROR_TIMEOUT) == 0) {
+ DBG("D-Bus timeout, re-add get_connman_state()");
+ get_connman_state();
+ } else {
+ dbus_error_free(&error);
+ goto done;
+ }
+ }
+
+ if (!dbus_message_has_signature(reply, signature)) {
+ connman_error("vpnd signature \"%s\" does not match "
+ "expected \"%s\"",
+ dbus_message_get_signature(reply),
+ signature);
+
+ goto done;
+ }
+
+ if (!dbus_message_iter_init(reply, &array))
+ goto done;
+
+ dbus_message_iter_recurse(&array, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_recurse(&dict, &iter);
+
+ dbus_message_iter_get_basic(&iter, &key);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &value);
+
+ if (g_ascii_strcasecmp(key, "State") == 0 &&
+ dbus_message_iter_get_arg_type(&value) ==
+ DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&value, &str);
+
+ DBG("Got initial state %s", str);
+
+ set_state(str);
+
+ /* No need to process further */
+ break;
+ }
+
+ dbus_message_iter_next(&dict);
+ }
+
+ state_query_completed = true;
+
+done:
+ if (reply)
+ dbus_message_unref(reply);
+
+ if (call)
+ dbus_pending_call_unref(call);
+}
+
+static gboolean run_get_connman_state(gpointer user_data)
+{
+ const char *path = "/";
+ const char *method = "GetProperties";
+ gboolean rval = FALSE;
+
+ DBusMessage *msg = NULL;
+ DBusPendingCall *call = NULL;
+
+ DBG("");
+
+ msg = dbus_message_new_method_call(CONNMAN_SERVICE, path,
+ CONNMAN_MANAGER_INTERFACE, method);
+ if (!msg)
+ goto out;
+
+ rval = g_dbus_send_message_with_reply(connection, msg, &call, -1);
+ if (!rval) {
+ connman_error("Cannot call %s on %s", method,
+ CONNMAN_MANAGER_INTERFACE);
+ goto out;
+ }
+
+ if (!call) {
+ connman_error("set pending call failed");
+ rval = FALSE;
+ goto out;
+ }
+
+ if (!dbus_pending_call_set_notify(call, get_connman_state_reply,
+ NULL, NULL)) {
+ connman_error("set notify to pending call failed");
+
+ if (call)
+ dbus_pending_call_unref(call);
+
+ rval = FALSE;
+ }
+
+out:
+ if (msg)
+ dbus_message_unref(msg);
+
+ /* In case sending was success, unset timeout function id */
+ if (rval) {
+ DBG("unsetting get_connman_state_timeout id");
+ get_connman_state_timeout = 0;
+ }
+
+ /* Return FALSE in case of success to remove from main event loop */
+ return !rval;
+}
+
+static void get_connman_state(void)
+{
+ if (get_connman_state_timeout)
+ return;
+
+ get_connman_state_timeout = g_timeout_add(STATE_INTERVAL_DEFAULT,
+ run_get_connman_state, NULL);
+}
+
+static void connman_service_watch_connected(DBusConnection *conn,
+ void *user_data)
+{
+ DBG("");
+
+ get_connman_state();
+}
+
+static void connman_service_watch_disconnected(DBusConnection *conn,
+ void *user_data)
+{
+ DBG("");
+
+ set_state(CONNMAN_STATE_IDLE);
+
+ /* Set state query variable to initial state */
+ state_query_completed = false;
+}
+
int __vpn_provider_init(void)
{
int err;
provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, unregister_provider);
+
+ connman_service_watch = g_dbus_add_service_watch(connection,
+ CONNMAN_SERVICE,
+ connman_service_watch_connected,
+ connman_service_watch_disconnected,
+ NULL, NULL);
+
+ connman_signal_watch = g_dbus_add_signal_watch(connection,
+ CONNMAN_SERVICE, NULL,
+ CONNMAN_MANAGER_INTERFACE,
+ PROPERTY_CHANGED,
+ connman_property_changed,
+ NULL, NULL);
+
return 0;
}
g_hash_table_destroy(provider_hash);
provider_hash = NULL;
+ if (get_connman_state_timeout) {
+ if (!g_source_remove(get_connman_state_timeout))
+ connman_error("connman state timeout not removed");
+ }
+
+ g_dbus_remove_watch(connection, connman_service_watch);
+ g_dbus_remove_watch(connection, connman_signal_watch);
+
dbus_connection_unref(connection);
}
bool force_change);
bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
bool default_value);
+bool vpn_provider_setting_key_exists(struct vpn_provider *provider,
+ const char *key);
+void vpn_provider_set_auth_error_limit(struct vpn_provider *provider,
+ unsigned int limit);
int vpn_provider_set_state(struct vpn_provider *provider,
enum vpn_provider_state state);
static GIOChannel *channel = NULL;
-struct rtnl_request {
- struct nlmsghdr hdr;
- struct rtgenmsg msg;
-};
-#define RTNL_REQUEST_SIZE (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
+#define RTNL_REQUEST_SIZE (NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(struct rtgenmsg)))
static GSList *request_list = NULL;
static guint32 request_seq = 0;
-static struct rtnl_request *find_request(guint32 seq)
+static struct nlmsghdr *find_request(guint32 seq)
{
GSList *list;
for (list = request_list; list; list = list->next) {
- struct rtnl_request *req = list->data;
+ struct nlmsghdr *hdr = list->data;
- if (req->hdr.nlmsg_seq == seq)
- return req;
+ if (hdr->nlmsg_seq == seq)
+ return hdr;
}
return NULL;
}
-static int send_request(struct rtnl_request *req)
+static int send_request(struct nlmsghdr *hdr)
{
struct sockaddr_nl addr;
int sk;
debug("%s len %d type %d flags 0x%04x seq %d",
- type2string(req->hdr.nlmsg_type),
- req->hdr.nlmsg_len, req->hdr.nlmsg_type,
- req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+ type2string(hdr->nlmsg_type),
+ hdr->nlmsg_len, hdr->nlmsg_type,
+ hdr->nlmsg_flags, hdr->nlmsg_seq);
sk = g_io_channel_unix_get_fd(channel);
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
- return sendto(sk, req, req->hdr.nlmsg_len, 0,
+ return sendto(sk, hdr, hdr->nlmsg_len, 0,
(struct sockaddr *) &addr, sizeof(addr));
}
-static int queue_request(struct rtnl_request *req)
+static int queue_request(struct nlmsghdr *hdr)
{
- request_list = g_slist_append(request_list, req);
+ request_list = g_slist_append(request_list, hdr);
if (g_slist_length(request_list) > 1)
return 0;
- return send_request(req);
+ return send_request(hdr);
}
static int process_response(guint32 seq)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
debug("seq %d", seq);
- req = find_request(seq);
- if (req) {
- request_list = g_slist_remove(request_list, req);
- g_free(req);
+ hdr = find_request(seq);
+ if (hdr) {
+ request_list = g_slist_remove(request_list, hdr);
+ g_free(hdr);
}
- req = g_slist_nth_data(request_list, 0);
- if (!req)
+ hdr = g_slist_nth_data(request_list, 0);
+ if (!hdr)
return 0;
- return send_request(req);
+ return send_request(hdr);
}
static void rtnl_message(void *buf, size_t len)
static int send_getlink(void)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
+ struct rtgenmsg *msg;
debug("");
- req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (!req)
- return -ENOMEM;
+ hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+ hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+ hdr->nlmsg_type = RTM_GETLINK;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = request_seq++;
- req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
- req->hdr.nlmsg_type = RTM_GETLINK;
- req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- req->hdr.nlmsg_pid = 0;
- req->hdr.nlmsg_seq = request_seq++;
- req->msg.rtgen_family = AF_INET;
+ msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+ msg->rtgen_family = AF_INET;
- return queue_request(req);
+ return queue_request(hdr);
}
static int send_getaddr(void)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
+ struct rtgenmsg *msg;
debug("");
- req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (!req)
- return -ENOMEM;
+ hdr = g_malloc0(RTNL_REQUEST_SIZE);
- req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
- req->hdr.nlmsg_type = RTM_GETADDR;
- req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- req->hdr.nlmsg_pid = 0;
- req->hdr.nlmsg_seq = request_seq++;
- req->msg.rtgen_family = AF_INET;
+ hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+ hdr->nlmsg_type = RTM_GETADDR;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = request_seq++;
- return queue_request(req);
+ msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+ msg->rtgen_family = AF_INET;
+
+ return queue_request(hdr);
}
static int send_getroute(void)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
+ struct rtgenmsg *msg;
debug("");
- req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (!req)
- return -ENOMEM;
+ hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+ hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+ hdr->nlmsg_type = RTM_GETROUTE;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = request_seq++;
- req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
- req->hdr.nlmsg_type = RTM_GETROUTE;
- req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- req->hdr.nlmsg_pid = 0;
- req->hdr.nlmsg_seq = request_seq++;
- req->msg.rtgen_family = AF_INET;
+ msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+ msg->rtgen_family = AF_INET;
- return queue_request(req);
+ return queue_request(hdr);
}
static gboolean update_timeout_cb(gpointer user_data)
update_list = NULL;
for (list = request_list; list; list = list->next) {
- struct rtnl_request *req = list->data;
+ struct nlmsghdr *hdr = list->data;
debug("%s len %d type %d flags 0x%04x seq %d",
- type2string(req->hdr.nlmsg_type),
- req->hdr.nlmsg_len, req->hdr.nlmsg_type,
- req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+ type2string(hdr->nlmsg_type),
+ hdr->nlmsg_len, hdr->nlmsg_type,
+ hdr->nlmsg_flags, hdr->nlmsg_seq);
- g_free(req);
+ g_free(hdr);
list->data = NULL;
}