0
0
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:
James Yonan 2012-11-06 17:50:30 +00:00
parent 4595ae0922
commit 065b83263c
7 changed files with 233 additions and 5 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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)
{

View File

@ -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)

View File

@ -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));