blob: e78e501954cab641df740545204a2508cd7f43c0 [file] [log] [blame]
/*
* ConnMan VPN daemon settings
*
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
* Copyright (C) 2018-2020 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.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <unistd.h>
#include <sys/types.h>
#include <connman/log.h>
#include "vpn.h"
#define DEFAULT_INPUT_REQUEST_TIMEOUT 300 * 1000
#define PLUGIN_CONFIGDIR CONFIGDIR "/vpn-plugin"
#define VPN_GROUP "DACPrivileges"
static struct {
unsigned int timeout_inputreq;
char *binary_user;
char *binary_group;
char **binary_supplementary_groups;
char **system_binary_users;
} connman_vpn_settings = {
.timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT,
.binary_user = NULL,
.binary_group = NULL,
.binary_supplementary_groups = NULL,
.system_binary_users = NULL,
};
struct vpn_plugin_data {
char *binary_user;
char *binary_group;
char **binary_supplementary_groups;
};
GHashTable *plugin_hash = NULL;
bool vpn_settings_is_system_user(const char *user)
{
struct passwd *pwd;
struct passwd *system_pwd;
int i;
/*
* The username is not set = override should not be used. This is the
* case after the override is reset.
*/
if (!user)
return true;
DBG("check user \"%s\"", user);
/*
* Ignore errors if no entry was found. Treat as system user to
* prevent using an invalid override.
*/
pwd = vpn_util_get_passwd(user);
if (!pwd)
return true;
if (!connman_vpn_settings.system_binary_users) {
DBG("no binary users set");
/*
* Check if the user is root, or the uid equals to process
* effective uid.
*/
return !pwd->pw_uid || pwd->pw_uid == geteuid();
}
/* Root set as user or the effective user id */
if (!pwd->pw_uid || pwd->pw_uid == geteuid())
return true;
for (i = 0; connman_vpn_settings.system_binary_users[i]; i++) {
const char *system_user =
connman_vpn_settings.system_binary_users[i];
system_pwd = vpn_util_get_passwd(system_user);
if (!system_pwd)
continue;
if (pwd->pw_uid == system_pwd->pw_uid)
return true;
}
return false;
}
const char *vpn_settings_get_binary_user(struct vpn_plugin_data *data)
{
if (data && data->binary_user)
return data->binary_user;
return connman_vpn_settings.binary_user;
}
const char *vpn_settings_get_binary_group(struct vpn_plugin_data *data)
{
if (data && data->binary_group)
return data->binary_group;
return connman_vpn_settings.binary_group;
}
char **vpn_settings_get_binary_supplementary_groups(struct vpn_plugin_data *data)
{
if (data && data->binary_supplementary_groups)
return data->binary_supplementary_groups;
return connman_vpn_settings.binary_supplementary_groups;
}
unsigned int __vpn_settings_get_timeout_inputreq()
{
return connman_vpn_settings.timeout_inputreq;
}
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_strstrip(str) : NULL;
}
static char **get_string_list(GKeyFile *config, const char *group,
const char *key)
{
gsize len = 0;
char **str = g_key_file_get_string_list(config, group, key, &len, NULL);
if (str) {
guint i = 0;
for (i = 0; i < len ; i++) {
str[i] = g_strstrip(str[i]);
}
}
return str;
}
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, group,
"InputRequestTimeout", &error);
if (!error && timeout >= 0)
connman_vpn_settings.timeout_inputreq = timeout * 1000;
g_clear_error(&error);
connman_vpn_settings.binary_user = get_string(config, VPN_GROUP,
"User");
connman_vpn_settings.binary_group = get_string(config, VPN_GROUP,
"Group");
connman_vpn_settings.binary_supplementary_groups = get_string_list(
config, VPN_GROUP,
"SupplementaryGroups");
connman_vpn_settings.system_binary_users = get_string_list(
config, VPN_GROUP,
"SystemBinaryUsers");
}
struct vpn_plugin_data *vpn_settings_get_vpn_plugin_config(const char *name)
{
struct vpn_plugin_data *data = NULL;
if (plugin_hash)
data = g_hash_table_lookup(plugin_hash, name);
return data;
}
static void vpn_plugin_data_free(gpointer data)
{
struct vpn_plugin_data *plugin_data = (struct vpn_plugin_data*)data;
g_free(plugin_data->binary_user);
g_free(plugin_data->binary_group);
g_strfreev(plugin_data->binary_supplementary_groups);
g_free(data);
}
int vpn_settings_parse_vpn_plugin_config(const char *name)
{
struct vpn_plugin_data *data;
gchar *file;
gchar *ext = ".conf";
GKeyFile *config;
gint err = 0;
if (!name || !*name)
return -EINVAL;
if (vpn_settings_get_vpn_plugin_config(name))
return -EALREADY;
file = g_strconcat(PLUGIN_CONFIGDIR, "/", name, ext, NULL);
config = __vpn_settings_load_config(file);
if (!config) {
err = -ENOENT;
DBG("Cannot load config %s for %s", file, name);
goto out;
}
data = g_try_new0(struct vpn_plugin_data, 1);
data->binary_user = get_string(config, VPN_GROUP, "User");
data->binary_group = get_string(config, VPN_GROUP, "Group");
data->binary_supplementary_groups = get_string_list(config, VPN_GROUP,
"SupplementaryGroups");
DBG("Loaded settings for %s: %s - %s",
name, data->binary_user, data->binary_group);
if (!plugin_hash)
plugin_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, vpn_plugin_data_free);
g_hash_table_replace(plugin_hash, g_strdup(name), data);
g_key_file_unref(config);
out:
g_free(file);
return err;
}
void vpn_settings_delete_vpn_plugin_config(const char *name)
{
if (plugin_hash && name)
g_hash_table_remove(plugin_hash, name);
}
GKeyFile *__vpn_settings_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;
}
int __vpn_settings_init(const char *file)
{
GKeyFile *config;
config = __vpn_settings_load_config(file);
parse_config(config, file);
if (config)
g_key_file_unref(config);
return 0;
}
void __vpn_settings_cleanup()
{
g_free(connman_vpn_settings.binary_user);
g_free(connman_vpn_settings.binary_group);
g_strfreev(connman_vpn_settings.binary_supplementary_groups);
g_strfreev(connman_vpn_settings.system_binary_users);
if (plugin_hash) {
g_hash_table_destroy(plugin_hash);
plugin_hash = NULL;
}
}