Skip to content

Commit

Permalink
Android 4 -- Implemented External PKI.
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesyonan committed Mar 6, 2012
1 parent d77865d commit deffcee
Show file tree
Hide file tree
Showing 13 changed files with 481 additions and 122 deletions.
18 changes: 18 additions & 0 deletions javacli/OpenVPNClientThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public interface EventReceiver {

// Callback to construct a new tun builder
TunBuilder tun_builder_new();

// Callback to get a certificate
void external_pki_cert_request(ClientAPI_ExternalPKICertRequest req);

// Callback to sign data
void external_pki_sign_request(ClientAPI_ExternalPKISignRequest req);
}

public interface TunBuilder {
Expand Down Expand Up @@ -151,6 +157,18 @@ public void log(ClientAPI_LogInfo loginfo) {
parent.log(loginfo);
}

@Override
public void external_pki_cert_request(ClientAPI_ExternalPKICertRequest req) {
if (parent != null)
parent.external_pki_cert_request(req);
}

@Override
public void external_pki_sign_request(ClientAPI_ExternalPKISignRequest req) {
if (parent != null)
parent.external_pki_sign_request(req);
}

// TunBuilderBase (C++ class) overrides

@Override
Expand Down
37 changes: 35 additions & 2 deletions javacli/ovpncli.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <vector>

#include <openvpn/tun/builder/base.hpp>
#include <openvpn/pki/epkibase.hpp>

namespace openvpn {
class OptionList;
Expand Down Expand Up @@ -96,7 +97,7 @@ namespace openvpn {
// User wants to force a given transport protocol
std::string protoOverride;

// An ID used for get certificate and RSA signing callbacks
// An ID used for get-certificate and RSA signing callbacks
// for External PKI profiles.
std::string externalPkiAlias;
};
Expand Down Expand Up @@ -125,12 +126,35 @@ namespace openvpn {
std::string text; // log output (usually but not always one line)
};

// base class for External PKI queries
struct ExternalPKIRequestBase {
ExternalPKIRequestBase() : error(false), invalidAlias(false) {}

bool error; // true if error occurred
std::string errorText; // text describing error
bool invalidAlias; // true if the error is caused by an invalid alias
std::string alias; // the alias string, used to query cert/key
};

// used to query for External PKI certificate
struct ExternalPKICertRequest : public ExternalPKIRequestBase
{
std::string cert;
};

// used to request an RSA signature
struct ExternalPKISignRequest : public ExternalPKIRequestBase
{
std::string data; // data rendered as base64
std::string sig; // RSA signature, rendered as base64
};

namespace Private {
struct ClientState;
};

// Top-level OpenVPN client class that is wrapped by swig.
class OpenVPNClient : public TunBuilderBase {
class OpenVPNClient : public TunBuilderBase, private ExternalPKIBase {
public:
OpenVPNClient();
virtual ~OpenVPNClient();
Expand Down Expand Up @@ -196,9 +220,18 @@ namespace openvpn {
// Will be called from the thread executing connect().
virtual void log(const LogInfo&) = 0;

// External PKI callbacks
// Will be called from the thread executing connect().
virtual void external_pki_cert_request(ExternalPKICertRequest&) = 0;
virtual void external_pki_sign_request(ExternalPKISignRequest&) = 0;

private:
static void parse_config(const Config&, EvalConfig&, OptionList&);
void parse_extras(const Config&, EvalConfig&);
void external_pki_error(const ExternalPKIRequestBase&);

// from ExternalPKIBase
virtual bool sign(const std::string& data, std::string& sig);

// disable copy and assignment
OpenVPNClient(const OpenVPNClient&);
Expand Down
4 changes: 4 additions & 0 deletions javacli/ovpncli.i
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@
%rename(ClientAPI_Event) Event;
%rename(ClientAPI_Status) Status;
%rename(ClientAPI_LogInfo) LogInfo;
%rename(ClientAPI_ExternalPKIRequestBase) ExternalPKIRequestBase;
%rename(ClientAPI_ExternalPKICertRequest) ExternalPKICertRequest;
%rename(ClientAPI_ExternalPKISignRequest) ExternalPKISignRequest;

// declare vectors
namespace std {
%template(ClientAPI_ServerEntryVector) vector<openvpn::ClientAPI::ServerEntry>;
};

// interface to be bridged between C++ and target language
%include "openvpn/pki/epkibase.hpp"
%include "openvpn/tun/builder/base.hpp"
%include "ovpncli.hpp"
113 changes: 75 additions & 38 deletions javacli/ovpncli.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@

#include <openvpn/init/initprocess.hpp>
#include <openvpn/common/types.hpp>
#include <openvpn/common/string.hpp>
#include <openvpn/common/scoped_ptr.hpp>
#include <openvpn/client/cliconnect.hpp>
#include <openvpn/options/cliopthelper.hpp>

namespace openvpn {
namespace ClientAPI {
Expand Down Expand Up @@ -157,6 +157,7 @@ namespace openvpn {
struct ClientState
{
OptionList options;
EvalConfig eval;
MySocketProtect socket_protect;
ClientCreds::Ptr creds;
MySessionStats::Ptr stats;
Expand All @@ -166,6 +167,7 @@ namespace openvpn {
// extra settings submitted by API client
std::string server_override;
Protocol proto_override;
std::string external_pki_alias;
};
};

Expand Down Expand Up @@ -197,44 +199,10 @@ namespace openvpn {
}

// External PKI
{
const Option* epki = options.get_ptr("EXTERNAL_PKI");
if (epki)
{
eval.externalPki = string::is_true(epki->get_optional(1));
}
else
{
const Option* cert = options.get_ptr("cert");
const Option* key = options.get_ptr("key");
eval.externalPki = !cert || !key;
}
}
eval.externalPki = ClientOptionHelper::is_external_pki(options);

// autologin
{
const Option* autologin = options.get_ptr("AUTOLOGIN");
if (autologin)
{
eval.autologin = string::is_true(autologin->get_optional(1));
}
else
{
const Option* auth_user_pass = options.get_ptr("auth-user-pass");
eval.autologin = !auth_user_pass;
if (eval.autologin)
{
// External PKI profiles from AS don't declare auth-user-pass,
// and we have no way of knowing if they are autologin unless
// we examine their cert, which requires accessing the system-level
// cert store on the client. For now, we are going to assume
// that External PKI profiles from the AS are always userlogin.
const Option* as = options.get_ptr("AUTOLOGIN_SPEC");
if (as)
eval.autologin = false;
}
}
}
eval.autologin = ClientOptionHelper::is_autologin(options);

// static challenge
{
Expand Down Expand Up @@ -296,6 +264,8 @@ namespace openvpn {
state->server_override = config.serverOverride;
if (!config.protoOverride.empty())
state->proto_override = Protocol::parse(config.protoOverride);
if (eval.externalPki)
state->external_pki_alias = config.externalPkiAlias;
}
catch (const std::exception& e)
{
Expand Down Expand Up @@ -324,6 +294,7 @@ namespace openvpn {

// handle extra settings in config
parse_extras(config, eval);
state->eval = eval;
return eval;
}

Expand Down Expand Up @@ -362,7 +333,6 @@ namespace openvpn {
}
}


inline Status OpenVPNClient::connect()
{
boost::asio::detail::signal_blocker signal_blocker; // signals should be handled by parent thread
Expand Down Expand Up @@ -391,6 +361,38 @@ namespace openvpn {
cc.socket_protect = &state->socket_protect;
cc.builder = this;
#endif

// external PKI
if (state->eval.externalPki)
{
if (!state->external_pki_alias.empty())
{
ExternalPKICertRequest req;
req.alias = state->external_pki_alias;
external_pki_cert_request(req);
if (!req.error)
{
Option o;
o.push_back("cert");
o.push_back(req.cert);
state->options.add_item(o);
cc.external_pki = this;
}
else
{
external_pki_error(req);
return ret;
}
}
else
{
ret.error = true;
ret.message = "Missing External PKI alias";
return ret;
}
}

// build client options object
ClientOptions::Ptr client_options = new ClientOptions(state->options, cc);

// configure creds in options
Expand Down Expand Up @@ -426,6 +428,41 @@ namespace openvpn {
return ret;
}

inline void OpenVPNClient::external_pki_error(const ExternalPKIRequestBase& req)
{
if (req.error)
{
if (req.invalidAlias)
{
ClientEvent::Base::Ptr ev = new ClientEvent::EpkiInvalidAlias(req.alias);
state->events->add_event(ev);
}
else
{
ClientEvent::Base::Ptr ev = new ClientEvent::EpkiError(req.errorText);
state->events->add_event(ev);
}
}
}

inline bool OpenVPNClient::sign(const std::string& data, std::string& sig)
{
ExternalPKISignRequest req;
req.data = data;
req.alias = state->external_pki_alias;
external_pki_sign_request(req); // call out to derived class for RSA signature
if (!req.error)
{
sig = req.sig;
return true;
}
else
{
external_pki_error(req);
return false;
}
}

inline int OpenVPNClient::stats_n()
{
return MySessionStats::combined_n();
Expand Down
7 changes: 7 additions & 0 deletions openvpn/applecrypto/ssl/sslctx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/frame/memq_stream.hpp>
#include <openvpn/pki/epkibase.hpp>
#include <openvpn/applecrypto/cf/cfsec.hpp>
#include <openvpn/applecrypto/cf/error.hpp>

Expand All @@ -32,6 +33,7 @@ namespace openvpn {
public:
OPENVPN_EXCEPTION(ssl_context_error);
OPENVPN_EXCEPTION(ssl_ciphertext_in_overflow);
OPENVPN_SIMPLE_EXCEPTION(external_pki_not_implemented);

typedef boost::intrusive_ptr<AppleSSLContext> Ptr;

Expand Down Expand Up @@ -77,6 +79,11 @@ namespace openvpn {
load_identity(subject_match);
}
}

void set_external_pki_callback(ExternalPKIBase* external_pki_arg)
{
throw external_pki_not_implemented();
}
};

// Represents an actual SSL session.
Expand Down
Loading

0 comments on commit deffcee

Please sign in to comment.