aboutsummaryrefslogtreecommitdiffstats
path: root/src/frontends/osx/charon-xpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontends/osx/charon-xpc')
-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
9 files changed, 1377 insertions, 0 deletions
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_ @}*/