Skip to content

Commit

Permalink
configmgr: Add config profile override support
Browse files Browse the repository at this point in the history
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 <arne@openvpn.net>
Signed-off-by: David Sommerseth <davids@openvpn.net>
  • Loading branch information
schwabe authored and dsommers committed Sep 24, 2018
1 parent 227b876 commit 7938ba7
Show file tree
Hide file tree
Showing 3 changed files with 311 additions and 0 deletions.
188 changes: 188 additions & 0 deletions src/configmgr/configmgr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <openvpn/log/logsimple.hpp>
#include "common/core-extensions.hpp"
#include "configmgr/overrides.hpp"
#include "dbus/core.hpp"
#include "dbus/connection-creds.hpp"
#include "dbus/exceptions.hpp"
Expand Down Expand Up @@ -279,6 +280,58 @@ class ConfigurationAlias : public DBusObject,
};


/**
* Specialised template for managing the override list property
*/
template <>
class PropertyType<std::vector<OverrideValue>> : public PropertyTypeBase<std::vector<OverrideValue>>
{
public:
PropertyType<std::vector<OverrideValue>>(DBusObject *obj_arg,
std::string name_arg,
std::string dbus_type_arg,
std::string dbus_acl_arg,
bool allow_root_arg,
std::vector<OverrideValue>& value_arg)
: PropertyTypeBase<std::vector<OverrideValue>>(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
Expand Down Expand Up @@ -371,6 +424,7 @@ class ConfigurationObject : public DBusObject,
properties.AddBinding(new PropertyType<bool>(this, "single_use", "b", "read", false, single_use));
properties.AddBinding(new PropertyType<unsigned int>(this, "used_count", "u", "read", false, used_count));
properties.AddBinding(new PropertyType<bool>(this, "valid", "b", "read", false, valid));
properties.AddBinding(new PropertyType<std::vector<OverrideValue>>(this, "overrides", "a{sv}", "read", true, override_list));

std::string introsp_xml ="<node name='" + objpath + "'>"
" <interface name='net.openvpn.v3.configuration'>"
Expand All @@ -384,6 +438,13 @@ class ConfigurationObject : public DBusObject,
" <arg direction='in' type='s' name='option'/>"
" <arg direction='in' type='s' name='value'/>"
" </method>"
" <method name='SetOverride'>"
" <arg direction='in' type='s' name='name'/>"
" <arg direction='in' type='v' name='value'/>"
" </method>"
" <method name='UnsetOverride'>"
" <arg direction='in' type='s' name='name'/>"
" </method>"
" <method name='AccessGrant'>"
" <arg direction='in' type='u' name='uid'/>"
" </method>"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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<OverrideValue> 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<void()> remove_callback;
std::string name;
Expand All @@ -852,6 +1039,7 @@ class ConfigurationObject : public DBusObject,
ConfigurationAlias *alias;
PropertyCollection properties;
OptionListJSON options;
std::vector<OverrideValue> override_list;
};


Expand Down
115 changes: 115 additions & 0 deletions src/configmgr/overrides.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// OpenVPN 3 Linux client -- Next generation OpenVPN client
//
// Copyright (C) 2018 OpenVPN, Inc. <sales@openvpn.net>
// Copyright (C) 2018 Arne Schwabe <arne@openvpn.net>
//
// 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 <https://www.gnu.org/licenses/>.
//

/**
* @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;
}
8 changes: 8 additions & 0 deletions src/policy/net.openvpn.v3.conf.in
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
send_interface="net.openvpn.v3.configuration"
send_type="method_call"
send_member="SetOption"/>
<allow send_destination="net.openvpn.v3.configuration"
send_interface="net.openvpn.v3.configuration"
send_type="method_call"
send_member="SetOverride"/>
<allow send_destination="net.openvpn.v3.configuration"
send_interface="net.openvpn.v3.configuration"
send_type="method_call"
send_member="UnsetOverride"/>
<allow send_destination="net.openvpn.v3.configuration"
send_interface="net.openvpn.v3.configuration"
send_type="method_call"
Expand Down

0 comments on commit 7938ba7

Please sign in to comment.