diff --git a/src/frontends/osx/.gitignore b/src/frontends/osx/.gitignore new file mode 100644 index 000000000..f4be87183 --- /dev/null +++ b/src/frontends/osx/.gitignore @@ -0,0 +1,2 @@ +xcuserdata +*.xcworkspace diff --git a/src/frontends/osx/README.md b/src/frontends/osx/README.md new file mode 100644 index 000000000..69ee460a1 --- /dev/null +++ b/src/frontends/osx/README.md @@ -0,0 +1,40 @@ +# 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" \ +CFLAGS="-I/usr/include -DOPENSSL_NO_CMS -O2 -Wall -Wno-format -Wno-pointer-sign" \ +./configure --prefix=/opt/local --disable-defaults --enable-openssl \ + --enable-kernel-pfkey --enable-kernel-pfroute --enable-eap-mschapv2 \ + --enable-eap-identity --enable-monolithic --enable-nonce --enable-random \ + --enable-pkcs1 --enable-pem --enable-socket-default --enable-xauth-generic \ + --enable-ikev1 --enable-ikev2 --enable-charon --disable-shared --enable-static + +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. \ No newline at end of file diff --git a/src/frontends/osx/charon-xpc/charon-xpc-Info.plist b/src/frontends/osx/charon-xpc/charon-xpc-Info.plist new file mode 100644 index 000000000..e8ddd24b0 --- /dev/null +++ b/src/frontends/osx/charon-xpc/charon-xpc-Info.plist @@ -0,0 +1,18 @@ + + + + + CFBundleIdentifier + org.strongswan.charon-xpc + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + charon-xpc + CFBundleVersion + 1.0 + SMAuthorizedClients + + identifier org.strongswan.osx and certificate leaf[subject.CN] = "Joe Developer" + + + diff --git a/src/frontends/osx/charon-xpc/charon-xpc-Launchd.plist b/src/frontends/osx/charon-xpc/charon-xpc-Launchd.plist new file mode 100644 index 000000000..703fab912 --- /dev/null +++ b/src/frontends/osx/charon-xpc/charon-xpc-Launchd.plist @@ -0,0 +1,13 @@ + + + + + Label + org.strongswan.charon-xpc + MachServices + + org.strongswan.charon-xpc + + + + diff --git a/src/frontends/osx/charon-xpc/charon-xpc.c b/src/frontends/osx/charon-xpc/charon-xpc.c new file mode 100644 index 000000000..19142d894 --- /dev/null +++ b/src/frontends/osx/charon-xpc/charon-xpc.c @@ -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 . + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +/** + * 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); + } +} + +/** + * Return version of this helper + */ +xpc_object_t get_version(xpc_object_t request, xpc_connection_t client) +{ + xpc_object_t reply; + + reply = xpc_dictionary_create_reply(request); + xpc_dictionary_set_string(reply, "version", PACKAGE_VERSION); + + return reply; +} + +/** + * XPC command dispatch table + */ +struct { + char *name; + xpc_object_t (*handler)(xpc_object_t request, xpc_connection_t client); +} commands[] = { + { "get_version", get_version }, +}; + +/** + * Handle a received XPC request message + */ +static void handle(xpc_object_t request) +{ + xpc_connection_t client; + xpc_object_t reply; + const char *command; + int i; + + client = xpc_dictionary_get_remote_connection(request); + command = xpc_dictionary_get_string(request, "command"); + if (command) + { + for (i = 0; i < countof(commands); i++) + { + if (streq(commands[i].name, command)) + { + reply = commands[i].handler(request, client); + if (reply) + { + xpc_connection_send_message(client, reply); + xpc_release(reply); + } + break; + } + } + } +} + +/** + * Dispatch XPC commands + */ +static int dispatch() +{ + xpc_connection_t service; + + service = xpc_connection_create_mach_service("org.strongswan.charon-xpc", + NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER); + if (!service) + { + return EXIT_FAILURE; + } + + xpc_connection_set_event_handler(service, ^(xpc_object_t conn) { + + xpc_connection_set_event_handler(conn, ^(xpc_object_t event) { + + if (xpc_get_type(event) == XPC_TYPE_ERROR) + { + if (event == XPC_ERROR_CONNECTION_INVALID || + event == XPC_ERROR_TERMINATION_IMMINENT) + { + xpc_connection_cancel(conn); + } + } + else + { + handle(event); + } + }); + xpc_connection_resume(conn); + }); + + xpc_connection_resume(service); + + dispatch_main(); + + xpc_release(service); +} + +/** + * Main function, starts the daemon. + */ +int main(int argc, char *argv[]) +{ + 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"); + if (!charon->initialize(charon, + lib->settings->get_str(lib->settings, "charon-xpc.load", + "random nonce pem pkcs1 openssl kernel-pfkey kernel-pfroute " + "socket-default eap-identity eap-mschapv2"))) + { + 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); + + charon->start(charon); + return dispatch(); +} diff --git a/src/frontends/osx/strongSwan.xcodeproj/project.pbxproj b/src/frontends/osx/strongSwan.xcodeproj/project.pbxproj new file mode 100644 index 000000000..eedc804e5 --- /dev/null +++ b/src/frontends/osx/strongSwan.xcodeproj/project.pbxproj @@ -0,0 +1,308 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 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 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 5BD1CCD11726DB4000587077 /* org.strongswan.charon-xpc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.objfile"; includeInIndex = 0; path = "org.strongswan.charon-xpc"; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + 5BD1CCE01726DCD000587077 /* charon-xpc-Launchd.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "charon-xpc-Launchd.plist"; sourceTree = ""; }; + 5BD1CCE11726DD9900587077 /* charon-xpc-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "charon-xpc-Info.plist"; sourceTree = ""; }; + 5BD1CCEA1727CCA400587077 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = ""; }; + 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; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5BD1CCCE1726DB4000587077 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 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 = ""; + }; + 5BD1CCAD1726DB0100587077 /* Products */ = { + isa = PBXGroup; + children = ( + 5BD1CCD11726DB4000587077 /* org.strongswan.charon-xpc */, + ); + name = Products; + sourceTree = ""; + }; + 5BD1CCAF1726DB0100587077 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5BD1CCF21727DE3E00587077 /* Security.framework */, + 5BD1CCEC1727D7AF00587077 /* ServiceManagement.framework */, + 5BD1CCD31726DB4000587077 /* CoreFoundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5BD1CCD51726DB4000587077 /* charon-xpc */ = { + isa = PBXGroup; + children = ( + 5BD1CCD61726DB4000587077 /* charon-xpc.c */, + 5BD1CCE01726DCD000587077 /* charon-xpc-Launchd.plist */, + 5BD1CCE11726DD9900587077 /* charon-xpc-Info.plist */, + ); + path = "charon-xpc"; + sourceTree = ""; + }; +/* 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 */, + ); + 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; + 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, + ); + 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; + 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, + ); + 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 */; +}