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"/> + +