Merge branch 'charon-xpc'
Implement a charon daemon controlled by the Apple specific XPC mechanism, acting as a backend for a yet to build unprivileged GUI. The keychain plugin coming with this merge provides certificates from the OS X keychain service.
This commit is contained in:
commit
569d114de8
|
@ -218,6 +218,7 @@ ARG_ENABL_SET([padlock], [enables VIA Padlock crypto plugin.])
|
|||
ARG_ENABL_SET([openssl], [enables the OpenSSL crypto plugin.])
|
||||
ARG_ENABL_SET([gcrypt], [enables the libgcrypt plugin.])
|
||||
ARG_ENABL_SET([agent], [enables the ssh-agent signing plugin.])
|
||||
ARG_ENABL_SET([keychain], [enables OS X Keychain Services credential set.])
|
||||
ARG_ENABL_SET([pkcs11], [enables the PKCS11 token support plugin.])
|
||||
ARG_ENABL_SET([ctr], [enables the Counter Mode wrapper crypto plugin.])
|
||||
ARG_ENABL_SET([ccm], [enables the CCM AEAD wrapper crypto plugin.])
|
||||
|
@ -1012,6 +1013,7 @@ ADD_PLUGIN([af-alg], [s charon openac scepclient pki scripts medsr
|
|||
ADD_PLUGIN([fips-prf], [s charon nm cmd])
|
||||
ADD_PLUGIN([gmp], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
|
||||
ADD_PLUGIN([agent], [s charon nm cmd])
|
||||
ADD_PLUGIN([keychain], [s charon cmd])
|
||||
ADD_PLUGIN([xcbc], [s charon nm cmd])
|
||||
ADD_PLUGIN([cmac], [s charon nm cmd])
|
||||
ADD_PLUGIN([hmac], [s charon scripts nm cmd])
|
||||
|
@ -1148,6 +1150,7 @@ AM_CONDITIONAL(USE_PADLOCK, test x$padlock = xtrue)
|
|||
AM_CONDITIONAL(USE_OPENSSL, test x$openssl = xtrue)
|
||||
AM_CONDITIONAL(USE_GCRYPT, test x$gcrypt = xtrue)
|
||||
AM_CONDITIONAL(USE_AGENT, test x$agent = xtrue)
|
||||
AM_CONDITIONAL(USE_KEYCHAIN, test x$keychain = xtrue)
|
||||
AM_CONDITIONAL(USE_PKCS11, test x$pkcs11 = xtrue)
|
||||
AM_CONDITIONAL(USE_CTR, test x$ctr = xtrue)
|
||||
AM_CONDITIONAL(USE_CCM, test x$ccm = xtrue)
|
||||
|
@ -1349,6 +1352,7 @@ AC_CONFIG_FILES([
|
|||
src/libstrongswan/plugins/openssl/Makefile
|
||||
src/libstrongswan/plugins/gcrypt/Makefile
|
||||
src/libstrongswan/plugins/agent/Makefile
|
||||
src/libstrongswan/plugins/keychain/Makefile
|
||||
src/libstrongswan/plugins/pkcs11/Makefile
|
||||
src/libstrongswan/plugins/ctr/Makefile
|
||||
src/libstrongswan/plugins/ccm/Makefile
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
xcuserdata
|
||||
*.xcworkspace
|
|
@ -0,0 +1,98 @@
|
|||
# strongSwan OS X App #
|
||||
|
||||
## Introduction ##
|
||||
|
||||
The strongSwan OS X App consists of two components:
|
||||
|
||||
* A frontend to configure and control connections
|
||||
* A privileged helper daemon, controlled using XPC, called charon-xpc
|
||||
|
||||
The privileged helper daemon gets installed automatically using SMJobBless
|
||||
functionality on its first use, and gets started automatically by Launchd when
|
||||
needed.
|
||||
|
||||
charon-xpc is a special build linking statically against strongSwan components.
|
||||
|
||||
## Building strongSwan ##
|
||||
|
||||
strongSwan on OS X requires the libvstr library. The simplest way to install
|
||||
it is using MacPorts. It gets statically linked to charon-xpc, hence it is not
|
||||
needed to run the built App.
|
||||
|
||||
Before building the Xcode project, the strongSwan base tree must be built using
|
||||
a monolithic and static build. This can be achieved on OS X by using:
|
||||
|
||||
LDFLAGS="-all_load -L/opt/local/lib" \
|
||||
CFLAGS="-idirafter /opt/local/include -O2 -Wall -Wno-format -Wno-pointer-sign" \
|
||||
./configure --enable-monolithic --disable-shared --enable-static \
|
||||
--disable-defaults \
|
||||
--enable-openssl --enable-kernel-pfkey --enable-kernel-pfroute \
|
||||
--enable-eap-mschapv2 --enable-eap-identity --enable-nonce \
|
||||
--enable-random --enable-pkcs1 --enable-pem --enable-socket-default \
|
||||
--enable-xauth-generic --enable-keychain --enable-charon \
|
||||
--enable-ikev1 --enable-ikev2
|
||||
|
||||
followed by calling make (no need to make install).
|
||||
|
||||
Building charon-xpc using the Xcode project yields a single binary without
|
||||
any non OS X dependencies.
|
||||
|
||||
Both charon-xpc and the App must be code-signed to allow the installation of
|
||||
the privileged helper. git-grep for "Joe Developer" to change the signing
|
||||
identity.
|
||||
|
||||
## XPC application protocol ##
|
||||
|
||||
charon-xpc provides a Mach service under the name _org.strongswan.charon-xpc_.
|
||||
Clients can connect to this service to control the daemon. All messages
|
||||
on all connections use the following string dictionary keys/values:
|
||||
|
||||
* _type_: XPC message type, currently either
|
||||
* _rpc_ for a remote procedure call, expects a response
|
||||
* _event_ for application specific event messages
|
||||
* _rpc_: defines the name of the RPC function to call (for _type_ = _rpc_)
|
||||
* _event_: defines a name for the event (for _type_ = _event_)
|
||||
|
||||
Additional arguments and return values are specified by the call and can have
|
||||
any type. Keys are directly attached to the message dictionary.
|
||||
|
||||
On the Mach service connection, the following RPC messages are currently
|
||||
defined:
|
||||
|
||||
* string version = get_version()
|
||||
* _version_: strongSwan version of charon-xpc
|
||||
* bool success = start_connection(string name, string host, string id,
|
||||
endpoint channel)
|
||||
* _success_: TRUE if initiation started successfully
|
||||
* _name_: connection name to initiate
|
||||
* _host_: server hostname (and identity)
|
||||
* _id_: client identity to use
|
||||
* _channel_: XPC endpoint for this connection
|
||||
|
||||
The start_connection() RPC returns just after the initation of the call and
|
||||
does not wait for the connection to establish. Nonetheless does it have a
|
||||
return value to indicate if connection initiation could be triggered.
|
||||
|
||||
The App passes an (anonymous) XPC endpoint to start_connection(). If the call
|
||||
succeeds, charon-xpc connects to this endpoint to establish a channel used for
|
||||
this specific IKE connection.
|
||||
|
||||
On this channel, the following RPC calls are currently defined from charon-xpc
|
||||
to the App:
|
||||
|
||||
* string password = get_password(string username)
|
||||
* _password_: user password returned
|
||||
* _username_: username to query a password for
|
||||
|
||||
And the following from the App to charon-xpc:
|
||||
|
||||
* bool success = stop_connection()
|
||||
* _success_: TRUE if termination of connection initiated
|
||||
|
||||
The following events are currently defined from charon-xpc to the App:
|
||||
|
||||
* up(): IKE_SA has been established
|
||||
* down(): IKE_SA has been closed or failed to establish
|
||||
* child_up(string local_ts, string remote_ts): CHILD_SA has been established
|
||||
* child_down(string local_ts, string remote_ts): CHILD_SA has been closed
|
||||
* log(string message): debug log message for this connection
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.strongswan.charon-xpc</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>charon-xpc</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>SMAuthorizedClients</key>
|
||||
<array>
|
||||
<string>identifier org.strongswan.osx and certificate leaf[subject.CN] = "Joe Developer"</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>org.strongswan.charon-xpc</string>
|
||||
<key>MachServices</key>
|
||||
<dict>
|
||||
<key>org.strongswan.charon-xpc</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <library.h>
|
||||
#include <hydra.h>
|
||||
#include <daemon.h>
|
||||
#include <threading/thread.h>
|
||||
#include <utils/backtrace.h>
|
||||
|
||||
#include "xpc_dispatch.h"
|
||||
|
||||
/**
|
||||
* XPC dispatcher class
|
||||
*/
|
||||
static xpc_dispatch_t *dispatcher;
|
||||
|
||||
/**
|
||||
* atexit() cleanup for dispatcher
|
||||
*/
|
||||
void dispatcher_cleanup()
|
||||
{
|
||||
DESTROY_IF(dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loglevel configuration
|
||||
*/
|
||||
static level_t levels[DBG_MAX];
|
||||
|
||||
/**
|
||||
* hook in library for debugging messages
|
||||
*/
|
||||
extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
|
||||
|
||||
/**
|
||||
* Logging hook for library logs, using stderr output
|
||||
*/
|
||||
static void dbg_stderr(debug_t group, level_t level, char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (level <= 1)
|
||||
{
|
||||
va_start(args, fmt);
|
||||
fprintf(stderr, "00[%N] ", debug_names, group);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the daemon and handle unix signals
|
||||
*/
|
||||
static int run()
|
||||
{
|
||||
sigset_t set;
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGINT);
|
||||
sigaddset(&set, SIGTERM);
|
||||
sigprocmask(SIG_BLOCK, &set, NULL);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
int sig;
|
||||
|
||||
if (sigwait(&set, &sig))
|
||||
{
|
||||
DBG1(DBG_DMN, "error while waiting for a signal");
|
||||
return 1;
|
||||
}
|
||||
switch (sig)
|
||||
{
|
||||
case SIGINT:
|
||||
DBG1(DBG_DMN, "signal of type SIGINT received. Shutting down");
|
||||
charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
|
||||
return 0;
|
||||
case SIGTERM:
|
||||
DBG1(DBG_DMN, "signal of type SIGTERM received. Shutting down");
|
||||
charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
|
||||
return 0;
|
||||
default:
|
||||
DBG1(DBG_DMN, "unknown signal %d received. Ignored", sig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle SIGSEGV/SIGILL signals raised by threads
|
||||
*/
|
||||
static void segv_handler(int signal)
|
||||
{
|
||||
backtrace_t *backtrace;
|
||||
|
||||
DBG1(DBG_DMN, "thread %u received %d", thread_current_id(), signal);
|
||||
backtrace = backtrace_create(2);
|
||||
backtrace->log(backtrace, NULL, TRUE);
|
||||
backtrace->destroy(backtrace);
|
||||
|
||||
DBG1(DBG_DMN, "killing ourself, received critical signal");
|
||||
abort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function, starts the daemon.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct sigaction action;
|
||||
struct utsname utsname;
|
||||
int group;
|
||||
|
||||
dbg = dbg_stderr;
|
||||
atexit(library_deinit);
|
||||
if (!library_init(NULL))
|
||||
{
|
||||
exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
|
||||
}
|
||||
if (lib->integrity)
|
||||
{
|
||||
if (!lib->integrity->check_file(lib->integrity, "charon-xpc", argv[0]))
|
||||
{
|
||||
exit(SS_RC_DAEMON_INTEGRITY);
|
||||
}
|
||||
}
|
||||
atexit(libhydra_deinit);
|
||||
if (!libhydra_init("charon-xpc"))
|
||||
{
|
||||
exit(SS_RC_INITIALIZATION_FAILED);
|
||||
}
|
||||
atexit(libcharon_deinit);
|
||||
if (!libcharon_init("charon-xpc"))
|
||||
{
|
||||
exit(SS_RC_INITIALIZATION_FAILED);
|
||||
}
|
||||
for (group = 0; group < DBG_MAX; group++)
|
||||
{
|
||||
levels[group] = LEVEL_CTRL;
|
||||
}
|
||||
charon->load_loggers(charon, levels, TRUE);
|
||||
|
||||
lib->settings->set_default_str(lib->settings, "charon-cmd.port", "0");
|
||||
lib->settings->set_default_str(lib->settings, "charon-cmd.port_nat_t", "0");
|
||||
lib->settings->set_default_str(lib->settings,
|
||||
"charon-cmd.close_ike_on_child_failure", "yes");
|
||||
if (!charon->initialize(charon,
|
||||
lib->settings->get_str(lib->settings, "charon-xpc.load",
|
||||
"random nonce pem pkcs1 openssl kernel-pfkey kernel-pfroute "
|
||||
"keychain socket-default eap-identity eap-mschapv2 osx-attr")))
|
||||
{
|
||||
exit(SS_RC_INITIALIZATION_FAILED);
|
||||
}
|
||||
|
||||
if (uname(&utsname) != 0)
|
||||
{
|
||||
memset(&utsname, 0, sizeof(utsname));
|
||||
}
|
||||
DBG1(DBG_DMN, "Starting charon-xpc IKE daemon (strongSwan %s, %s %s, %s)",
|
||||
VERSION, utsname.sysname, utsname.release, utsname.machine);
|
||||
|
||||
/* add handler for SEGV and ILL,
|
||||
* INT, TERM and HUP are handled by sigwait() in run() */
|
||||
action.sa_handler = segv_handler;
|
||||
action.sa_flags = 0;
|
||||
sigemptyset(&action.sa_mask);
|
||||
sigaddset(&action.sa_mask, SIGINT);
|
||||
sigaddset(&action.sa_mask, SIGTERM);
|
||||
sigaddset(&action.sa_mask, SIGHUP);
|
||||
sigaction(SIGSEGV, &action, NULL);
|
||||
sigaction(SIGILL, &action, NULL);
|
||||
sigaction(SIGBUS, &action, NULL);
|
||||
action.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &action, NULL);
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL);
|
||||
|
||||
dispatcher = xpc_dispatch_create();
|
||||
if (!dispatcher)
|
||||
{
|
||||
exit(SS_RC_INITIALIZATION_FAILED);
|
||||
}
|
||||
atexit(dispatcher_cleanup);
|
||||
|
||||
charon->start(charon);
|
||||
return run();
|
||||
}
|
|
@ -0,0 +1,540 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "xpc_channels.h"
|
||||
#include "xpc_logger.h"
|
||||
|
||||
#include <credentials/sets/callback_cred.h>
|
||||
#include <collections/hashtable.h>
|
||||
#include <threading/rwlock.h>
|
||||
#include <daemon.h>
|
||||
|
||||
typedef struct private_xpc_channels_t private_xpc_channels_t;
|
||||
|
||||
/**
|
||||
* Private data of an xpc_channels_t object.
|
||||
*/
|
||||
struct private_xpc_channels_t {
|
||||
|
||||
/**
|
||||
* Public xpc_channels_t interface.
|
||||
*/
|
||||
xpc_channels_t public;
|
||||
|
||||
/**
|
||||
* Registered channels, IKE_SA unique ID => entry_t
|
||||
*/
|
||||
hashtable_t *channels;
|
||||
|
||||
/**
|
||||
* Lock for channels list
|
||||
*/
|
||||
rwlock_t *lock;
|
||||
|
||||
/**
|
||||
* Callback credential set for passwords
|
||||
*/
|
||||
callback_cred_t *creds;
|
||||
};
|
||||
|
||||
/**
|
||||
* Channel entry
|
||||
*/
|
||||
typedef struct {
|
||||
/* XPC channel to App */
|
||||
xpc_connection_t conn;
|
||||
/* associated IKE_SA unique identifier */
|
||||
uintptr_t sa;
|
||||
/* did we already ask for a password? */
|
||||
bool passworded;
|
||||
/* channel specific logger */
|
||||
xpc_logger_t *logger;
|
||||
} entry_t;
|
||||
|
||||
/**
|
||||
* Clean up an entry, cancelling connection
|
||||
*/
|
||||
static void destroy_entry(entry_t *entry)
|
||||
{
|
||||
charon->bus->remove_logger(charon->bus, &entry->logger->logger);
|
||||
entry->logger->destroy(entry->logger);
|
||||
xpc_connection_suspend(entry->conn);
|
||||
xpc_release(entry->conn);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an IKE_SA unique identifier by a given XPC channel
|
||||
*/
|
||||
static u_int32_t find_ike_sa_by_conn(private_xpc_channels_t *this,
|
||||
xpc_connection_t conn)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
entry_t *entry;
|
||||
u_int32_t ike_sa = 0;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->channels->create_enumerator(this->channels);
|
||||
while (enumerator->enumerate(enumerator, NULL, &entry))
|
||||
{
|
||||
if (entry->conn == conn)
|
||||
{
|
||||
ike_sa = entry->sa;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
return ike_sa;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an entry for a given XPC connection
|
||||
*/
|
||||
static void remove_conn(private_xpc_channels_t *this, xpc_connection_t conn)
|
||||
{
|
||||
uintptr_t ike_sa;
|
||||
entry_t *entry;
|
||||
|
||||
ike_sa = find_ike_sa_by_conn(this, conn);
|
||||
if (ike_sa)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
entry = this->channels->remove(this->channels, (void*)ike_sa);
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
destroy_entry(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger termination of a connection
|
||||
*/
|
||||
static void stop_connection(private_xpc_channels_t *this, u_int32_t ike_sa,
|
||||
xpc_object_t request, xpc_object_t reply)
|
||||
{
|
||||
status_t status;
|
||||
|
||||
status = charon->controller->terminate_ike(charon->controller, ike_sa,
|
||||
NULL, NULL, 0);
|
||||
xpc_dictionary_set_bool(reply, "success", status != NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* XPC RPC command dispatch table
|
||||
*/
|
||||
static struct {
|
||||
char *name;
|
||||
void (*handler)(private_xpc_channels_t *this, u_int32_t ike_sa,
|
||||
xpc_object_t request, xpc_object_t reply);
|
||||
} commands[] = {
|
||||
{ "stop_connection", stop_connection },
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a request message from App
|
||||
*/
|
||||
static void handle(private_xpc_channels_t *this, xpc_connection_t conn,
|
||||
xpc_object_t request)
|
||||
{
|
||||
xpc_object_t reply;
|
||||
const char *type, *rpc;
|
||||
bool found = FALSE;
|
||||
u_int32_t ike_sa;
|
||||
int i;
|
||||
|
||||
type = xpc_dictionary_get_string(request, "type");
|
||||
if (type)
|
||||
{
|
||||
if (streq(type, "rpc"))
|
||||
{
|
||||
reply = xpc_dictionary_create_reply(request);
|
||||
rpc = xpc_dictionary_get_string(request, "rpc");
|
||||
ike_sa = find_ike_sa_by_conn(this, conn);
|
||||
if (reply && rpc && ike_sa)
|
||||
{
|
||||
for (i = 0; i < countof(commands); i++)
|
||||
{
|
||||
if (streq(commands[i].name, rpc))
|
||||
{
|
||||
found = TRUE;
|
||||
commands[i].handler(this, ike_sa, request, reply);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
DBG1(DBG_CFG, "received invalid XPC rpc command: %s", rpc);
|
||||
}
|
||||
if (reply)
|
||||
{
|
||||
xpc_connection_send_message(conn, reply);
|
||||
xpc_release(reply);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "received unknown XPC message type: %s", type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "received XPC message without a type");
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(xpc_channels_t, add, void,
|
||||
private_xpc_channels_t *this, xpc_connection_t conn, u_int32_t ike_sa)
|
||||
{
|
||||
entry_t *entry;
|
||||
|
||||
INIT(entry,
|
||||
.conn = conn,
|
||||
.sa = ike_sa,
|
||||
.logger = xpc_logger_create(conn),
|
||||
);
|
||||
|
||||
xpc_connection_set_event_handler(entry->conn, ^(xpc_object_t event)
|
||||
{
|
||||
if (event == XPC_ERROR_CONNECTION_INVALID ||
|
||||
event == XPC_ERROR_CONNECTION_INTERRUPTED)
|
||||
{
|
||||
remove_conn(this, conn);
|
||||
}
|
||||
else if (xpc_get_type(event) == XPC_TYPE_DICTIONARY)
|
||||
{
|
||||
handle(this, conn, event);
|
||||
}
|
||||
});
|
||||
|
||||
entry->logger->set_ike_sa(entry->logger, entry->sa);
|
||||
charon->bus->add_logger(charon->bus, &entry->logger->logger);
|
||||
|
||||
this->lock->write_lock(this->lock);
|
||||
this->channels->put(this->channels, (void*)entry->sa, entry);
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
xpc_connection_resume(conn);
|
||||
}
|
||||
|
||||
METHOD(listener_t, alert, bool,
|
||||
private_xpc_channels_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
|
||||
{
|
||||
const char *desc;
|
||||
|
||||
switch (alert)
|
||||
{
|
||||
case ALERT_LOCAL_AUTH_FAILED:
|
||||
desc = "local-auth";
|
||||
break;
|
||||
case ALERT_PEER_AUTH_FAILED:
|
||||
desc = "remote-auth";
|
||||
break;
|
||||
case ALERT_PEER_ADDR_FAILED:
|
||||
desc = "dns";
|
||||
break;
|
||||
case ALERT_PEER_INIT_UNREACHABLE:
|
||||
desc = "unreachable";
|
||||
break;
|
||||
case ALERT_RETRANSMIT_SEND_TIMEOUT:
|
||||
desc = "timeout";
|
||||
break;
|
||||
case ALERT_PROPOSAL_MISMATCH_IKE:
|
||||
case ALERT_PROPOSAL_MISMATCH_CHILD:
|
||||
desc = "proposal-mismatch";
|
||||
break;
|
||||
case ALERT_TS_MISMATCH:
|
||||
desc = "ts-mismatch";
|
||||
break;
|
||||
default:
|
||||
return TRUE;
|
||||
}
|
||||
if (ike_sa)
|
||||
{
|
||||
entry_t *entry;
|
||||
uintptr_t sa;
|
||||
|
||||
sa = ike_sa->get_unique_id(ike_sa);
|
||||
this->lock->read_lock(this->lock);
|
||||
entry = this->channels->get(this->channels, (void*)sa);
|
||||
if (entry)
|
||||
{
|
||||
xpc_object_t msg;
|
||||
|
||||
msg = xpc_dictionary_create(NULL, NULL, 0);
|
||||
xpc_dictionary_set_string(msg, "type", "event");
|
||||
xpc_dictionary_set_string(msg, "event", "alert");
|
||||
xpc_dictionary_set_string(msg, "alert", desc);
|
||||
xpc_connection_send_message(entry->conn, msg);
|
||||
xpc_release(msg);
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(listener_t, ike_rekey, bool,
|
||||
private_xpc_channels_t *this, ike_sa_t *old, ike_sa_t *new)
|
||||
{
|
||||
entry_t *entry;
|
||||
uintptr_t sa;
|
||||
|
||||
sa = old->get_unique_id(old);
|
||||
this->lock->write_lock(this->lock);
|
||||
entry = this->channels->remove(this->channels, (void*)sa);
|
||||
if (entry)
|
||||
{
|
||||
entry->sa = new->get_unique_id(new);
|
||||
entry->logger->set_ike_sa(entry->logger, entry->sa);
|
||||
this->channels->put(this->channels, (void*)entry->sa, entry);
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(listener_t, ike_state_change, bool,
|
||||
private_xpc_channels_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
|
||||
{
|
||||
if (state == IKE_CONNECTING)
|
||||
{
|
||||
entry_t *entry;
|
||||
uintptr_t sa;
|
||||
|
||||
sa = ike_sa->get_unique_id(ike_sa);
|
||||
this->lock->read_lock(this->lock);
|
||||
entry = this->channels->get(this->channels, (void*)sa);
|
||||
if (entry)
|
||||
{
|
||||
xpc_object_t msg;
|
||||
|
||||
msg = xpc_dictionary_create(NULL, NULL, 0);
|
||||
xpc_dictionary_set_string(msg, "type", "event");
|
||||
xpc_dictionary_set_string(msg, "event", "connecting");
|
||||
xpc_connection_send_message(entry->conn, msg);
|
||||
xpc_release(msg);
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(listener_t, child_updown, bool,
|
||||
private_xpc_channels_t *this, ike_sa_t *ike_sa,
|
||||
child_sa_t *child_sa, bool up)
|
||||
{
|
||||
entry_t *entry;
|
||||
uintptr_t sa;
|
||||
|
||||
sa = ike_sa->get_unique_id(ike_sa);
|
||||
this->lock->read_lock(this->lock);
|
||||
entry = this->channels->get(this->channels, (void*)sa);
|
||||
if (entry)
|
||||
{
|
||||
xpc_object_t msg;
|
||||
linked_list_t *list;
|
||||
char buf[256];
|
||||
|
||||
msg = xpc_dictionary_create(NULL, NULL, 0);
|
||||
xpc_dictionary_set_string(msg, "type", "event");
|
||||
if (up)
|
||||
{
|
||||
xpc_dictionary_set_string(msg, "event", "child_up");
|
||||
}
|
||||
else
|
||||
{
|
||||
xpc_dictionary_set_string(msg, "event", "child_down");
|
||||
}
|
||||
|
||||
list = child_sa->get_traffic_selectors(child_sa, TRUE);
|
||||
snprintf(buf, sizeof(buf), "%#R", list);
|
||||
xpc_dictionary_set_string(msg, "ts_local", buf);
|
||||
|
||||
list = child_sa->get_traffic_selectors(child_sa, FALSE);
|
||||
snprintf(buf, sizeof(buf), "%#R", list);
|
||||
xpc_dictionary_set_string(msg, "ts_remote", buf);
|
||||
|
||||
xpc_connection_send_message(entry->conn, msg);
|
||||
xpc_release(msg);
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(listener_t, ike_updown, bool,
|
||||
private_xpc_channels_t *this, ike_sa_t *ike_sa, bool up)
|
||||
{
|
||||
xpc_object_t msg;
|
||||
entry_t *entry;
|
||||
uintptr_t sa;
|
||||
|
||||
sa = ike_sa->get_unique_id(ike_sa);
|
||||
if (up)
|
||||
{
|
||||
this->lock->read_lock(this->lock);
|
||||
entry = this->channels->get(this->channels, (void*)sa);
|
||||
if (entry)
|
||||
{
|
||||
msg = xpc_dictionary_create(NULL, NULL, 0);
|
||||
xpc_dictionary_set_string(msg, "type", "event");
|
||||
xpc_dictionary_set_string(msg, "event", "up");
|
||||
xpc_connection_send_message(entry->conn, msg);
|
||||
xpc_release(msg);
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
entry = this->channels->remove(this->channels, (void*)sa);
|
||||
this->lock->unlock(this->lock);
|
||||
if (entry)
|
||||
{
|
||||
msg = xpc_dictionary_create(NULL, NULL, 0);
|
||||
xpc_dictionary_set_string(msg, "type", "event");
|
||||
xpc_dictionary_set_string(msg, "event", "down");
|
||||
xpc_connection_send_message(entry->conn, msg);
|
||||
xpc_release(msg);
|
||||
xpc_connection_send_barrier(entry->conn, ^() {
|
||||
destroy_entry(entry);
|
||||
});
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query password from App using XPC channel
|
||||
*/
|
||||
static shared_key_t *query_password(xpc_connection_t conn, identification_t *id)
|
||||
{
|
||||
char buf[128], *password;
|
||||
xpc_object_t request, response;
|
||||
shared_key_t *shared = NULL;
|
||||
|
||||
request = xpc_dictionary_create(NULL, NULL, 0);
|
||||
xpc_dictionary_set_string(request, "type", "rpc");
|
||||
xpc_dictionary_set_string(request, "rpc", "get_password");
|
||||
snprintf(buf, sizeof(buf), "%Y", id);
|
||||
xpc_dictionary_set_string(request, "username", buf);
|
||||
|
||||
response = xpc_connection_send_message_with_reply_sync(conn, request);
|
||||
xpc_release(request);
|
||||
if (xpc_get_type(response) == XPC_TYPE_DICTIONARY)
|
||||
{
|
||||
password = (char*)xpc_dictionary_get_string(response, "password");
|
||||
if (password)
|
||||
{
|
||||
shared = shared_key_create(SHARED_EAP,
|
||||
chunk_clone(chunk_from_str(password)));
|
||||
}
|
||||
}
|
||||
xpc_release(response);
|
||||
return shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Password query callback
|
||||
*/
|
||||
static shared_key_t* password_cb(private_xpc_channels_t *this,
|
||||
shared_key_type_t type,
|
||||
identification_t *me, identification_t *other,
|
||||
id_match_t *match_me, id_match_t *match_other)
|
||||
{
|
||||
shared_key_t *shared = NULL;
|
||||
ike_sa_t *ike_sa;
|
||||
entry_t *entry;
|
||||
u_int32_t sa;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case SHARED_EAP:
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
ike_sa = charon->bus->get_sa(charon->bus);
|
||||
if (ike_sa)
|
||||
{
|
||||
sa = ike_sa->get_unique_id(ike_sa);
|
||||
this->lock->read_lock(this->lock);
|
||||
entry = this->channels->get(this->channels, (void*)sa);
|
||||
if (entry && !entry->passworded)
|
||||
{
|
||||
entry->passworded = TRUE;
|
||||
|
||||
shared = query_password(entry->conn, me);
|
||||
if (shared)
|
||||
{
|
||||
if (match_me)
|
||||
{
|
||||
*match_me = ID_MATCH_PERFECT;
|
||||
}
|
||||
if (match_other)
|
||||
{
|
||||
*match_other = ID_MATCH_PERFECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
return shared;
|
||||
}
|
||||
|
||||
METHOD(xpc_channels_t, destroy, void,
|
||||
private_xpc_channels_t *this)
|
||||
{
|
||||
lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
|
||||
this->creds->destroy(this->creds);
|
||||
this->channels->destroy(this->channels);
|
||||
this->lock->destroy(this->lock);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
xpc_channels_t *xpc_channels_create()
|
||||
{
|
||||
private_xpc_channels_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.listener = {
|
||||
.alert = _alert,
|
||||
.ike_updown = _ike_updown,
|
||||
.ike_rekey = _ike_rekey,
|
||||
.ike_state_change = _ike_state_change,
|
||||
.child_updown = _child_updown,
|
||||
},
|
||||
.add = _add,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.channels = hashtable_create(hashtable_hash_ptr,
|
||||
hashtable_equals_ptr, 4),
|
||||
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
||||
);
|
||||
|
||||
this->creds = callback_cred_create_shared(
|
||||
(callback_cred_shared_cb_t)password_cb, this);
|
||||
lib->credmgr->add_set(lib->credmgr, &this->creds->set);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup xpc_channels xpc_channels
|
||||
* @{ @ingroup xpc
|
||||
*/
|
||||
|
||||
#ifndef XPC_CHANNELS_H_
|
||||
#define XPC_CHANNELS_H_
|
||||
|
||||
#include <xpc/xpc.h>
|
||||
|
||||
#include <bus/bus.h>
|
||||
|
||||
typedef struct xpc_channels_t xpc_channels_t;
|
||||
|
||||
/**
|
||||
* XPC to App channel management.
|
||||
*/
|
||||
struct xpc_channels_t {
|
||||
|
||||
/**
|
||||
* Implements listener_t.
|
||||
*/
|
||||
listener_t listener;
|
||||
|
||||
/**
|
||||
* Associate an IKE_SA unique identifier to an XPC connection.
|
||||
*
|
||||
* @param conn XPC connection to channel
|
||||
* @param ike_sa IKE_SA unique identifier to associate to connection
|
||||
*/
|
||||
void (*add)(xpc_channels_t *this, xpc_connection_t conn, u_int32_t ike_sa);
|
||||
|
||||
/**
|
||||
* Destroy a xpc_channels_t.
|
||||
*/
|
||||
void (*destroy)(xpc_channels_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a xpc_channels instance.
|
||||
*/
|
||||
xpc_channels_t *xpc_channels_create();
|
||||
|
||||
#endif /** XPC_CHANNELS_H_ @}*/
|
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "xpc_dispatch.h"
|
||||
#include "xpc_channels.h"
|
||||
|
||||
#include <xpc/xpc.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <daemon.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
|
||||
typedef struct private_xpc_dispatch_t private_xpc_dispatch_t;
|
||||
|
||||
/**
|
||||
* Private data of an xpc_dispatch_t object.
|
||||
*/
|
||||
struct private_xpc_dispatch_t {
|
||||
|
||||
/**
|
||||
* Public xpc_dispatch_t interface.
|
||||
*/
|
||||
xpc_dispatch_t public;
|
||||
|
||||
/**
|
||||
* XPC service we offer
|
||||
*/
|
||||
xpc_connection_t service;
|
||||
|
||||
/**
|
||||
* XPC IKE_SA specific channels to App
|
||||
*/
|
||||
xpc_channels_t *channels;
|
||||
|
||||
/**
|
||||
* GCD queue for XPC events
|
||||
*/
|
||||
dispatch_queue_t queue;
|
||||
|
||||
/**
|
||||
* Number of active App connections
|
||||
*/
|
||||
refcount_t refcount;
|
||||
|
||||
/**
|
||||
* PID of main thread
|
||||
*/
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return version of this helper
|
||||
*/
|
||||
static void get_version(private_xpc_dispatch_t *this,
|
||||
xpc_object_t request, xpc_object_t reply)
|
||||
{
|
||||
xpc_dictionary_set_string(reply, "version", PACKAGE_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create peer config with associated ike config
|
||||
*/
|
||||
static peer_cfg_t* create_peer_cfg(char *name, char *host)
|
||||
{
|
||||
ike_cfg_t *ike_cfg;
|
||||
peer_cfg_t *peer_cfg;
|
||||
u_int16_t local_port, remote_port = IKEV2_UDP_PORT;
|
||||
|
||||
local_port = charon->socket->get_port(charon->socket, FALSE);
|
||||
if (local_port != IKEV2_UDP_PORT)
|
||||
{
|
||||
remote_port = IKEV2_NATT_PORT;
|
||||
}
|
||||
ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, local_port,
|
||||
host, FALSE, remote_port, FRAGMENTATION_NO, 0);
|
||||
ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
|
||||
peer_cfg = peer_cfg_create(name, ike_cfg,
|
||||
CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
|
||||
36000, 0, /* rekey 10h, reauth none */
|
||||
600, 600, /* jitter, over 10min */
|
||||
TRUE, FALSE, /* mobike, aggressive */
|
||||
30, 0, /* DPD delay, timeout */
|
||||
FALSE, NULL, NULL); /* mediation */
|
||||
peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0));
|
||||
|
||||
return peer_cfg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single auth cfg of given class to peer cfg
|
||||
*/
|
||||
static void add_auth_cfg(peer_cfg_t *peer_cfg, bool local,
|
||||
char *id, auth_class_t class)
|
||||
{
|
||||
auth_cfg_t *auth;
|
||||
|
||||
auth = auth_cfg_create();
|
||||
auth->add(auth, AUTH_RULE_AUTH_CLASS, class);
|
||||
auth->add(auth, AUTH_RULE_IDENTITY, identification_create_from_string(id));
|
||||
peer_cfg->add_auth_cfg(peer_cfg, auth, local);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach child config to peer config
|
||||
*/
|
||||
static child_cfg_t* create_child_cfg(char *name)
|
||||
{
|
||||
child_cfg_t *child_cfg;
|
||||
traffic_selector_t *ts;
|
||||
lifetime_cfg_t lifetime = {
|
||||
.time = {
|
||||
.life = 10800 /* 3h */,
|
||||
.rekey = 10200 /* 2h50min */,
|
||||
.jitter = 300 /* 5min */
|
||||
}
|
||||
};
|
||||
|
||||
child_cfg = child_cfg_create(name, &lifetime,
|
||||
NULL, FALSE, MODE_TUNNEL, /* updown, hostaccess */
|
||||
ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
|
||||
0, 0, NULL, NULL, 0);
|
||||
child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
|
||||
ts = traffic_selector_create_dynamic(0, 0, 65535);
|
||||
child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
|
||||
ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
|
||||
"0.0.0.0", 0, "255.255.255.255", 65535);
|
||||
child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
|
||||
|
||||
return child_cfg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller initiate callback
|
||||
*/
|
||||
static bool initiate_cb(u_int32_t *sa, debug_t group, level_t level,
|
||||
ike_sa_t *ike_sa, const char *message)
|
||||
{
|
||||
if (ike_sa)
|
||||
{
|
||||
*sa = ike_sa->get_unique_id(ike_sa);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start initiating an IKE connection
|
||||
*/
|
||||
void start_connection(private_xpc_dispatch_t *this,
|
||||
xpc_object_t request, xpc_object_t reply)
|
||||
{
|
||||
peer_cfg_t *peer_cfg;
|
||||
child_cfg_t *child_cfg;
|
||||
char *name, *id, *host;
|
||||
bool success = FALSE;
|
||||
xpc_endpoint_t endpoint;
|
||||
xpc_connection_t channel;
|
||||
u_int32_t ike_sa;
|
||||
|
||||
name = (char*)xpc_dictionary_get_string(request, "name");
|
||||
host = (char*)xpc_dictionary_get_string(request, "host");
|
||||
id = (char*)xpc_dictionary_get_string(request, "id");
|
||||
endpoint = xpc_dictionary_get_value(request, "channel");
|
||||
channel = xpc_connection_create_from_endpoint(endpoint);
|
||||
|
||||
if (name && id && host && channel)
|
||||
{
|
||||
peer_cfg = create_peer_cfg(name, host);
|
||||
|
||||
add_auth_cfg(peer_cfg, TRUE, id, AUTH_CLASS_EAP);
|
||||
add_auth_cfg(peer_cfg, FALSE, host, AUTH_CLASS_ANY);
|
||||
|
||||
child_cfg = create_child_cfg(name);
|
||||
peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
|
||||
|
||||
if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
|
||||
(controller_cb_t)initiate_cb, &ike_sa, 0) == NEED_MORE)
|
||||
{
|
||||
this->channels->add(this->channels, channel, ike_sa);
|
||||
success = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
xpc_dictionary_set_bool(reply, "success", success);
|
||||
}
|
||||
|
||||
/**
|
||||
* XPC RPC command dispatch table
|
||||
*/
|
||||
static struct {
|
||||
char *name;
|
||||
void (*handler)(private_xpc_dispatch_t *this,
|
||||
xpc_object_t request, xpc_object_t reply);
|
||||
} commands[] = {
|
||||
{ "get_version", get_version },
|
||||
{ "start_connection", start_connection },
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a received XPC request message
|
||||
*/
|
||||
static void handle(private_xpc_dispatch_t *this, xpc_object_t request)
|
||||
{
|
||||
xpc_connection_t client;
|
||||
xpc_object_t reply;
|
||||
const char *type, *rpc;
|
||||
bool found = FALSE;
|
||||
int i;
|
||||
|
||||
type = xpc_dictionary_get_string(request, "type");
|
||||
if (type)
|
||||
{
|
||||
if (streq(type, "rpc"))
|
||||
{
|
||||
reply = xpc_dictionary_create_reply(request);
|
||||
rpc = xpc_dictionary_get_string(request, "rpc");
|
||||
if (reply && rpc)
|
||||
{
|
||||
for (i = 0; i < countof(commands); i++)
|
||||
{
|
||||
if (streq(commands[i].name, rpc))
|
||||
{
|
||||
found = TRUE;
|
||||
commands[i].handler(this, request, reply);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
DBG1(DBG_CFG, "received invalid XPC rpc command: %s", rpc);
|
||||
}
|
||||
if (reply)
|
||||
{
|
||||
client = xpc_dictionary_get_remote_connection(request);
|
||||
xpc_connection_send_message(client, reply);
|
||||
xpc_release(reply);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "received unknown XPC message type: %s", type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "received XPC message without a type");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizer for client connections
|
||||
*/
|
||||
static void cleanup_connection(private_xpc_dispatch_t *this)
|
||||
{
|
||||
if (ref_put(&this->refcount))
|
||||
{
|
||||
DBG1(DBG_CFG, "no XPC connections, raising SIGTERM");
|
||||
kill(this->pid, SIGTERM);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up GCD handler for XPC events
|
||||
*/
|
||||
static void set_handler(private_xpc_dispatch_t *this)
|
||||
{
|
||||
xpc_connection_set_event_handler(this->service, ^(xpc_object_t conn)
|
||||
{
|
||||
xpc_connection_set_event_handler(conn, ^(xpc_object_t event)
|
||||
{
|
||||
if (xpc_get_type(event) == XPC_TYPE_DICTIONARY)
|
||||
{
|
||||
handle(this, event);
|
||||
}
|
||||
});
|
||||
ref_get(&this->refcount);
|
||||
xpc_connection_set_context(conn, this);
|
||||
xpc_connection_set_finalizer_f(conn, (void*)cleanup_connection);
|
||||
xpc_connection_resume(conn);
|
||||
});
|
||||
xpc_connection_resume(this->service);
|
||||
}
|
||||
|
||||
METHOD(xpc_dispatch_t, destroy, void,
|
||||
private_xpc_dispatch_t *this)
|
||||
{
|
||||
charon->bus->remove_listener(charon->bus, &this->channels->listener);
|
||||
this->channels->destroy(this->channels);
|
||||
if (this->service)
|
||||
{
|
||||
xpc_connection_suspend(this->service);
|
||||
xpc_release(this->service);
|
||||
}
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
xpc_dispatch_t *xpc_dispatch_create()
|
||||
{
|
||||
private_xpc_dispatch_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.channels = xpc_channels_create(),
|
||||
.queue = dispatch_queue_create("org.strongswan.charon-xpc.q",
|
||||
DISPATCH_QUEUE_CONCURRENT),
|
||||
.pid = getpid(),
|
||||
);
|
||||
charon->bus->add_listener(charon->bus, &this->channels->listener);
|
||||
|
||||
this->service = xpc_connection_create_mach_service(
|
||||
"org.strongswan.charon-xpc", this->queue,
|
||||
XPC_CONNECTION_MACH_SERVICE_LISTENER);
|
||||
if (!this->service)
|
||||
{
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
set_handler(this);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup xpc_dispatch xpc_dispatch
|
||||
* @{ @ingroup xpc
|
||||
*/
|
||||
|
||||
#ifndef XPC_DISPATCH_H_
|
||||
#define XPC_DISPATCH_H_
|
||||
|
||||
typedef struct xpc_dispatch_t xpc_dispatch_t;
|
||||
|
||||
/**
|
||||
* XPC dispatcher to control the daemon.
|
||||
*/
|
||||
struct xpc_dispatch_t {
|
||||
|
||||
/**
|
||||
* Destroy a xpc_dispatch_t.
|
||||
*/
|
||||
void (*destroy)(xpc_dispatch_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a xpc_dispatch instance.
|
||||
*/
|
||||
xpc_dispatch_t *xpc_dispatch_create();
|
||||
|
||||
#endif /** XPC_DISPATCH_H_ @}*/
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "xpc_logger.h"
|
||||
|
||||
typedef struct private_xpc_logger_t private_xpc_logger_t;
|
||||
|
||||
/**
|
||||
* Private data of an xpc_logger_t object.
|
||||
*/
|
||||
struct private_xpc_logger_t {
|
||||
|
||||
/**
|
||||
* Public xpc_logger_t interface.
|
||||
*/
|
||||
xpc_logger_t public;
|
||||
|
||||
/**
|
||||
* XPC channel to send logging messages to
|
||||
*/
|
||||
xpc_connection_t conn;
|
||||
|
||||
/**
|
||||
* IKE_SA we log for
|
||||
*/
|
||||
u_int32_t ike_sa;
|
||||
};
|
||||
|
||||
METHOD(logger_t, log_, void,
|
||||
private_xpc_logger_t *this, debug_t group, level_t level, int thread,
|
||||
ike_sa_t* ike_sa, const char *message)
|
||||
{
|
||||
if (ike_sa && ike_sa->get_unique_id(ike_sa) == this->ike_sa)
|
||||
{
|
||||
xpc_object_t msg;
|
||||
|
||||
msg = xpc_dictionary_create(NULL, NULL, 0);
|
||||
xpc_dictionary_set_string(msg, "type", "event");
|
||||
xpc_dictionary_set_string(msg, "event", "log");
|
||||
xpc_dictionary_set_string(msg, "message", message);
|
||||
xpc_connection_send_message(this->conn, msg);
|
||||
xpc_release(msg);
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(logger_t, get_level, level_t,
|
||||
private_xpc_logger_t *this, debug_t group)
|
||||
{
|
||||
return LEVEL_CTRL;
|
||||
}
|
||||
|
||||
METHOD(xpc_logger_t, set_ike_sa, void,
|
||||
private_xpc_logger_t *this, u_int32_t ike_sa)
|
||||
{
|
||||
this->ike_sa = ike_sa;
|
||||
}
|
||||
|
||||
METHOD(xpc_logger_t, destroy, void,
|
||||
private_xpc_logger_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
xpc_logger_t *xpc_logger_create(xpc_connection_t conn)
|
||||
{
|
||||
private_xpc_logger_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.logger = {
|
||||
.log = _log_,
|
||||
.get_level = _get_level,
|
||||
},
|
||||
.set_ike_sa = _set_ike_sa,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.conn = conn,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup xpc_logger xpc_logger
|
||||
* @{ @ingroup xpc
|
||||
*/
|
||||
|
||||
#ifndef XPC_LOGGER_H_
|
||||
#define XPC_LOGGER_H_
|
||||
|
||||
#include <xpc/xpc.h>
|
||||
|
||||
#include <daemon.h>
|
||||
|
||||
typedef struct xpc_logger_t xpc_logger_t;
|
||||
|
||||
/**
|
||||
* Connection specific logger over XPC.
|
||||
*/
|
||||
struct xpc_logger_t {
|
||||
|
||||
/**
|
||||
* Implements logger_t.
|
||||
*/
|
||||
logger_t logger;
|
||||
|
||||
/**
|
||||
* Set the IKE_SA unique identifier this logger logs for.
|
||||
*
|
||||
* @param ike_sa IKE_SA unique identifier
|
||||
*/
|
||||
void (*set_ike_sa)(xpc_logger_t *this, u_int32_t ike_sa);
|
||||
|
||||
/**
|
||||
* Destroy a xpc_logger_t.
|
||||
*/
|
||||
void (*destroy)(xpc_logger_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a xpc_logger instance.
|
||||
*
|
||||
* @param conn XPC connection to send logging events to
|
||||
* @return XPC logger
|
||||
*/
|
||||
xpc_logger_t *xpc_logger_create(xpc_connection_t conn);
|
||||
|
||||
#endif /** XPC_LOGGER_H_ @}*/
|
|
@ -0,0 +1,333 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
5B74989217311B200041971E /* xpc_channels.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B74989117311B200041971E /* xpc_channels.c */; };
|
||||
5B7498B8173275D10041971E /* xpc_logger.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B7498B7173275D10041971E /* xpc_logger.c */; };
|
||||
5BD1CCD71726DB4000587077 /* charon-xpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 5BD1CCD61726DB4000587077 /* charon-xpc.c */; };
|
||||
5BF60F31173405A000E5D608 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BD1CCD31726DB4000587077 /* CoreFoundation.framework */; };
|
||||
5BF60F33173405AC00E5D608 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BD1CCF21727DE3E00587077 /* Security.framework */; };
|
||||
5BF60F3E1734070A00E5D608 /* xpc_dispatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B74984C172AA3550041971E /* xpc_dispatch.c */; };
|
||||
5BF60F631743C57500E5D608 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BF60F621743C57500E5D608 /* SystemConfiguration.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
5B74984C172AA3550041971E /* xpc_dispatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xpc_dispatch.c; sourceTree = "<group>"; };
|
||||
5B74984E172AA3670041971E /* xpc_dispatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xpc_dispatch.h; sourceTree = "<group>"; };
|
||||
5B74989017311AFC0041971E /* xpc_channels.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xpc_channels.h; sourceTree = "<group>"; };
|
||||
5B74989117311B200041971E /* xpc_channels.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xpc_channels.c; sourceTree = "<group>"; };
|
||||
5B7498B7173275D10041971E /* xpc_logger.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xpc_logger.c; sourceTree = "<group>"; };
|
||||
5B7498B9173275DD0041971E /* xpc_logger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xpc_logger.h; sourceTree = "<group>"; };
|
||||
5BD1CCD31726DB4000587077 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
|
||||
5BD1CCD61726DB4000587077 /* charon-xpc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "charon-xpc.c"; sourceTree = "<group>"; };
|
||||
5BD1CCE01726DCD000587077 /* charon-xpc-Launchd.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "charon-xpc-Launchd.plist"; sourceTree = "<group>"; };
|
||||
5BD1CCE11726DD9900587077 /* charon-xpc-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "charon-xpc-Info.plist"; sourceTree = "<group>"; };
|
||||
5BD1CCEA1727CCA400587077 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = "<group>"; };
|
||||
5BD1CCEC1727D7AF00587077 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
|
||||
5BD1CCF21727DE3E00587077 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||
5BF60F621743C57500E5D608 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
5BD1CCCE1726DB4000587077 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5BF60F631743C57500E5D608 /* SystemConfiguration.framework in Frameworks */,
|
||||
5BF60F31173405A000E5D608 /* CoreFoundation.framework in Frameworks */,
|
||||
5BF60F33173405AC00E5D608 /* Security.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
5BD1CCA11726DB0100587077 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5BD1CCEA1727CCA400587077 /* README.md */,
|
||||
5BD1CCD51726DB4000587077 /* charon-xpc */,
|
||||
5BD1CCAF1726DB0100587077 /* Frameworks */,
|
||||
5BD1CCAD1726DB0100587077 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5BD1CCAD1726DB0100587077 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5BD1CCD11726DB4000587077 /* org.strongswan.charon-xpc */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5BD1CCAF1726DB0100587077 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5BF60F621743C57500E5D608 /* SystemConfiguration.framework */,
|
||||
5BD1CCF21727DE3E00587077 /* Security.framework */,
|
||||
5BD1CCEC1727D7AF00587077 /* ServiceManagement.framework */,
|
||||
5BD1CCD31726DB4000587077 /* CoreFoundation.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5BD1CCD51726DB4000587077 /* charon-xpc */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5BD1CCD61726DB4000587077 /* charon-xpc.c */,
|
||||
5BD1CCE01726DCD000587077 /* charon-xpc-Launchd.plist */,
|
||||
5BD1CCE11726DD9900587077 /* charon-xpc-Info.plist */,
|
||||
5B74984C172AA3550041971E /* xpc_dispatch.c */,
|
||||
5B74984E172AA3670041971E /* xpc_dispatch.h */,
|
||||
5B74989017311AFC0041971E /* xpc_channels.h */,
|
||||
5B74989117311B200041971E /* xpc_channels.c */,
|
||||
5B7498B7173275D10041971E /* xpc_logger.c */,
|
||||
5B7498B9173275DD0041971E /* xpc_logger.h */,
|
||||
);
|
||||
path = "charon-xpc";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
5BD1CCD01726DB4000587077 /* charon-xpc */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 5BD1CCDA1726DB4000587077 /* Build configuration list for PBXNativeTarget "charon-xpc" */;
|
||||
buildPhases = (
|
||||
5BD1CCCD1726DB4000587077 /* Sources */,
|
||||
5BD1CCCE1726DB4000587077 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "charon-xpc";
|
||||
productName = "charon-xpc";
|
||||
productReference = 5BD1CCD11726DB4000587077 /* org.strongswan.charon-xpc */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
5BD1CCA31726DB0100587077 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0450;
|
||||
ORGANIZATIONNAME = "revosec AG";
|
||||
};
|
||||
buildConfigurationList = 5BD1CCA61726DB0100587077 /* Build configuration list for PBXProject "strongSwan" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 5BD1CCA11726DB0100587077;
|
||||
productRefGroup = 5BD1CCAD1726DB0100587077 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
5BD1CCD01726DB4000587077 /* charon-xpc */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
5BD1CCCD1726DB4000587077 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5BD1CCD71726DB4000587077 /* charon-xpc.c in Sources */,
|
||||
5B74989217311B200041971E /* xpc_channels.c in Sources */,
|
||||
5BF60F3E1734070A00E5D608 /* xpc_dispatch.c in Sources */,
|
||||
5B7498B8173275D10041971E /* xpc_logger.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
5BD1CCC81726DB0200587077 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
5BD1CCC91726DB0200587077 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
5BD1CCDB1726DB4000587077 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Joe Developer";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
/usr/include,
|
||||
../../libstrongswan,
|
||||
../../libcharon,
|
||||
../../libhydra,
|
||||
/opt/local/include,
|
||||
);
|
||||
INFOPLIST_FILE = "charon-xpc/charon-xpc-Info.plist";
|
||||
INSTALL_PATH = /;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
/usr/lib,
|
||||
../../libstrongswan/.libs,
|
||||
../../libcharon/.libs,
|
||||
../../libhydra/.libs,
|
||||
/opt/local/lib,
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-include",
|
||||
../../../config.h,
|
||||
"-DVSTR_COMPILE_INLINE=0",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-lcrypto",
|
||||
/opt/local/lib/libvstr.a,
|
||||
"-force_load",
|
||||
../../libstrongswan/.libs/libstrongswan.a,
|
||||
"-force_load",
|
||||
../../libhydra/.libs/libhydra.a,
|
||||
"-force_load",
|
||||
../../libcharon/.libs/libcharon.a,
|
||||
"-sectcreate",
|
||||
__TEXT,
|
||||
__info_plist,
|
||||
"charon-xpc/charon-xpc-Info.plist",
|
||||
"-sectcreate",
|
||||
__TEXT,
|
||||
__launchd_plist,
|
||||
"charon-xpc/charon-xpc-Launchd.plist",
|
||||
);
|
||||
PRODUCT_NAME = "org.strongswan.charon-xpc";
|
||||
PROVISIONING_PROFILE = "";
|
||||
STRIP_STYLE = "non-global";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
5BD1CCDC1726DB4000587077 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Joe Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
/usr/include,
|
||||
../../libstrongswan,
|
||||
../../libcharon,
|
||||
../../libhydra,
|
||||
/opt/local/include,
|
||||
);
|
||||
INFOPLIST_FILE = "charon-xpc/charon-xpc-Info.plist";
|
||||
INSTALL_PATH = /;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
/usr/lib,
|
||||
../../libstrongswan/.libs,
|
||||
../../libcharon/.libs,
|
||||
../../libhydra/.libs,
|
||||
/opt/local/lib,
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-include",
|
||||
../../../config.h,
|
||||
"-DVSTR_COMPILE_INLINE=0",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-lcrypto",
|
||||
/opt/local/lib/libvstr.a,
|
||||
"-force_load",
|
||||
../../libstrongswan/.libs/libstrongswan.a,
|
||||
"-force_load",
|
||||
../../libhydra/.libs/libhydra.a,
|
||||
"-force_load",
|
||||
../../libcharon/.libs/libcharon.a,
|
||||
"-sectcreate",
|
||||
__TEXT,
|
||||
__info_plist,
|
||||
"charon-xpc/charon-xpc-Info.plist",
|
||||
"-sectcreate",
|
||||
__TEXT,
|
||||
__launchd_plist,
|
||||
"charon-xpc/charon-xpc-Launchd.plist",
|
||||
);
|
||||
PRODUCT_NAME = "org.strongswan.charon-xpc";
|
||||
PROVISIONING_PROFILE = "";
|
||||
STRIP_STYLE = "non-global";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
5BD1CCA61726DB0100587077 /* Build configuration list for PBXProject "strongSwan" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
5BD1CCC81726DB0200587077 /* Debug */,
|
||||
5BD1CCC91726DB0200587077 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
5BD1CCDA1726DB4000587077 /* Build configuration list for PBXNativeTarget "charon-xpc" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
5BD1CCDB1726DB4000587077 /* Debug */,
|
||||
5BD1CCDC1726DB4000587077 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 5BD1CCA31726DB0100587077 /* Project object */;
|
||||
}
|
|
@ -173,6 +173,7 @@ sys_logger_t *sys_logger_create(int facility)
|
|||
);
|
||||
|
||||
set_level(this, DBG_ANY, LEVEL_SILENT);
|
||||
setlogmask(LOG_UPTO(LOG_INFO));
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
|
|
@ -423,6 +423,13 @@ if MONOLITHIC
|
|||
endif
|
||||
endif
|
||||
|
||||
if USE_KEYCHAIN
|
||||
SUBDIRS += plugins/keychain
|
||||
if MONOLITHIC
|
||||
libstrongswan_la_LIBADD += plugins/keychain/libstrongswan-keychain.la
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_PKCS11
|
||||
SUBDIRS += plugins/pkcs11
|
||||
if MONOLITHIC
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include "hashtable.h"
|
||||
|
||||
#include <utils/chunk.h>
|
||||
|
||||
/** The maximum capacity of the hash table (MUST be a power of 2) */
|
||||
#define MAX_CAPACITY (1 << 30)
|
||||
|
||||
|
@ -146,9 +148,40 @@ struct private_enumerator_t {
|
|||
* previous pair (used by remove_at)
|
||||
*/
|
||||
pair_t *prev;
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* See header.
|
||||
*/
|
||||
u_int hashtable_hash_ptr(void *key)
|
||||
{
|
||||
return chunk_hash(chunk_from_thing(key));
|
||||
}
|
||||
|
||||
/*
|
||||
* See header.
|
||||
*/
|
||||
u_int hashtable_hash_str(void *key)
|
||||
{
|
||||
return chunk_hash(chunk_from_str((char*)key));
|
||||
}
|
||||
|
||||
/*
|
||||
* See header.
|
||||
*/
|
||||
bool hashtable_equals_ptr(void *key, void *other_key)
|
||||
{
|
||||
return key == other_key;
|
||||
}
|
||||
|
||||
/*
|
||||
* See header.
|
||||
*/
|
||||
bool hashtable_equals_str(void *key, void *other_key)
|
||||
{
|
||||
return streq(key, other_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the next-highest power of two for the given number.
|
||||
* The algorithm works by setting all bits on the right-hand side of the most
|
||||
|
@ -441,4 +474,3 @@ hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
|
|||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,22 @@ typedef struct hashtable_t hashtable_t;
|
|||
*/
|
||||
typedef u_int (*hashtable_hash_t)(void *key);
|
||||
|
||||
/**
|
||||
* Hashtable hash function calculation the hash solely based on the key pointer.
|
||||
*
|
||||
* @param key key to hash
|
||||
* @return hash of key
|
||||
*/
|
||||
u_int hashtable_hash_ptr(void *key);
|
||||
|
||||
/**
|
||||
* Hashtable hash function calculation the hash for char* keys.
|
||||
*
|
||||
* @param key key to hash, a char*
|
||||
* @return hash of key
|
||||
*/
|
||||
u_int hashtable_hash_str(void *key);
|
||||
|
||||
/**
|
||||
* Prototype for a function that compares the two keys for equality.
|
||||
*
|
||||
|
@ -42,6 +58,24 @@ typedef u_int (*hashtable_hash_t)(void *key);
|
|||
*/
|
||||
typedef bool (*hashtable_equals_t)(void *key, void *other_key);
|
||||
|
||||
/**
|
||||
* Hashtable equals function comparing pointers.
|
||||
*
|
||||
* @param key key to compare
|
||||
* @param other_key other key to compare
|
||||
* @return TRUE if key == other_key
|
||||
*/
|
||||
bool hashtable_equals_ptr(void *key, void *other_key);
|
||||
|
||||
/**
|
||||
* Hashtable equals function comparing char* keys.
|
||||
*
|
||||
* @param key key to compare
|
||||
* @param other_key other key to compare
|
||||
* @return TRUE if streq(key, other_key)
|
||||
*/
|
||||
bool hashtable_equals_str(void *key, void *other_key);
|
||||
|
||||
/**
|
||||
* Class implementing a hash table.
|
||||
*
|
||||
|
@ -121,7 +155,6 @@ struct hashtable_t {
|
|||
* Destroys a hash table object.
|
||||
*/
|
||||
void (*destroy) (hashtable_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -378,8 +378,8 @@ METHOD(credential_manager_t, get_shared, shared_key_t*,
|
|||
identification_t *me, identification_t *other)
|
||||
{
|
||||
shared_key_t *current, *found = NULL;
|
||||
id_match_t *best_me = ID_MATCH_NONE, *best_other = ID_MATCH_NONE;
|
||||
id_match_t *match_me, *match_other;
|
||||
id_match_t best_me = ID_MATCH_NONE, best_other = ID_MATCH_NONE;
|
||||
id_match_t match_me, match_other;
|
||||
enumerator_t *enumerator;
|
||||
|
||||
enumerator = create_shared_enumerator(this, type, me, other);
|
||||
|
@ -393,6 +393,10 @@ METHOD(credential_manager_t, get_shared, shared_key_t*,
|
|||
best_me = match_me;
|
||||
best_other = match_other;
|
||||
}
|
||||
if (best_me == ID_MATCH_PERFECT && best_other == ID_MATCH_PERFECT)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return found;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
INCLUDES = -I$(top_srcdir)/src/libstrongswan
|
||||
|
||||
AM_CFLAGS = -rdynamic
|
||||
|
||||
if MONOLITHIC
|
||||
noinst_LTLIBRARIES = libstrongswan-keychain.la
|
||||
else
|
||||
plugin_LTLIBRARIES = libstrongswan-keychain.la
|
||||
endif
|
||||
|
||||
libstrongswan_keychain_la_SOURCES = \
|
||||
keychain_plugin.h keychain_plugin.c \
|
||||
keychain_creds.h keychain_creds.c
|
||||
|
||||
libstrongswan_keychain_la_LDFLAGS = -module -avoid-version \
|
||||
-framework Security -framework CoreFoundation
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "keychain_creds.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <credentials/sets/mem_cred.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
|
||||
#include <Security/Security.h>
|
||||
|
||||
/**
|
||||
* System Roots keychain
|
||||
*/
|
||||
#define SYSTEM_ROOTS "/System/Library/Keychains/SystemRootCertificates.keychain"
|
||||
|
||||
/**
|
||||
* System keychain
|
||||
*/
|
||||
#define SYSTEM "/Library/Keychains/System.keychain"
|
||||
|
||||
typedef struct private_keychain_creds_t private_keychain_creds_t;
|
||||
|
||||
/**
|
||||
* Private data of an keychain_creds_t object.
|
||||
*/
|
||||
struct private_keychain_creds_t {
|
||||
|
||||
/**
|
||||
* Public keychain_creds_t interface.
|
||||
*/
|
||||
keychain_creds_t public;
|
||||
|
||||
/**
|
||||
* Active in-memory credential set
|
||||
*/
|
||||
mem_cred_t *set;
|
||||
|
||||
/**
|
||||
* System roots credential set
|
||||
*/
|
||||
mem_cred_t *roots;
|
||||
|
||||
/**
|
||||
* Run loop of event monitoring thread
|
||||
*/
|
||||
CFRunLoopRef loop;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a credential sets with certificates from a keychain path
|
||||
*/
|
||||
static mem_cred_t* load_certs(private_keychain_creds_t *this, char *path)
|
||||
{
|
||||
SecKeychainRef keychain;
|
||||
SecKeychainSearchRef search;
|
||||
SecKeychainItemRef item;
|
||||
mem_cred_t *set;
|
||||
OSStatus status;
|
||||
|
||||
set = mem_cred_create();
|
||||
|
||||
DBG1(DBG_CFG, "loading certificates from %s:", path);
|
||||
status = SecKeychainOpen(path, &keychain);
|
||||
if (status == errSecSuccess)
|
||||
{
|
||||
status = SecKeychainSearchCreateFromAttributes(keychain,
|
||||
kSecCertificateItemClass, NULL, &search);
|
||||
if (status == errSecSuccess)
|
||||
{
|
||||
while (SecKeychainSearchCopyNext(search, &item) == errSecSuccess)
|
||||
{
|
||||
certificate_t *cert;
|
||||
UInt32 len;
|
||||
void *data;
|
||||
|
||||
if (SecKeychainItemCopyAttributesAndData(item, NULL, NULL, NULL,
|
||||
&len, &data) == errSecSuccess)
|
||||
{
|
||||
cert = lib->creds->create(lib->creds,
|
||||
CRED_CERTIFICATE, CERT_X509,
|
||||
BUILD_BLOB_ASN1_DER, chunk_create(data, len),
|
||||
BUILD_END);
|
||||
if (cert)
|
||||
{
|
||||
DBG1(DBG_CFG, " loaded '%Y'", cert->get_subject(cert));
|
||||
set->add_cert(set, TRUE, cert);
|
||||
}
|
||||
SecKeychainItemFreeAttributesAndData(NULL, data);
|
||||
}
|
||||
CFRelease(item);
|
||||
}
|
||||
CFRelease(search);
|
||||
}
|
||||
CFRelease(keychain);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function reloading keychain on changes
|
||||
*/
|
||||
static OSStatus keychain_cb(SecKeychainEvent keychainEvent,
|
||||
SecKeychainCallbackInfo *info,
|
||||
private_keychain_creds_t *this)
|
||||
{
|
||||
mem_cred_t *new;
|
||||
|
||||
DBG1(DBG_CFG, "received keychain event, reloading credentials");
|
||||
|
||||
/* register new before removing old */
|
||||
new = load_certs(this, SYSTEM);
|
||||
lib->credmgr->add_set(lib->credmgr, &new->set);
|
||||
lib->credmgr->remove_set(lib->credmgr, &this->set->set);
|
||||
|
||||
lib->credmgr->flush_cache(lib->credmgr, CERT_X509);
|
||||
|
||||
this->set->destroy(this->set);
|
||||
this->set = new;
|
||||
|
||||
return errSecSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for changes in the keychain and handle them
|
||||
*/
|
||||
static job_requeue_t monitor_changes(private_keychain_creds_t *this)
|
||||
{
|
||||
if (SecKeychainAddCallback((SecKeychainCallback)keychain_cb,
|
||||
kSecAddEventMask | kSecDeleteEventMask |
|
||||
kSecUpdateEventMask | kSecTrustSettingsChangedEventMask,
|
||||
this) == errSecSuccess)
|
||||
{
|
||||
this->loop = CFRunLoopGetCurrent();
|
||||
|
||||
/* does not return until cancelled */
|
||||
CFRunLoopRun();
|
||||
|
||||
this->loop = NULL;
|
||||
SecKeychainRemoveCallback((SecKeychainCallback)keychain_cb);
|
||||
}
|
||||
return JOB_REQUEUE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the monitoring thread in its RunLoop
|
||||
*/
|
||||
static bool cancel_monitor(private_keychain_creds_t *this)
|
||||
{
|
||||
if (this->loop)
|
||||
{
|
||||
CFRunLoopStop(this->loop);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(keychain_creds_t, destroy, void,
|
||||
private_keychain_creds_t *this)
|
||||
{
|
||||
lib->credmgr->remove_set(lib->credmgr, &this->set->set);
|
||||
lib->credmgr->remove_set(lib->credmgr, &this->roots->set);
|
||||
this->set->destroy(this->set);
|
||||
this->roots->destroy(this->roots);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
keychain_creds_t *keychain_creds_create()
|
||||
{
|
||||
private_keychain_creds_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.destroy = _destroy,
|
||||
},
|
||||
);
|
||||
|
||||
this->roots = load_certs(this, SYSTEM_ROOTS);
|
||||
this->set = load_certs(this, SYSTEM);
|
||||
|
||||
lib->credmgr->add_set(lib->credmgr, &this->roots->set);
|
||||
lib->credmgr->add_set(lib->credmgr, &this->set->set);
|
||||
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)callback_job_create_with_prio((void*)monitor_changes,
|
||||
this, NULL, (void*)cancel_monitor, JOB_PRIO_CRITICAL));
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup keychain_creds keychain_creds
|
||||
* @{ @ingroup keychain
|
||||
*/
|
||||
|
||||
#ifndef KEYCHAIN_CREDS_H_
|
||||
#define KEYCHAIN_CREDS_H_
|
||||
|
||||
typedef struct keychain_creds_t keychain_creds_t;
|
||||
|
||||
#include <credentials/credential_manager.h>
|
||||
|
||||
/**
|
||||
* Credential set using OS X Keychain Services.
|
||||
*/
|
||||
struct keychain_creds_t {
|
||||
|
||||
/**
|
||||
* Destroy a keychain_creds_t.
|
||||
*/
|
||||
void (*destroy)(keychain_creds_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a keychain_creds instance.
|
||||
*/
|
||||
keychain_creds_t *keychain_creds_create();
|
||||
|
||||
#endif /** KEYCHAIN_CREDS_H_ @}*/
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "keychain_plugin.h"
|
||||
#include "keychain_creds.h"
|
||||
|
||||
#include <library.h>
|
||||
|
||||
typedef struct private_keychain_plugin_t private_keychain_plugin_t;
|
||||
|
||||
/**
|
||||
* private data of keychain_plugin
|
||||
*/
|
||||
struct private_keychain_plugin_t {
|
||||
|
||||
/**
|
||||
* public functions
|
||||
*/
|
||||
keychain_plugin_t public;
|
||||
|
||||
/**
|
||||
* System level Keychain Services credential set
|
||||
*/
|
||||
keychain_creds_t *creds;
|
||||
};
|
||||
|
||||
METHOD(plugin_t, get_name, char*,
|
||||
private_keychain_plugin_t *this)
|
||||
{
|
||||
return "keychain";
|
||||
}
|
||||
|
||||
/**
|
||||
* Load/unload certificates from Keychain.
|
||||
*/
|
||||
static bool load_creds(private_keychain_plugin_t *this,
|
||||
plugin_feature_t *feature, bool reg, void *data)
|
||||
{
|
||||
if (reg)
|
||||
{
|
||||
this->creds = keychain_creds_create();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->creds->destroy(this->creds);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(plugin_t, get_features, int,
|
||||
private_keychain_plugin_t *this, plugin_feature_t *features[])
|
||||
{
|
||||
static plugin_feature_t f[] = {
|
||||
PLUGIN_CALLBACK((plugin_feature_callback_t)load_creds, NULL),
|
||||
PLUGIN_PROVIDE(CUSTOM, "keychain"),
|
||||
PLUGIN_DEPENDS(CERT_DECODE, CERT_X509),
|
||||
};
|
||||
*features = f;
|
||||
return countof(f);
|
||||
}
|
||||
|
||||
METHOD(plugin_t, destroy, void,
|
||||
private_keychain_plugin_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* see header file
|
||||
*/
|
||||
plugin_t *keychain_plugin_create()
|
||||
{
|
||||
private_keychain_plugin_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.plugin = {
|
||||
.get_name = _get_name,
|
||||
.get_features = _get_features,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return &this->public.plugin;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup keychain keychain
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @defgroup keychain_plugin keychain_plugin
|
||||
* @{ @ingroup keychain
|
||||
*/
|
||||
|
||||
#ifndef KEYCHAIN_PLUGIN_H_
|
||||
#define KEYCHAIN_PLUGIN_H_
|
||||
|
||||
#include <plugins/plugin.h>
|
||||
|
||||
typedef struct keychain_plugin_t keychain_plugin_t;
|
||||
|
||||
/**
|
||||
* Plugin providing OS X Keychain Services support.
|
||||
*/
|
||||
struct keychain_plugin_t {
|
||||
|
||||
/**
|
||||
* Implements plugin interface,
|
||||
*/
|
||||
plugin_t plugin;
|
||||
};
|
||||
|
||||
#endif /** KEYCHAIN_PLUGIN_H_ @}*/
|
|
@ -678,6 +678,41 @@ static bool parse_keyUsage_ext(private_openssl_x509_t *this,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse ExtendedKeyUsage
|
||||
*/
|
||||
static bool parse_extKeyUsage_ext(private_openssl_x509_t *this,
|
||||
X509_EXTENSION *ext)
|
||||
{
|
||||
EXTENDED_KEY_USAGE *usage;
|
||||
int i;
|
||||
|
||||
usage = X509V3_EXT_d2i(ext);
|
||||
if (usage)
|
||||
{
|
||||
for (i = 0; i < sk_ASN1_OBJECT_num(usage); i++)
|
||||
{
|
||||
switch (OBJ_obj2nid(sk_ASN1_OBJECT_value(usage, i)))
|
||||
{
|
||||
case NID_server_auth:
|
||||
this->flags |= X509_SERVER_AUTH;
|
||||
break;
|
||||
case NID_client_auth:
|
||||
this->flags |= X509_CLIENT_AUTH;
|
||||
break;
|
||||
case NID_OCSP_sign:
|
||||
this->flags |= X509_OCSP_SIGNER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
sk_ASN1_OBJECT_pop_free(usage, ASN1_OBJECT_free);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse CRL distribution points
|
||||
*/
|
||||
|
@ -963,6 +998,9 @@ static bool parse_extensions(private_openssl_x509_t *this)
|
|||
case NID_key_usage:
|
||||
ok = parse_keyUsage_ext(this, ext);
|
||||
break;
|
||||
case NID_ext_key_usage:
|
||||
ok = parse_extKeyUsage_ext(this, ext);
|
||||
break;
|
||||
case NID_crl_distribution_points:
|
||||
ok = parse_crlDistributionPoints_ext(this, ext);
|
||||
break;
|
||||
|
@ -977,7 +1015,12 @@ static bool parse_extensions(private_openssl_x509_t *this)
|
|||
"libstrongswan.x509.enforce_critical", TRUE);
|
||||
if (!ok)
|
||||
{
|
||||
DBG1(DBG_LIB, "found unsupported critical X.509 extension");
|
||||
char buf[80] = "";
|
||||
|
||||
OBJ_obj2txt(buf, sizeof(buf),
|
||||
X509_EXTENSION_get_object(ext), 0);
|
||||
DBG1(DBG_LIB, "found unsupported critical X.509 "
|
||||
"extension: %s", buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -990,38 +1033,6 @@ static bool parse_extensions(private_openssl_x509_t *this)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse ExtendedKeyUsage
|
||||
*/
|
||||
static void parse_extKeyUsage(private_openssl_x509_t *this)
|
||||
{
|
||||
EXTENDED_KEY_USAGE *usage;
|
||||
int i;
|
||||
|
||||
usage = X509_get_ext_d2i(this->x509, NID_ext_key_usage, NULL, NULL);
|
||||
if (usage)
|
||||
{
|
||||
for (i = 0; i < sk_ASN1_OBJECT_num(usage); i++)
|
||||
{
|
||||
switch (OBJ_obj2nid(sk_ASN1_OBJECT_value(usage, i)))
|
||||
{
|
||||
case NID_server_auth:
|
||||
this->flags |= X509_SERVER_AUTH;
|
||||
break;
|
||||
case NID_client_auth:
|
||||
this->flags |= X509_CLIENT_AUTH;
|
||||
break;
|
||||
case NID_OCSP_sign:
|
||||
this->flags |= X509_OCSP_SIGNER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
sk_ASN1_OBJECT_pop_free(usage, ASN1_OBJECT_free);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a DER encoded x509 certificate
|
||||
*/
|
||||
|
@ -1088,7 +1099,6 @@ static bool parse_certificate(private_openssl_x509_t *this)
|
|||
{
|
||||
return FALSE;
|
||||
}
|
||||
parse_extKeyUsage(this);
|
||||
|
||||
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
|
||||
if (!hasher || !hasher->allocate_hash(hasher, this->encoding, &this->hash))
|
||||
|
|
|
@ -341,7 +341,20 @@ thread_t *thread_create(thread_main_t main, void *arg)
|
|||
*/
|
||||
thread_t *thread_current()
|
||||
{
|
||||
return current_thread->get(current_thread);
|
||||
private_thread_t *this;
|
||||
|
||||
this = (private_thread_t*)current_thread->get(current_thread);
|
||||
if (!this)
|
||||
{
|
||||
this = thread_create_internal();
|
||||
|
||||
id_mutex->lock(id_mutex);
|
||||
this->id = next_id++;
|
||||
id_mutex->unlock(id_mutex);
|
||||
|
||||
current_thread->set(current_thread, (void*)this);
|
||||
}
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue