summaryrefslogtreecommitdiff
path: root/vpn/plugins/vpn.c
diff options
context:
space:
mode:
Diffstat (limited to 'vpn/plugins/vpn.c')
-rw-r--r--vpn/plugins/vpn.c197
1 files changed, 191 insertions, 6 deletions
diff --git a/vpn/plugins/vpn.c b/vpn/plugins/vpn.c
index acede747..e04670c8 100644
--- a/vpn/plugins/vpn.c
+++ b/vpn/plugins/vpn.c
@@ -33,6 +33,9 @@
#include <sys/types.h>
#include <linux/if_tun.h>
#include <net/if.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
#include <dbus/dbus.h>
@@ -47,6 +50,7 @@
#include "../vpn-provider.h"
#include "vpn.h"
+#include "../vpn.h"
struct vpn_data {
struct vpn_provider *provider;
@@ -85,8 +89,10 @@ static int stop_vpn(struct vpn_provider *provider)
vpn_driver_data = g_hash_table_lookup(driver_hash, name);
if (vpn_driver_data && vpn_driver_data->vpn_driver &&
- vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
+ vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_TUN) {
+ vpn_driver_data->vpn_driver->disconnect(data->provider);
return 0;
+ }
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = data->tun_flags | IFF_NO_PI;
@@ -284,7 +290,7 @@ static DBusMessage *vpn_notify(struct connman_task *task,
* We need to remove first the old address, just
* replacing the old address will not work as expected
* because the old address will linger in the interface
- * and not disapper so the clearing is needed here.
+ * and not disappear so the clearing is needed here.
*
* Also the state must change, otherwise the routes
* will not be set properly.
@@ -415,12 +421,155 @@ exist_err:
return ret;
}
+static gboolean is_numeric(const char *str)
+{
+ gint i;
+
+ if(!str || !(*str))
+ return false;
+
+ for(i = 0; str[i] ; i++) {
+ if(!g_ascii_isdigit(str[i]))
+ return false;
+ }
+
+ return true;
+}
+
+static gint get_gid(const char *group_name)
+{
+ gint gid = -1;
+ struct group *grp;
+
+ if(!group_name || !(*group_name))
+ return gid;
+
+ if (is_numeric(group_name)) {
+ gid_t group_id = (gid_t)g_ascii_strtoull(group_name, NULL, 10);
+ grp = getgrgid(group_id);
+ } else {
+ grp = getgrnam(group_name);
+ }
+
+ if (grp)
+ gid = grp->gr_gid;
+
+ return gid;
+}
+
+static gint get_uid(const char *user_name)
+{
+ gint uid = -1;
+ struct passwd *pw;
+
+ if(!user_name || !(*user_name))
+ return uid;
+
+ if (is_numeric(user_name)) {
+ uid_t user_id = (uid_t)g_ascii_strtoull(user_name, NULL, 10);
+ pw = getpwuid(user_id);
+ } else {
+ pw = getpwnam(user_name);
+ }
+
+ if (pw)
+ uid = pw->pw_uid;
+
+ return uid;
+}
+
+static gint get_supplementary_gids(gchar **groups, gid_t **gid_list)
+{
+ gint group_count = 0;
+ gid_t *list = NULL;
+ int i;
+
+ if (groups) {
+ for(i = 0; groups[i]; i++) {
+ group_count++;
+
+ list = (gid_t*)g_try_realloc(list,
+ sizeof(gid_t) * group_count);
+
+ if (!list) {
+ DBG("cannot allocate supplementary group list");
+ break;
+ }
+
+ list[i] = get_gid(groups[i]);
+ }
+ }
+
+ *gid_list = list;
+
+ return group_count;
+}
+
+static void vpn_task_setup(gpointer user_data)
+{
+ struct vpn_plugin_data *data;
+ gint uid;
+ gint gid;
+ gid_t *gid_list = NULL;
+ size_t gid_list_size;
+ const gchar *user;
+ const gchar *group;
+ gchar **suppl_groups;
+
+ data = user_data;
+ user = vpn_settings_get_binary_user(data);
+ group = vpn_settings_get_binary_group(data);
+ suppl_groups = vpn_settings_get_binary_supplementary_groups(data);
+
+ uid = get_uid(user);
+ gid = get_gid(group);
+ gid_list_size = get_supplementary_gids(suppl_groups, &gid_list);
+
+ DBG("vpn_task_setup uid:%d gid:%d supplementary group list size:%zu",
+ uid, gid, gid_list_size);
+
+
+ /* Change group if proper group name was set, requires CAP_SETGID.*/
+ if (gid > 0 && setgid(gid))
+ connman_error("error setting gid %d %s", gid, strerror(errno));
+
+ /* Set the supplementary groups if list exists, requires CAP_SETGID. */
+ if (gid_list_size && gid_list && setgroups(gid_list_size, gid_list))
+ connman_error("error setting gid list %s", strerror(errno));
+
+ /* Change user for the task if set, requires CAP_SETUID */
+ if (uid > 0 && setuid(uid))
+ connman_error("error setting uid %d %s", uid, strerror(errno));
+}
+
+
+static gboolean update_provider_state(gpointer data)
+{
+ struct vpn_provider *provider = data;
+ struct vpn_data *vpn_data;
+ int index;
+
+ DBG("");
+
+ vpn_data = vpn_provider_get_data(provider);
+
+ index = vpn_provider_get_index(provider);
+ DBG("index to watch %d", index);
+ vpn_provider_ref(provider);
+ vpn_data->watch = vpn_rtnl_add_newlink_watch(index,
+ vpn_newlink, provider);
+ connman_inet_ifup(index);
+
+ return FALSE;
+}
+
static int vpn_connect(struct vpn_provider *provider,
vpn_provider_connect_cb_t cb,
const char *dbus_sender, void *user_data)
{
struct vpn_data *data = vpn_provider_get_data(provider);
struct vpn_driver_data *vpn_driver_data;
+ struct vpn_plugin_data *vpn_plugin_data;
const char *name;
int ret = 0, tun_flags = IFF_TUN;
enum vpn_state state = VPN_STATE_UNKNOWN;
@@ -469,7 +618,7 @@ static int vpn_connect(struct vpn_provider *provider,
goto exist_err;
}
- if (vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
+ if (!(vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_TUN)) {
if (vpn_driver_data->vpn_driver->device_flags) {
tun_flags = vpn_driver_data->vpn_driver->device_flags(provider);
}
@@ -478,7 +627,30 @@ static int vpn_connect(struct vpn_provider *provider,
goto exist_err;
}
- data->task = connman_task_create(vpn_driver_data->program);
+
+ if (vpn_driver_data && vpn_driver_data->vpn_driver &&
+ vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_DAEMON) {
+
+ ret = vpn_driver_data->vpn_driver->connect(provider,
+ NULL, NULL, NULL, NULL, NULL);
+ if (ret) {
+ stop_vpn(provider);
+ goto exist_err;
+ }
+
+ DBG("%s started with dev %s",
+ vpn_driver_data->provider_driver.name, data->if_name);
+
+ data->state = VPN_STATE_CONNECT;
+
+ g_timeout_add(1, update_provider_state, provider);
+ return -EINPROGRESS;
+ }
+
+ vpn_plugin_data =
+ vpn_settings_get_vpn_plugin_config(vpn_driver_data->name);
+ data->task = connman_task_create(vpn_driver_data->program,
+ vpn_task_setup, vpn_plugin_data);
if (!data->task) {
ret = -ENOMEM;
@@ -553,7 +725,15 @@ static int vpn_disconnect(struct vpn_provider *provider)
}
data->state = VPN_STATE_DISCONNECT;
- connman_task_stop(data->task);
+
+ if (!vpn_driver_data->vpn_driver->disconnect) {
+ DBG("Driver has no disconnect() implementation, set provider "
+ "state to disconnect.");
+ vpn_provider_set_state(provider, VPN_PROVIDER_STATE_DISCONNECT);
+ }
+
+ if (data->task)
+ connman_task_stop(data->task);
return 0;
}
@@ -577,7 +757,8 @@ static int vpn_remove(struct vpn_provider *provider)
data->watch = 0;
}
- connman_task_stop(data->task);
+ if (data->task)
+ connman_task_stop(data->task);
g_usleep(G_USEC_PER_SEC);
stop_vpn(provider);
@@ -643,6 +824,9 @@ int vpn_register(const char *name, struct vpn_driver *vpn_driver,
data->name = name;
data->program = program;
+ if (vpn_settings_parse_vpn_plugin_config(data->name) != 0)
+ DBG("No configuration provided for VPN plugin %s", data->name);
+
data->vpn_driver = vpn_driver;
data->provider_driver.name = name;
@@ -681,6 +865,7 @@ void vpn_unregister(const char *name)
return;
vpn_provider_driver_unregister(&data->provider_driver);
+ vpn_settings_delete_vpn_plugin_config(name);
g_hash_table_remove(driver_hash, name);