Merge branch 'charon-xpc'
authorMartin Willi <martin@revosec.ch>
Thu, 18 Jul 2013 10:18:32 +0000 (12:18 +0200)
committerMartin Willi <martin@revosec.ch>
Thu, 18 Jul 2013 10:18:32 +0000 (12:18 +0200)
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.

25 files changed:
configure.in
src/frontends/osx/.gitignore [new file with mode: 0644]
src/frontends/osx/README.md [new file with mode: 0644]
src/frontends/osx/charon-xpc/charon-xpc-Info.plist [new file with mode: 0644]
src/frontends/osx/charon-xpc/charon-xpc-Launchd.plist [new file with mode: 0644]
src/frontends/osx/charon-xpc/charon-xpc.c [new file with mode: 0644]
src/frontends/osx/charon-xpc/xpc_channels.c [new file with mode: 0644]
src/frontends/osx/charon-xpc/xpc_channels.h [new file with mode: 0644]
src/frontends/osx/charon-xpc/xpc_dispatch.c [new file with mode: 0644]
src/frontends/osx/charon-xpc/xpc_dispatch.h [new file with mode: 0644]
src/frontends/osx/charon-xpc/xpc_logger.c [new file with mode: 0644]
src/frontends/osx/charon-xpc/xpc_logger.h [new file with mode: 0644]
src/frontends/osx/strongSwan.xcodeproj/project.pbxproj [new file with mode: 0644]
src/libcharon/bus/listeners/sys_logger.c
src/libstrongswan/Makefile.am
src/libstrongswan/collections/hashtable.c
src/libstrongswan/collections/hashtable.h
src/libstrongswan/credentials/credential_manager.c
src/libstrongswan/plugins/keychain/Makefile.am [new file with mode: 0644]
src/libstrongswan/plugins/keychain/keychain_creds.c [new file with mode: 0644]
src/libstrongswan/plugins/keychain/keychain_creds.h [new file with mode: 0644]
src/libstrongswan/plugins/keychain/keychain_plugin.c [new file with mode: 0644]
src/libstrongswan/plugins/keychain/keychain_plugin.h [new file with mode: 0644]
src/libstrongswan/plugins/openssl/openssl_x509.c
src/libstrongswan/threading/thread.c

index 53d06a3..f1524c2 100644 (file)
@@ -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
diff --git a/src/frontends/osx/.gitignore b/src/frontends/osx/.gitignore
new file mode 100644 (file)
index 0000000..f4be871
--- /dev/null
@@ -0,0 +1,2 @@
+xcuserdata
+*.xcworkspace
diff --git a/src/frontends/osx/README.md b/src/frontends/osx/README.md
new file mode 100644 (file)
index 0000000..bd24cce
--- /dev/null
@@ -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 (file)
index 0000000..e8ddd24
--- /dev/null
@@ -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 (file)
index 0000000..703fab9
--- /dev/null
@@ -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 (file)
index 0000000..a1f6411
--- /dev/null
@@ -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 (file)
index 0000000..9b52600
--- /dev/null
@@ -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 (file)
index 0000000..125a81f
--- /dev/null
@@ -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 (file)
index 0000000..f60bcbf
--- /dev/null
@@ -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 (file)
index 0000000..9f40e60
--- /dev/null
@@ -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 (file)
index 0000000..38c34e4
--- /dev/null
@@ -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 (file)
index 0000000..fd5ad37
--- /dev/null
@@ -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 (file)
index 0000000..2eb88b7
--- /dev/null
@@ -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 */;
+}
index 82e2c8e..4aeb1c0 100644 (file)
@@ -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;
 }
index bde5f71..82d2159 100644 (file)
@@ -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
index d181d8e..1003aa0 100644 (file)
@@ -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;
 }
-
index e38850d..520a86c 100644 (file)
@@ -34,6 +34,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.
  *
  * @param key                  first key (the one we are looking for)
@@ -43,6 +59,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.
  *
  * General purpose hash table. This hash table is not synchronized.
@@ -121,7 +155,6 @@ struct hashtable_t {
         * Destroys a hash table object.
         */
        void (*destroy) (hashtable_t *this);
-
 };
 
 /**
index f4cd9b9..fa25555 100644 (file)
@@ -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;
diff --git a/src/libstrongswan/plugins/keychain/Makefile.am b/src/libstrongswan/plugins/keychain/Makefile.am
new file mode 100644 (file)
index 0000000..508a4b0
--- /dev/null
@@ -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
diff --git a/src/libstrongswan/plugins/keychain/keychain_creds.c b/src/libstrongswan/plugins/keychain/keychain_creds.c
new file mode 100644 (file)
index 0000000..ddcc7a4
--- /dev/null
@@ -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;
+}
diff --git a/src/libstrongswan/plugins/keychain/keychain_creds.h b/src/libstrongswan/plugins/keychain/keychain_creds.h
new file mode 100644 (file)
index 0000000..64a2ede
--- /dev/null
@@ -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_ @}*/
diff --git a/src/libstrongswan/plugins/keychain/keychain_plugin.c b/src/libstrongswan/plugins/keychain/keychain_plugin.c
new file mode 100644 (file)
index 0000000..6112afa
--- /dev/null
@@ -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;
+}
diff --git a/src/libstrongswan/plugins/keychain/keychain_plugin.h b/src/libstrongswan/plugins/keychain/keychain_plugin.h
new file mode 100644 (file)
index 0000000..482f173
--- /dev/null
@@ -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_ @}*/
index c98e805..24b12d5 100644 (file)
@@ -679,6 +679,41 @@ static bool parse_keyUsage_ext(private_openssl_x509_t *this,
 }
 
 /**
+ * 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
  */
 static bool parse_crlDistributionPoints_ext(private_openssl_x509_t *this,
@@ -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;
                        }
@@ -991,38 +1034,6 @@ static bool parse_extensions(private_openssl_x509_t *this)
 }
 
 /**
- * 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
  */
 static bool parse_certificate(private_openssl_x509_t *this)
@@ -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))
index d6d98d1..eb167d6 100644 (file)
@@ -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;
 }
 
 /**