/* * 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 #endif #include #include #include #include #include #include #include #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; } }