mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 04:02:15 +02:00
Initial Apple VPN-On-Demand implementation:
* VoD profiles can be defined using the iPhone Configuration utility: 1. Connection Type should be set to Custom SSL 2. Identifier should be set to net.openvpn.OpenVPN-Connect.vpnplugin 3. Server can be set to a hostname, or "DEFAULT" to use the hostname(s) from the OpenVPN configuration. 4. User Authentication should be set to Certificate, and the client certificate+key should be attached as a PKCS#12 file. 5. VPN On Demand should be enabled and match entries should be defined. In addition, the OpenVPN client configuration file may be defined via key/value pairs: 1. VoD requires an autologin profile. 2. Define each OpenVPN directive as a key, with arguments specified as the value. 3. For Access server meta-directives such as OVPN_ACCESS_SERVER_USERNAME, remove the "OVPN_ACCESS_SERVER_" prefix, giving USERNAME as the directive. 4. If no arguments are present, use "NOARGS" as the value. 5. If multiple instances of the same directive are present, number the directives in the order they should be processed by appending .<n> to the directive, where n is an integer, such as remote.1 or remote.2 6. For multi-line directives such as <ca> and <tls-auth>, you must convert the multi-line argument to a single line by specifying line breaks as \n -- also note that because of this escaping model, you must use \\ to pass backslash itself. * VoD profiles are recognized and listed by the app. * The app can disconnect but not connect a VoD profile. * Most app-level functionality such as logging and preferences work correctly for VoD profiles. Core changes: * Added support for key-direction parameter in core.
This commit is contained in:
parent
4595ae0922
commit
065b83263c
@ -300,7 +300,14 @@ namespace openvpn {
|
||||
Protocol::parse(config.protoOverride);
|
||||
|
||||
// parse config
|
||||
const ParseClientConfig cc = ParseClientConfig::parse(config.content, options);
|
||||
OptionList::KeyValueList kvl;
|
||||
kvl.reserve(config.contentList.size());
|
||||
for (size_t i = 0; i < config.contentList.size(); ++i)
|
||||
{
|
||||
const KeyValue& kv = config.contentList[i];
|
||||
kvl.push_back(new OptionList::KeyValue(kv.key, kv.value));
|
||||
}
|
||||
const ParseClientConfig cc = ParseClientConfig::parse(config.content, &kvl, options);
|
||||
eval.error = cc.error();
|
||||
eval.message = cc.message();
|
||||
eval.userlockedUsername = cc.userlockedUsername();
|
||||
|
@ -104,6 +104,14 @@ namespace openvpn {
|
||||
bool responseRequired;
|
||||
};
|
||||
|
||||
// a basic key/value pair, used in Config below when OpenVPN profile is
|
||||
// passed as a dictionary
|
||||
struct KeyValue
|
||||
{
|
||||
std::string key;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
// OpenVPN config-file/profile
|
||||
// (client writes)
|
||||
struct Config
|
||||
@ -113,6 +121,10 @@ namespace openvpn {
|
||||
// OpenVPN profile as a string
|
||||
std::string content;
|
||||
|
||||
// OpenVPN profile as series of key/value pairs (may be provided exclusively
|
||||
// or in addition to content string above).
|
||||
std::vector<KeyValue> contentList;
|
||||
|
||||
// Use a different server than that specified in "remote"
|
||||
// option of profile
|
||||
std::string serverOverride;
|
||||
|
@ -21,6 +21,7 @@
|
||||
%rename(ClientAPI_ProvideCreds) ProvideCreds;
|
||||
%rename(ClientAPI_SessionToken) SessionToken;
|
||||
%rename(ClientAPI_DynamicChallenge) DynamicChallenge;
|
||||
%rename(ClientAPI_KeyValue) KeyValue;
|
||||
%rename(ClientAPI_Config) Config;
|
||||
%rename(ClientAPI_Event) Event;
|
||||
%rename(ClientAPI_ConnectionInfo) ConnectionInfo;
|
||||
|
@ -76,6 +76,17 @@ namespace openvpn {
|
||||
return cppstring(string_cast(dict_index(dict, key)));
|
||||
}
|
||||
|
||||
// lookup a string in a dictionary (DICT should be a Dict or a MutableDict)
|
||||
template <typename DICT, typename KEY>
|
||||
inline std::string dict_get_str(const DICT& dict, const KEY& key, const std::string& default_value)
|
||||
{
|
||||
String str(string_cast(dict_index(dict, key)));
|
||||
if (str.defined())
|
||||
return cppstring(str());
|
||||
else
|
||||
return default_value;
|
||||
}
|
||||
|
||||
// lookup an integer in a dictionary (DICT should be a Dict or a MutableDict)
|
||||
template <typename DICT, typename KEY>
|
||||
inline int dict_get_int(const DICT& dict, const KEY& key, const int default_value)
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <algorithm> // for std::sort
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/algorithm/string.hpp> // for boost::algorithm::starts_with, ends_with
|
||||
@ -19,6 +20,7 @@
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/types.hpp>
|
||||
#include <openvpn/common/typeinfo.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
|
||||
@ -138,6 +140,143 @@ namespace openvpn {
|
||||
typedef boost::unordered_map<std::string, IndexList> IndexMap;
|
||||
typedef std::pair<std::string, IndexList> IndexPair;
|
||||
|
||||
class KeyValue : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef boost::intrusive_ptr<KeyValue> Ptr;
|
||||
|
||||
KeyValue() : key_priority(0) {}
|
||||
KeyValue(const std::string& key_arg, const std::string& value_arg, const int key_priority_arg=0)
|
||||
: key(key_arg), value(value_arg), key_priority(key_priority_arg) {}
|
||||
|
||||
Option convert_to_option() const
|
||||
{
|
||||
bool newline_present = false;
|
||||
Option opt;
|
||||
const std::string unesc_value = unescape(value, newline_present);
|
||||
opt.push_back(key);
|
||||
if (newline_present || singular_arg(key))
|
||||
opt.push_back(unesc_value);
|
||||
else if (unesc_value != "NOARGS")
|
||||
Split::by_space_void<Option, Lex, SpaceMatch>(opt, unesc_value);
|
||||
return opt;
|
||||
}
|
||||
|
||||
void split_priority()
|
||||
{
|
||||
// look for usage such as: remote.7
|
||||
const size_t dp = key.find_last_of(".");
|
||||
if (dp != std::string::npos)
|
||||
{
|
||||
const size_t tp = dp + 1;
|
||||
if (tp < key.length())
|
||||
{
|
||||
const char *tail = key.c_str() + tp;
|
||||
try {
|
||||
key_priority = parse_number<int>(tail);
|
||||
key = key.substr(0, dp);
|
||||
}
|
||||
catch (const number_parse_exception& e)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool compare(const Ptr& a, const Ptr& b)
|
||||
{
|
||||
const int cmp = a->key.compare(b->key);
|
||||
if (cmp < 0)
|
||||
return true;
|
||||
else if (cmp > 0)
|
||||
return false;
|
||||
else
|
||||
return a->key_priority < b->key_priority;
|
||||
}
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
int key_priority;
|
||||
|
||||
private:
|
||||
static std::string unescape(const std::string& value, bool& newline_present)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(value.length());
|
||||
|
||||
bool bs = false;
|
||||
for (size_t i = 0; i < value.length(); ++i)
|
||||
{
|
||||
const char c = value[i];
|
||||
if (bs)
|
||||
{
|
||||
if (c == 'n')
|
||||
{
|
||||
ret += '\n';
|
||||
newline_present = true;
|
||||
}
|
||||
else if (c == '\\')
|
||||
ret += '\\';
|
||||
else
|
||||
{
|
||||
ret += '\\';
|
||||
ret += c;
|
||||
}
|
||||
bs = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '\\')
|
||||
bs = true;
|
||||
else
|
||||
ret += c;
|
||||
}
|
||||
}
|
||||
if (bs)
|
||||
ret += '\\';
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool singular_arg(const std::string& key)
|
||||
{
|
||||
bool upper = false;
|
||||
bool lower = false;
|
||||
for (size_t i = 0; i < key.length(); ++i)
|
||||
{
|
||||
const char c = key[i];
|
||||
if (c >= 'a' && c <= 'z')
|
||||
lower = true;
|
||||
else if (c >= 'A' && c <= 'Z')
|
||||
upper = true;
|
||||
}
|
||||
return upper && !lower;
|
||||
}
|
||||
};
|
||||
|
||||
struct KeyValueList : public std::vector<KeyValue::Ptr>
|
||||
{
|
||||
void preprocess()
|
||||
{
|
||||
split_priority();
|
||||
sort();
|
||||
}
|
||||
|
||||
void split_priority()
|
||||
{
|
||||
for (iterator i = begin(); i != end(); ++i)
|
||||
{
|
||||
KeyValue& kv = **i;
|
||||
kv.split_priority();
|
||||
}
|
||||
}
|
||||
|
||||
void sort()
|
||||
{
|
||||
std::sort(begin(), end(), KeyValue::compare);
|
||||
}
|
||||
};
|
||||
|
||||
static OptionList parse_from_csv_static(const std::string& str)
|
||||
{
|
||||
OptionList ret;
|
||||
@ -172,6 +311,14 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
// caller may want to call list.preprocess() before this function
|
||||
// caller should call update_map() after this function
|
||||
void parse_from_key_value_list(const KeyValueList& list)
|
||||
{
|
||||
for (KeyValueList::const_iterator i = list.begin(); i != list.end(); ++i)
|
||||
push_back((*i)->convert_to_option());
|
||||
}
|
||||
|
||||
// caller should call update_map() after this function
|
||||
void parse_from_config(const std::string& str)
|
||||
{
|
||||
|
@ -130,19 +130,51 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
static ParseClientConfig parse(const std::string& content)
|
||||
static ParseClientConfig parse(const std::string& content, OptionList::KeyValueList* content_list)
|
||||
{
|
||||
OptionList options;
|
||||
return parse(content, options);
|
||||
return parse(content, content_list, options);
|
||||
}
|
||||
|
||||
static ParseClientConfig parse(const std::string& content, OptionList& options)
|
||||
static ParseClientConfig parse(const std::string& content,
|
||||
OptionList::KeyValueList* content_list,
|
||||
OptionList& options)
|
||||
{
|
||||
try {
|
||||
options.clear();
|
||||
options.parse_from_config(content);
|
||||
options.parse_meta_from_config(content, "OVPN_ACCESS_SERVER");
|
||||
if (content_list)
|
||||
{
|
||||
content_list->preprocess();
|
||||
options.parse_from_key_value_list(*content_list);
|
||||
}
|
||||
options.update_map();
|
||||
|
||||
// add in missing options
|
||||
bool added = false;
|
||||
|
||||
// client
|
||||
if (!options.exists("client"))
|
||||
{
|
||||
Option opt;
|
||||
opt.push_back("client");
|
||||
options.push_back(opt);
|
||||
added = true;
|
||||
}
|
||||
|
||||
// dev
|
||||
if (!options.exists("dev"))
|
||||
{
|
||||
Option opt;
|
||||
opt.push_back("dev");
|
||||
opt.push_back("tun");
|
||||
options.push_back(opt);
|
||||
added = true;
|
||||
}
|
||||
if (added)
|
||||
options.update_map();
|
||||
|
||||
return ParseClientConfig(options);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
|
@ -204,6 +204,7 @@ namespace openvpn {
|
||||
pid_time_backtrack = 0;
|
||||
pid_debug_level = 0;
|
||||
autologin = false;
|
||||
key_direction = -1;
|
||||
}
|
||||
|
||||
// master SSL context
|
||||
@ -241,6 +242,7 @@ namespace openvpn {
|
||||
// tls_auth parms
|
||||
OpenVPNStaticKey tls_auth_key; // leave this undefined to disable tls_auth
|
||||
typename CRYPTO_API::Digest tls_auth_digest;
|
||||
int key_direction; // -1 if undefined
|
||||
|
||||
// reliability layer parms
|
||||
reliable::id_t reliable_window;
|
||||
@ -277,6 +279,7 @@ namespace openvpn {
|
||||
comp_ctx = CompressContext(CompressContext::NONE, false);
|
||||
protocol = Protocol();
|
||||
pid_mode = PacketIDReceive::UDP_MODE;
|
||||
key_direction = -1;
|
||||
|
||||
// load parameters that can be present in both config file or pushed options
|
||||
load_common(opt, pco);
|
||||
@ -339,6 +342,21 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
// key-direction
|
||||
{
|
||||
const Option *o = opt.get_ptr("key-direction");
|
||||
if (o)
|
||||
{
|
||||
const std::string& dir = o->get(1);
|
||||
if (dir == "0")
|
||||
key_direction = 0;
|
||||
else if (dir == "1")
|
||||
key_direction = 1;
|
||||
else
|
||||
throw proto_option_error("bad key-direction parameter");
|
||||
}
|
||||
}
|
||||
|
||||
// compression
|
||||
{
|
||||
const Option *o;
|
||||
@ -1810,7 +1828,7 @@ namespace openvpn {
|
||||
if (use_tls_auth)
|
||||
{
|
||||
// init tls_auth hmac
|
||||
const unsigned int key_dir = is_server() ? OpenVPNStaticKey::NORMAL : OpenVPNStaticKey::INVERSE;
|
||||
const unsigned int key_dir = (c.key_direction >= 0 ? !c.key_direction : is_server()) ? OpenVPNStaticKey::NORMAL : OpenVPNStaticKey::INVERSE;
|
||||
ta_hmac_send.init(c.tls_auth_digest, c.tls_auth_key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::ENCRYPT | key_dir));
|
||||
ta_hmac_recv.init(c.tls_auth_digest, c.tls_auth_key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::DECRYPT | key_dir));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user