aboutsummaryrefslogtreecommitdiffstats
path: root/src/frontends/osx
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontends/osx')
-rw-r--r--src/frontends/osx/.gitignore2
-rw-r--r--src/frontends/osx/README.md98
-rw-r--r--src/frontends/osx/charon-xpc/charon-xpc-Info.plist18
-rw-r--r--src/frontends/osx/charon-xpc/charon-xpc-Launchd.plist13
-rw-r--r--src/frontends/osx/charon-xpc/charon-xpc.c207
-rw-r--r--src/frontends/osx/charon-xpc/xpc_channels.c540
-rw-r--r--src/frontends/osx/charon-xpc/xpc_channels.h59
-rw-r--r--src/frontends/osx/charon-xpc/xpc_dispatch.c341
-rw-r--r--src/frontends/osx/charon-xpc/xpc_dispatch.h42
-rw-r--r--src/frontends/osx/charon-xpc/xpc_logger.c96
-rw-r--r--src/frontends/osx/charon-xpc/xpc_logger.h61
-rw-r--r--src/frontends/osx/strongSwan.xcodeproj/project.pbxproj333
12 files changed, 1810 insertions, 0 deletions
diff --git a/src/frontends/osx/.gitignore b/src/frontends/osx/.gitignore
new file mode 100644
index 000000000..f4be87183
--- /dev/null
+++ b/src/frontends/osx/.gitignore
@@ -0,0 +1,2 @@
+xcuserdata
+*.xcworkspace
diff --git a/src/frontends/osx/README.md b/src/frontends/osx/README.md
new file mode 100644
index 000000000..bd24cce1b
--- /dev/null
+++ b/src/frontends/osx/README.md
@@ -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
diff --git a/src/frontends/osx/charon-xpc/charon-xpc-Info.plist b/src/frontends/osx/charon-xpc/charon-xpc-Info.plist
new file mode 100644
index 000000000..e8ddd24b0
--- /dev/null
+++ b/src/frontends/osx/charon-xpc/charon-xpc-Info.plist
@@ -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>
diff --git a/src/frontends/osx/charon-xpc/charon-xpc-Launchd.plist b/src/frontends/osx/charon-xpc/charon-xpc-Launchd.plist
new file mode 100644
index 000000000..703fab912
--- /dev/null
+++ b/src/frontends/osx/charon-xpc/charon-xpc-Launchd.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>
diff --git a/src/frontends/osx/charon-xpc/charon-xpc.c b/src/frontends/osx/charon-xpc/charon-xpc.c
new file mode 100644
index 000000000..a1f64112a
--- /dev/null
+++ b/src/frontends/osx/charon-xpc/charon-xpc.c
@@ -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();
+}
diff --git a/src/frontends/osx/charon-xpc/xpc_channels.c b/src/frontends/osx/charon-xpc/xpc_channels.c
new file mode 100644
index 000000000..9b5260047
--- /dev/null
+++ b/src/frontends/osx/charon-xpc/xpc_channels.c
@@ -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;
+}
diff --git a/src/frontends/osx/charon-xpc/xpc_channels.h b/src/frontends/osx/charon-xpc/xpc_channels.h
new file mode 100644
index 000000000..125a81f1d
--- /dev/null
+++ b/src/frontends/osx/charon-xpc/xpc_channels.h
@@ -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_ @}*/
diff --git a/src/frontends/osx/charon-xpc/xpc_dispatch.c b/src/frontends/osx/charon-xpc/xpc_dispatch.c
new file mode 100644
index 000000000..f60bcbfe0
--- /dev/null
+++ b/src/frontends/osx/charon-xpc/xpc_dispatch.c
@@ -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;
+}
diff --git a/src/frontends/osx/charon-xpc/xpc_dispatch.h b/src/frontends/osx/charon-xpc/xpc_dispatch.h
new file mode 100644
index 000000000..9f40e6027
--- /dev/null
+++ b/src/frontends/osx/charon-xpc/xpc_dispatch.h
@@ -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_ @}*/
diff --git a/src/frontends/osx/charon-xpc/xpc_logger.c b/src/frontends/osx/charon-xpc/xpc_logger.c
new file mode 100644
index 000000000..38c34e460
--- /dev/null
+++ b/src/frontends/osx/charon-xpc/xpc_logger.c
@@ -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;
+}
diff --git a/src/frontends/osx/charon-xpc/xpc_logger.h b/src/frontends/osx/charon-xpc/xpc_logger.h
new file mode 100644
index 000000000..fd5ad37a2
--- /dev/null
+++ b/src/frontends/osx/charon-xpc/xpc_logger.h
@@ -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_ @}*/
diff --git a/src/frontends/osx/strongSwan.xcodeproj/project.pbxproj b/src/frontends/osx/strongSwan.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..2eb88b77a
--- /dev/null
+++ b/src/frontends/osx/strongSwan.xcodeproj/project.pbxproj
@@ -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 */;
+}