Commit d30a73b2 authored by Jussi Laakkonen's avatar Jussi Laakkonen

[connman-vpn] Add support for configurable user and groups to run VPN binary. JB#39969

Improved VPN settings. Moved VPN settings from connman/vpn/main.c to
connman/vpn/vpn-settings.c to support configurable user and groups
for connman tasks (VPN binaries).

Added loading of username, group and supplementary groups from connman
VPN config file (defaults to /etc/connman/connman-vpn.conf) inside
config group VPNBinary. Leading and trailing whitespaces are removed
from each config parameter.

Configurable VPN binary variables added to connman-vpn.conf:
 - User = user to use for running VPN binary (text or numeric)
 - Group = the main group to use for running VPN binary (text or
   numeric)
 - SupplementaryGroups = supplementary groups used (separator: comma,
   text or numeric)

These config parameters are used by the custom task setup function
vpn_task_setup() (added to vpn/plugins/vpn.c, function type into
vpn/plugins/vpn.h). The custom task setup function is added to the
task with connman_task_create(), and run by src/task.c:task_setup().

The added vpn_task_setup() function sets the user and groups for
starting the VPN binary defined by the VPN plugin before executing
exec() (by g_spawn_async_with_pipes). Changes to user and/or groups are
made if these parameters are defined in the config. Because of this
Connman systemd capabilities list was incremented with CAP_SETGID and
CAP_SETUID.

Unit tests for vpn-settings.c are added and it tests functionality with
no config file, empty config file, config file with minimum content and
config file with full content.

Currently on Sailfish Os the plugin requires "vpn" as main group, and inet
+ net_admin groups are required as supplementary groups to gain necessary
access to resources.
parent 1461efa8
......@@ -71,6 +71,7 @@ unit/test-dnsproxy
unit/test-access
unit/test-sailfish_access
unit/test-sailfish_wakeup_timer
unit/test-vpn-settings
*.gcda
*.gcno
......
......@@ -134,7 +134,7 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) $(backtrace_sources) \
src/inotify.c src/firewall.c src/ipv6pd.c src/peer.c \
src/peer_service.c src/machine.c src/util.c \
src/wakeup_timer.c src/jolla-stats.c src/fsid.c \
src/access.c
src/access.c vpn/vpn-settings.c
src_connmand_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ @GNUTLS_LIBS@ \
......@@ -168,7 +168,7 @@ vpn_connman_vpnd_SOURCES = $(builtin_vpn_sources) $(backtrace_sources) \
vpn/vpn-ipconfig.c src/inet.c vpn/vpn-rtnl.c \
src/dbus.c src/storage.c src/ipaddress.c src/agent.c \
vpn/vpn-agent.c vpn/vpn-agent.h src/inotify.c \
vpn/vpn-config.c src/fsid.c
vpn/vpn-config.c src/fsid.c vpn/vpn-settings.c
vpn_connman_vpnd_LDADD = gdbus/libgdbus-internal.la $(builtin_vpn_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ @GNUTLS_LIBS@ \
......@@ -239,6 +239,7 @@ src_connmand_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
-DSCRIPTDIR=\""$(build_scriptdir)"\" \
-DDEFAULT_STORAGE_ROOT=\""$(storageroot)\"" \
-DCONFIGDIR=\""$(configdir)\"" \
-DDEFAULT_VPN_STATEDIR=\""$(vpn_statedir)"\" \
-I$(builddir)/src
EXTRA_DIST = src/genbuiltin src/connman-dbus.conf src/connman-polkit.conf \
......@@ -283,7 +284,7 @@ client_connmanctl_LDADD = gdbus/libgdbus-internal.la @DBUS_LIBS@ @GLIB_LIBS@ \
endif
noinst_PROGRAMS += unit/test-access unit/test-dnsproxy unit/test-ippool \
unit/test-sailfish_access
unit/test-sailfish_access unit/test-vpn-settings
if TEST_COVERAGE
COVERAGE_OPT = --coverage
......@@ -311,8 +312,14 @@ unit_test_dnsproxy_SOURCES = unit/test-dnsproxy.c src/log.c \
$(backtrace_sources)
unit_test_dnsproxy_LDADD = @GLIB_LIBS@ -lresolv -ldl
unit_test_vpn_settings_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS) \
-DDEFAULT_VPN_STATEDIR=\""$(vpn_statedir)"\"
unit_test_vpn_settings_SOURCES = $(backtrace_sources) unit/test-vpn-settings.c \
vpn/vpn-settings.c src/log.c
unit_test_vpn_settings_LDADD = @GLIB_LIBS@ -ldl
TESTS = unit/test-access unit/test-ippool unit/test-dnsproxy \
unit/test-sailfish_access
unit/test-sailfish_access unit/test-vpn-settings
if SAILFISH_WAKEUP_TIMER
unit_test_sailfish_wakeup_timer_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
......
......@@ -42,7 +42,10 @@ typedef void (* connman_task_exit_t) (struct connman_task *task,
typedef DBusMessage * (* connman_task_notify_t) (struct connman_task *task,
DBusMessage *message, void *user_data);
struct connman_task *connman_task_create(const char *program);
typedef void (* connman_task_setup_t) (void *user_data);
struct connman_task *connman_task_create(const char *program,
connman_task_setup_t custom_task_setup);
void connman_task_destroy(struct connman_task *task);
const char *connman_task_get_path(struct connman_task *task);
......
......@@ -45,6 +45,7 @@ struct connman_task {
GPtrArray *argv;
GPtrArray *envp;
connman_task_exit_t exit_func;
connman_task_setup_t custom_setup_func;
void *exit_data;
GHashTable *notify;
};
......@@ -93,7 +94,8 @@ static void free_task(gpointer data)
*
* Returns: a newly-allocated #connman_task structure
*/
struct connman_task *connman_task_create(const char *program)
struct connman_task *connman_task_create(const char *program,
connman_task_setup_t custom_task_setup)
{
struct connman_task *task;
gint counter;
......@@ -115,6 +117,8 @@ struct connman_task *connman_task_create(const char *program)
str = g_strdup(program);
g_ptr_array_add(task->argv, str);
task->custom_setup_func = custom_task_setup;
task->notify = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, g_free);
......@@ -277,6 +281,9 @@ static void task_setup(gpointer user_data)
sigemptyset(&mask);
if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0)
connman_error("Failed to clean signal mask");
if (task->custom_setup_func)
task->custom_setup_func(user_data);
}
/**
......
/*
*
* ConnMan VPN daemon settings unit tests
*
* Copyright (C) 2018 Jolla Ltd. All rights reserved.
* Contact: jussi.laakkonen@jolla.com
*
* 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
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <glib.h>
#include <glib/gstdio.h>
#include <unistd.h>
#include "../src/connman.h"
#include "../vpn/vpn.h"
#define TEST_PREFIX "/vpn-settings"
#define TEST_PATH_PREFIX "/tmp/connman_test"
gchar* setup_test_directory()
{
gchar *test_path = NULL;
test_path = g_strdup_printf("%s.XXXXXX", TEST_PATH_PREFIX);
g_assert(test_path);
if(!g_file_test(test_path, G_FILE_TEST_EXISTS))
test_path = g_mkdtemp(test_path);
g_assert(g_file_test(test_path, G_FILE_TEST_EXISTS));
g_assert(g_file_test(test_path, G_FILE_TEST_IS_DIR));
return test_path;
}
void cleanup_test_directory(gchar *test_path)
{
gint access_mode = R_OK|W_OK|X_OK;
if(g_file_test(test_path, G_FILE_TEST_IS_DIR))
{
g_assert(!access(test_path, access_mode));
g_rmdir(test_path);
}
}
void set_and_verify_content(const gchar *file, gchar **content_in)
{
const char separator[] = "\n";
gchar *content = NULL;
gchar *content_verify = NULL;
gsize content_verify_len = 0;
g_assert(file);
if(!content_in || g_strv_length(content_in) == 0)
content = g_strdup("");
else
content = g_strjoinv(separator, content_in);
g_assert(g_file_set_contents(file, content, -1, NULL));
g_assert(g_file_get_contents(file, &content_verify, &content_verify_len,
NULL));
g_assert(g_ascii_strcasecmp(content, content_verify) == 0);
g_free(content);
g_free(content_verify);
}
void test_vpn_settings_no_config()
{
gchar* test_path = setup_test_directory();
gchar* file_path = g_strdup_printf("%s%s", test_path, "/connman-vpn.conf");
mode_t dir_p = 0700, file_p = 0600, umask = 0077;
gint timeout = 300 * 1000;
__vpn_settings_init(file_path);
//g_assert(g_ascii_strcasecmp( __vpn_settings_state_dir()) == 0);
g_assert(__vpn_settings_get_fs_identity() == NULL);
g_assert(__vpn_settings_get_storage_root() == NULL);
g_assert(__vpn_settings_get_storage_dir_permissions() == dir_p);
g_assert(__vpn_settings_get_storage_file_permissions() == file_p);
g_assert(__vpn_settings_get_umask() == umask);
g_assert(__vpn_settings_get_timeout_inputreq() == timeout);
g_assert(__vpn_settings_get_binary_user() == NULL);
g_assert(__vpn_settings_get_binary_group() == NULL);
char **groups = __vpn_settings_get_binary_supplementary_groups();
g_assert(groups == NULL);
__vpn_settings_free();
cleanup_test_directory(test_path);
g_free(test_path);
g_free(file_path);
}
void test_vpn_settings_empty_config()
{
gchar* test_path = setup_test_directory();
gchar* file_path = g_strdup_printf("%s%s", test_path, "/connman-vpn.conf");
mode_t dir_p = 0700, file_p = 0600, umask = 0077;
gint timeout = 300 * 1000;
set_and_verify_content(file_path, NULL);
__vpn_settings_init(file_path);
//g_assert(g_ascii_strcasecmp( __vpn_settings_state_dir()) == 0);
g_assert(__vpn_settings_get_fs_identity() == NULL);
g_assert(__vpn_settings_get_storage_root() == NULL);
g_assert(__vpn_settings_get_storage_dir_permissions() == dir_p);
g_assert(__vpn_settings_get_storage_file_permissions() == file_p);
g_assert(__vpn_settings_get_umask() == umask);
g_assert(__vpn_settings_get_timeout_inputreq() == timeout);
g_assert(__vpn_settings_get_binary_user() == NULL);
g_assert(__vpn_settings_get_binary_group() == NULL);
char **groups = __vpn_settings_get_binary_supplementary_groups();
g_assert(groups == NULL);
__vpn_settings_free();
cleanup_test_directory(test_path);
g_free(test_path);
g_free(file_path);
}
void test_vpn_settings_min_config()
{
gchar* test_path = setup_test_directory();
gchar* file_path = g_strdup_printf("%s%s", test_path, "/connman-vpn.conf");
gchar * content_min[] = {
"# ConnMan vpn-settings test minimal",
"[General]",
"InputRequestTimeout = 200",
"[VPNBinary]",
"User = user",
"Group = vpn",
"SupplementaryGroups = inet, net_admin, net_raw",
NULL
};
gchar **groups = NULL;
const gchar const * group_verify[] = {"inet", "net_admin", "net_raw", NULL};
mode_t dir_p = 0700, file_p = 0600, umask = 0077;
gint i = 0;
gint timeout = 200 * 1000;
set_and_verify_content(file_path, content_min);
__vpn_settings_init(file_path);
g_assert(__vpn_settings_get_fs_identity() == NULL);
g_assert(__vpn_settings_get_storage_root() == NULL);
g_assert(__vpn_settings_get_storage_dir_permissions() == dir_p);
g_assert(__vpn_settings_get_storage_file_permissions() == file_p);
g_assert(__vpn_settings_get_umask() == umask);
g_assert(__vpn_settings_get_timeout_inputreq() == timeout);
g_assert(g_ascii_strcasecmp(__vpn_settings_get_binary_user(), "user") == 0);
g_assert(g_ascii_strcasecmp(__vpn_settings_get_binary_group(), "vpn") == 0);
groups = __vpn_settings_get_binary_supplementary_groups();
g_assert(groups);
for(i = 0; groups[i]; i++)
g_assert(g_ascii_strcasecmp(groups[i], group_verify[i]) == 0);
__vpn_settings_free();
cleanup_test_directory(test_path);
g_free(test_path);
g_free(file_path);
}
void test_vpn_settings_full_config()
{
gchar* test_path = setup_test_directory();
gchar* file_path = g_strdup_printf("%s%s", test_path, "/connman-vpn.conf");
gchar * content_full[] = {
"# ConnMan vpn-settings test full",
"[General]",
"FileSystemIdentity = root",
"StateDirectory = /tmp/state",
"StorageRoot = /tmp/storage",
"StorageDirPermissions = 0754",
"StorageFilePermissions = 0645",
"Umask = 0067",
"InputRequestTimeout = 100",
"[VPNBinary]",
"User = user",
"Group = vpn",
"SupplementaryGroups = inet,net_admin",
NULL
};
gchar **groups = NULL;
const gchar const * group_verify[] = {"inet", "net_admin", NULL};
mode_t dir_p = 0754, file_p = 0645, umask = 0067;
gint i = 0;
gint timeout = 100 * 1000;
set_and_verify_content(file_path, content_full);
__vpn_settings_init(file_path);
g_assert(g_ascii_strcasecmp(__vpn_settings_get_fs_identity(),
"root")== 0);
g_assert(g_ascii_strcasecmp( __vpn_settings_state_dir(),
"/tmp/state") == 0);
g_assert(g_ascii_strcasecmp(__vpn_settings_get_storage_root(),
"/tmp/storage") == 0);
g_assert(__vpn_settings_get_storage_dir_permissions() == dir_p);
g_assert(__vpn_settings_get_storage_file_permissions() == file_p);
g_assert(__vpn_settings_get_umask() == umask);
g_assert(__vpn_settings_get_timeout_inputreq() == timeout);
g_assert(g_ascii_strcasecmp(__vpn_settings_get_binary_user(), "user") == 0);
g_assert(g_ascii_strcasecmp(__vpn_settings_get_binary_group(), "vpn") == 0);
groups = __vpn_settings_get_binary_supplementary_groups();
g_assert(groups);
for(i = 0; groups[i]; i++)
g_assert(g_ascii_strcasecmp(groups[i], group_verify[i]) == 0);
__vpn_settings_free();
cleanup_test_directory(test_path);
g_free(test_path);
g_free(file_path);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_PREFIX "/no_file", test_vpn_settings_no_config);
g_test_add_func(TEST_PREFIX "/empty_file", test_vpn_settings_empty_config);
g_test_add_func(TEST_PREFIX "/min_file", test_vpn_settings_min_config);
g_test_add_func(TEST_PREFIX "/full_file", test_vpn_settings_full_config);
return g_test_run();
}
......@@ -6,3 +6,10 @@
# increase the value in case of different user
# interface designs.
# InputRequestTimeout = 300
[VPNBinary]
User = nemo
Group = vpn
SupplementaryGroups = inet,net_admin
......@@ -8,7 +8,7 @@ Type=dbus
BusName=net.connman.vpn
ExecStart=@sbindir@/connman-vpnd -n
StandardOutput=null
CapabilityBoundingSet=CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_DAC_OVERRIDE
CapabilityBoundingSet=CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_DAC_OVERRIDE CAP_SETGID CAP_SETUID
ProtectSystem=full
[Install]
......
......@@ -44,125 +44,10 @@
#define CONFIGMAINFILE CONFIGDIR "/connman-vpn.conf"
#define DEFAULT_INPUT_REQUEST_TIMEOUT 300 * 1000
#define DEFAULT_STORAGE_DIR_PERMISSIONS (0700)
#define DEFAULT_STORAGE_FILE_PERMISSIONS (0600)
#define DEFAULT_UMASK (0077)
static GMainLoop *main_loop = NULL;
static unsigned int __terminated = 0;
static struct {
unsigned int timeout_inputreq;
char *fs_identity;
char *storage_root;
char *state_dir;
mode_t storage_dir_permissions;
mode_t storage_file_permissions;
mode_t umask;
} connman_vpn_settings = {
.timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT,
.storage_dir_permissions = DEFAULT_STORAGE_DIR_PERMISSIONS,
.storage_file_permissions = DEFAULT_STORAGE_FILE_PERMISSIONS,
.umask = DEFAULT_UMASK
};
static char *get_string(GKeyFile *config, const char *group, const char *key)
{
char *str = g_key_file_get_string(config, group, key, NULL);
return str ? g_strchomp(str) : NULL;
}
static gboolean get_perm(GKeyFile *config, const char *group,
const char *key, mode_t *perm)
{
gboolean ok = FALSE;
char *str = g_key_file_get_string(config, group, key, NULL);
if (str) {
/*
* Some people are thinking that # is a comment
* anywhere on the line, not just at the beginning
*/
unsigned long val;
char *comment = strchr(str, '#');
if (comment) *comment = 0;
val = strtoul(g_strstrip(str), NULL, 0);
if (val > 0 && !(val & ~0777UL)) {
*perm = (mode_t)val;
ok = TRUE;
}
g_free(str);
}
return ok;
}
static GKeyFile *load_config(const char *file)
{
GError *err = NULL;
GKeyFile *keyfile;
keyfile = g_key_file_new();
g_key_file_set_list_separator(keyfile, ',');
if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
if (err->code != G_FILE_ERROR_NOENT) {
connman_error("Parsing %s failed: %s", file,
err->message);
}
g_error_free(err);
g_key_file_unref(keyfile);
return NULL;
}
return keyfile;
}
static void parse_config(GKeyFile *config, const char *file)
{
const char *group = "General";
GError *error = NULL;
int timeout;
if (!config)
return;
DBG("parsing %s", file);
timeout = g_key_file_get_integer(config, "General",
"InputRequestTimeout", &error);
if (!error && timeout >= 0)
connman_vpn_settings.timeout_inputreq = timeout * 1000;
g_clear_error(&error);
connman_vpn_settings.fs_identity = get_string(config, group,
"FileSystemIdentity");
connman_vpn_settings.storage_root = get_string(config, group,
"StorageRoot");
connman_vpn_settings.state_dir = get_string(config, group,
"StateDirectory");
get_perm(config, group, "StorageDirPermissions",
&connman_vpn_settings.storage_dir_permissions);
get_perm(config, group, "StorageFilePermissions",
&connman_vpn_settings.storage_file_permissions);
get_perm(config, group, "Umask", &connman_vpn_settings.umask);
}
static int config_init(const char *file)
{
GKeyFile *config;
config = load_config(file);
parse_config(config, file);
if (config)
g_key_file_unref(config);
return 0;
}
static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
gpointer user_data)
{
......@@ -257,13 +142,6 @@ static bool parse_debug(const char *key, const char *value,
return true;
}
const char *__vpn_state_dir()
{
return connman_vpn_settings.state_dir ?
connman_vpn_settings.state_dir :
DEFAULT_VPN_STATEDIR;
}
static GOptionEntry options[] = {
{ "config", 'c', 0, G_OPTION_ARG_STRING, &option_config,
"Load the specified configuration file "
......@@ -291,7 +169,7 @@ static GOptionEntry options[] = {
*/
unsigned int connman_timeout_input_request(void)
{
return connman_vpn_settings.timeout_inputreq;
return __vpn_settings_get_timeout_inputreq();
}
int main(int argc, char *argv[])
......@@ -331,27 +209,28 @@ int main(int argc, char *argv[])
__connman_log_init(argv[0], option_debug, option_detach, false,
"Connection Manager VPN daemon", VERSION);
if (connman_vpn_settings.fs_identity)
__connman_set_fsid(connman_vpn_settings.fs_identity);
const char* fs_identity = NULL;
if ((fs_identity = __vpn_settings_get_fs_identity()))
__connman_set_fsid(fs_identity);
__connman_inotify_init();
__connman_storage_init(connman_vpn_settings.storage_root,
connman_vpn_settings.storage_dir_permissions,
connman_vpn_settings.storage_file_permissions);
__connman_storage_init(__vpn_settings_get_storage_root(),
__vpn_settings_get_storage_dir_permissions(),
__vpn_settings_get_storage_file_permissions());
if (g_mkdir_with_parents(VPN_STATEDIR,
connman_vpn_settings.storage_dir_permissions) < 0) {
__vpn_settings_get_storage_dir_permissions()) < 0) {
if (errno != EEXIST)
perror("Failed to create state directory");
}
if (g_mkdir_with_parents(VPN_STORAGEDIR,
connman_vpn_settings.storage_dir_permissions) < 0) {
__vpn_settings_get_storage_dir_permissions()) < 0) {
if (errno != EEXIST)
perror("Failed to create VPN storage directory");
}
umask(connman_vpn_settings.umask);
umask(__vpn_settings_get_umask());
main_loop = g_main_loop_new(NULL, FALSE);
......@@ -374,9 +253,9 @@ int main(int argc, char *argv[])
__connman_dbus_init(conn);
if (!option_config)
config_init(CONFIGMAINFILE);
__vpn_settings_init(CONFIGMAINFILE);
else
config_init(option_config);
__vpn_settings_init(option_config);
__connman_agent_init();
__vpn_provider_init(option_routes);
......@@ -413,9 +292,7 @@ int main(int argc, char *argv[])
g_main_loop_unref(main_loop);
g_free(connman_vpn_settings.fs_identity);
g_free(connman_vpn_settings.storage_root);
g_free(connman_vpn_settings.state_dir);
__vpn_settings_free();
g_free(option_debug);
......
......@@ -34,6 +34,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>
......@@ -48,6 +51,7 @@
#include "../vpn-provider.h"
#include "vpn.h"
#include "../vpn.h"
struct vpn_data {
struct vpn_provider *provider;
......@@ -407,6 +411,122 @@ exist_err:
return ret;
}
static gboolean is_numeric(const char *str)
{
gint i = 0;
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 = NULL;
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 = NULL;
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;
int i = 0;
gid_t *list = NULL;
if (groups)
{
for(i = 0; groups[i]; i++)
{
group_count++;
list = (gid_t*)g_try_realloc(list, sizeof(gid_t) * group_count);
if(!list)
{
connman_error("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 connman_task *task = user_data;
// These are retrieved from vpn config
gint uid = get_uid(__vpn_settings_get_binary_user());
gint gid = get_gid(__vpn_settings_get_binary_group());
gchar **suppl_grps = __vpn_settings_get_binary_supplementary_groups();
gid_t *gid_list = NULL;
size_t gid_list_size = get_supplementary_gids(suppl_grps, &gid_list);
DBG("vpn_task_setup %p", task);
// 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));