From 7938ba72b93d7ec829559cc730706d402feafbfa Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Thu, 20 Sep 2018 11:57:38 +0200 Subject: [PATCH] configmgr: Add config profile override support This extends the configuration manager to support most of the various override or site specific configuration parameters. Many of them cannot be provided by any configuration file option and can only be set after importing a configuration profile. This is slightly modified by David S, reorganizing the code slightly and renaming some of the variables, functions and structs. Signed-off-by: Arne Schwabe Signed-off-by: David Sommerseth --- src/configmgr/configmgr.hpp | 188 ++++++++++++++++++++++++++++++ src/configmgr/overrides.hpp | 115 ++++++++++++++++++ src/policy/net.openvpn.v3.conf.in | 8 ++ 3 files changed, 311 insertions(+) create mode 100644 src/configmgr/overrides.hpp diff --git a/src/configmgr/configmgr.hpp b/src/configmgr/configmgr.hpp index da600555..8b0fedb7 100644 --- a/src/configmgr/configmgr.hpp +++ b/src/configmgr/configmgr.hpp @@ -26,6 +26,7 @@ #include #include "common/core-extensions.hpp" +#include "configmgr/overrides.hpp" #include "dbus/core.hpp" #include "dbus/connection-creds.hpp" #include "dbus/exceptions.hpp" @@ -279,6 +280,58 @@ class ConfigurationAlias : public DBusObject, }; +/** + * Specialised template for managing the override list property + */ +template <> +class PropertyType> : public PropertyTypeBase> +{ +public: + PropertyType>(DBusObject *obj_arg, + std::string name_arg, + std::string dbus_type_arg, + std::string dbus_acl_arg, + bool allow_root_arg, + std::vector& value_arg) + : PropertyTypeBase>(obj_arg, + name_arg, + dbus_type_arg, + dbus_acl_arg, + allow_root_arg, + value_arg) + { + } + + + virtual GVariant *GetValue() const override + { + GVariantBuilder *bld = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + for (const auto& e : this->value) + { + GVariant* value; + if (OverrideType::string == e.override.type) + { + value = g_variant_new("s", e.strValue.c_str()); + } + else + { + value = g_variant_new("b", e.boolValue); + } + g_variant_builder_add(bld, "{sv}", e.override.key.c_str(), value); + } + GVariant* ret = g_variant_builder_end(bld); + g_variant_builder_unref(bld); + return ret; + } + + // This property is readonly and need to be modified with Add/UnsetOverride + virtual GVariantBuilder *SetValue(GVariant *value_arg) override + { + return nullptr; + } +}; + + /** * A ConfigurationObject contains information about a specific VPN * configuration profile. This object is then exposed on the D-Bus through @@ -371,6 +424,7 @@ class ConfigurationObject : public DBusObject, properties.AddBinding(new PropertyType(this, "single_use", "b", "read", false, single_use)); properties.AddBinding(new PropertyType(this, "used_count", "u", "read", false, used_count)); properties.AddBinding(new PropertyType(this, "valid", "b", "read", false, valid)); + properties.AddBinding(new PropertyType>(this, "overrides", "a{sv}", "read", true, override_list)); std::string introsp_xml ="" " " @@ -384,6 +438,13 @@ class ConfigurationObject : public DBusObject, " " " " " " + " " + " " + " " + " " + " " + " " + " " " " " " " " @@ -535,6 +596,74 @@ class ConfigurationObject : public DBusObject, excp.SetDBusError(invoc); } } + else if ("SetOverride" == method_name) + { + if (readonly) + { + g_dbus_method_invocation_return_dbus_error(invoc, + "net.openvpn.v3.error.ReadOnly", + "Configuration is sealed and readonly"); + return; + } + try + { + CheckOwnerAccess(sender); + gchar *key = nullptr; + GVariant *val = nullptr; + g_variant_get(params, "(sv)", &key, &val); + + const OverrideValue vo = set_override(key, val); + + std::string newValue = vo.strValue; + if (OverrideType::boolean == vo.override.type) + { + newValue = vo.boolValue ? "true" : "false"; + } + + LogInfo("Setting configuration override '" + std::string(key) + + "' to '" + newValue + "' by UID " + std::to_string(GetUID(sender))); + + g_free(key); + //g_variant_unref(val); + g_dbus_method_invocation_return_value(invoc, NULL); + return; + } + catch (DBusCredentialsException& excp) + { + LogWarn(excp.err()); + excp.SetDBusError(invoc); + } + } + else if ("UnsetOverride" == method_name) + { + if (readonly) + { + g_dbus_method_invocation_return_dbus_error(invoc, + "net.openvpn.v3.error.ReadOnly", + "Configuration is sealed and readonly"); + return; + } + try + { + CheckOwnerAccess(sender); + gchar *key = nullptr; + g_variant_get(params, "(s)", &key); + if(!remove_override(key)) + THROW_DBUSEXCEPTION("ConfigManagerObject", + "Override does not exist"); + + LogInfo("Unset configuration override '" + std::string(key) + + "' by UID " + std::to_string(GetUID(sender))); + + g_dbus_method_invocation_return_value(invoc, NULL); + g_free(key); + } + catch (DBusCredentialsException& excp) + { + LogWarn(excp.err()); + excp.SetDBusError(invoc); + } + } else if ("AccessGrant" == method_name) { if (readonly) @@ -837,6 +966,64 @@ class ConfigurationObject : public DBusObject, }; + /** + * Sets an override value for the configuration profile + * + * @param key char * of the override key + * @param value GVariant object of the override value to use + * + * @return Returns the OverrideValue object added to the + * array of override settings + */ + OverrideValue set_override(const gchar *key, GVariant *value) + { + const ValidOverride& vo = GetConfigOverride(key); + if (!vo.valid()) + { + THROW_DBUSEXCEPTION("ConfigManagerObject", + "Override is not valid"); + } + + // Ensure that a previous override value is remove + (void) remove_override(key); + + if (OverrideType::string == vo.type) + { + gsize len = 0; + std::string v(g_variant_get_string(value, &len)); + override_list.push_back(OverrideValue(vo, v)); + + } + else if (OverrideType::boolean == vo.type) + { + bool v=g_variant_get_boolean(value); + override_list.push_back(OverrideValue(vo, v)); + } + return override_list.back(); + } + + + /** + * Removes and override from the std::vector array + * + * @param key std::string of the override key to remove + * + * @return Returns true on successful removal, otherwise false. + */ + bool remove_override(const gchar *key) + { + for (auto it = override_list.begin(); it != override_list.end(); it++) + { + if ((*it).override.key == key) + { + override_list.erase(it); + return true; + } + } + return false; + } + + private: std::function remove_callback; std::string name; @@ -852,6 +1039,7 @@ class ConfigurationObject : public DBusObject, ConfigurationAlias *alias; PropertyCollection properties; OptionListJSON options; + std::vector override_list; }; diff --git a/src/configmgr/overrides.hpp b/src/configmgr/overrides.hpp new file mode 100644 index 00000000..ffae5eeb --- /dev/null +++ b/src/configmgr/overrides.hpp @@ -0,0 +1,115 @@ +// OpenVPN 3 Linux client -- Next generation OpenVPN client +// +// Copyright (C) 2018 OpenVPN, Inc. +// Copyright (C) 2018 Arne Schwabe +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, version 3 of the +// License. +// +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +/** + * @file overrides.hpp + * + * @brief Code needed to handle configuration overrides + */ + +#pragma once + +enum class OverrideType +{ + string, + boolean, + invalid +}; + +/** + * Helper classes to store the list of overrides + */ +struct ValidOverride { + ValidOverride(std::string key, OverrideType type) + : key(key), type(type) + { + } + + + inline bool valid() const + { + return type != OverrideType::invalid; + } + + + std::string key; + OverrideType type; +}; + + +struct OverrideValue { + OverrideValue(const ValidOverride& override, bool value) + : override(override), boolValue(value) + { + } + + + OverrideValue(const ValidOverride& override, std::string value) + : override(override), strValue(value) + { + } + + + ValidOverride override; + bool boolValue; + std::string strValue; +}; + + +const ValidOverride configProfileOverrides[] = { + {"server-override", OverrideType::string}, + {"port-override", OverrideType::string}, + {"proto-override", OverrideType::string}, + {"ipv6", OverrideType::string}, + {"dns-fallback-google", OverrideType::boolean}, + {"dns-sync-lookup", OverrideType::boolean}, + {"auth-fail-retry", OverrideType::boolean}, + {"no-client-cert", OverrideType::boolean}, + {"allow-compression", OverrideType::string}, + {"force-cipher-aes-cbc", OverrideType::boolean}, + {"tls-version-min", OverrideType::string}, + {"tls-cert-profile", OverrideType::string}, + {"proxy-auth-cleartext", OverrideType::boolean} +}; + + +const ValidOverride invalidOverride(std::string("invalid"), OverrideType::invalid); + + +const ValidOverride & GetConfigOverride(const std::string & key, bool ignoreCase=false) +{ + for (const auto& vo: configProfileOverrides) + { + if (vo.key==key) + { + return vo; + } + + // This is appearently the best way to do a case insenstive + // comparision in C++ + if (ignoreCase && std::equal(vo.key.begin(), vo.key.end(), key.begin(), + [](char a, char b) { return std::tolower(a) == std::tolower(b);} )) + { + return vo; + } + } + + // Override not found + return invalidOverride; +} diff --git a/src/policy/net.openvpn.v3.conf.in b/src/policy/net.openvpn.v3.conf.in index 673cc742..963185c1 100644 --- a/src/policy/net.openvpn.v3.conf.in +++ b/src/policy/net.openvpn.v3.conf.in @@ -24,6 +24,14 @@ send_interface="net.openvpn.v3.configuration" send_type="method_call" send_member="SetOption"/> + +