From 979727af1827a99dcf5016d815f9cd95f7d5687f Mon Sep 17 00:00:00 2001 From: Damian Rickard Date: Sun, 21 Jun 2026 15:28:43 -0400 Subject: [PATCH] macOS: native authentication via an SMJobBless privileged helper On macOS, VeraCrypt elevated privileges through the common Unix path: it prompted for the administrator password in its own dialog and started the core service via "sudo". This replaces that with a code-signed launchd privileged helper installed via SMJobBless, so the OS shows its standard authentication dialog and VeraCrypt never handles the administrator password. Closes #1437. Linux/FreeBSD are unchanged. Design (reuses the existing root entry point): - A thin, self-contained helper (PrivilegedHelper/Helper.cpp) exposes one privileged Mach service. On request it validates the connecting client's code signature (audit token -> SecCode -> designated requirement) and the client-supplied app binary, then socketpair()/fork()/execs " --core-service" as root and returns one socket end over XPC. - The returned descriptor is wrapped into the existing Service streams and fed the same {0,0x11,0x22} sync code, so ProcessElevatedRequests() / ProcessRequests() and all CoreServiceRequest serialization and validation are unchanged. - The SMJobBless install and XPC connection run in the main application process (CoreService::SendRequest), not in the unprivileged core service: the latter is a fork()ed child that never calls exec(), where the Authorization / XPC / ServiceManagement frameworks cannot run. Security: - SMJobBless install gate matches the app's SMPrivilegedExecutables against the helper signature and the helper's SMAuthorizedClients against the app. - Native auth via AuthorizationCopyRights(kSMRightBlessPrivilegedHelper). - Per-connection client code-signature check inside the helper. - The helper only execs a binary that is a root-owned regular file inside a non-user-writable ".app/Contents/MacOS" tree (every path component is verified root-owned and not group/other-writable, admin-group write allowed only outside the bundle), with the opened descriptor pinned and re-checked immediately before exec. macOS has no fexecve() and rejects exec via /dev/fd, so this trusted-location requirement is what closes the validate/exec race rather than executing the descriptor directly. - The elevated SetFileOwner and the APFS formatter now share one device-node validator (lstat-based; rejects symlinks; requires a block/char device). Build / packaging: - Link the app with Security/ServiceManagement/CoreFoundation; the Blocks/XPC client glue is isolated in PrivilegedHelperClient.mm. - Main.make builds the helper, embeds it at Contents/Library/LaunchServices, and signs it inside-out before the app. Signing identity and Team ID are build-overridable (defaults pin the IDRIX project identity); the Team ID is templated into the code-signing requirement strings and the generated plists are regenerated on every build so a Team ID change always takes effect. - notarize.sh signs/verifies the nested helper; a macOS uninstaller removes the helper and its LaunchDaemon. --- .gitignore | 4 + .../Resources/MacOSX/Helper-Info.plist.xml | 32 ++ .../Resources/MacOSX/Helper-Launchd.plist.xml | 16 + src/Build/Resources/MacOSX/Info.plist.xml | 10 + src/Core/Core.make | 1 + src/Core/Unix/CoreService.cpp | 85 +++- src/Core/Unix/MacOSX/PrivilegedHelperClient.h | 24 + .../Unix/MacOSX/PrivilegedHelperClient.mm | 160 ++++++ .../Unix/MacOSX/PrivilegedHelperProtocol.h | 39 ++ src/Main/Main.make | 16 +- src/Makefile | 10 +- src/PrivilegedHelper/Helper.cpp | 466 ++++++++++++++++++ src/PrivilegedHelper/PrivilegedHelper.make | 56 +++ .../MacOSX/VeraCryptHelper.entitlements.plist | 11 + src/Setup/MacOSX/notarize.sh | 16 + src/Setup/MacOSX/veracrypt-uninstall.sh | 32 ++ 16 files changed, 964 insertions(+), 14 deletions(-) create mode 100644 src/Build/Resources/MacOSX/Helper-Info.plist.xml create mode 100644 src/Build/Resources/MacOSX/Helper-Launchd.plist.xml create mode 100644 src/Core/Unix/MacOSX/PrivilegedHelperClient.h create mode 100644 src/Core/Unix/MacOSX/PrivilegedHelperClient.mm create mode 100644 src/Core/Unix/MacOSX/PrivilegedHelperProtocol.h create mode 100644 src/PrivilegedHelper/Helper.cpp create mode 100644 src/PrivilegedHelper/PrivilegedHelper.make create mode 100644 src/Setup/MacOSX/VeraCryptHelper.entitlements.plist create mode 100755 src/Setup/MacOSX/veracrypt-uninstall.sh diff --git a/.gitignore b/.gitignore index 11201ffb81..b6ec782f74 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,10 @@ src/Main/VeraCrypt src/Main/VeraCrypt.app src/Main/*.dmg src/Setup/MacOSX/*.pkg +# macOS privileged helper build artifacts (binary + plists generated from templates) +src/PrivilegedHelper/org.idrix.VeraCrypt.helper +src/PrivilegedHelper/Helper-Info.plist +src/PrivilegedHelper/Helper-Launchd.plist *.oo *.o.32 *.o.64 diff --git a/src/Build/Resources/MacOSX/Helper-Info.plist.xml b/src/Build/Resources/MacOSX/Helper-Info.plist.xml new file mode 100644 index 0000000000..bd5d1e4b61 --- /dev/null +++ b/src/Build/Resources/MacOSX/Helper-Info.plist.xml @@ -0,0 +1,32 @@ + + + + + CFBundleInfoDictionaryVersion + 6.0 + + CFBundleIdentifier + org.idrix.VeraCrypt.helper + + CFBundleName + VeraCrypt Privileged Helper + + CFBundlePackageType + APPL + + CFBundleVersion + _VERSION_ + + CFBundleShortVersionString + _VERSION_ + + + SMAuthorizedClients + + identifier "org.idrix.VeraCrypt" and anchor apple generic and certificate leaf[subject.OU] = "_TEAMID_" + + + diff --git a/src/Build/Resources/MacOSX/Helper-Launchd.plist.xml b/src/Build/Resources/MacOSX/Helper-Launchd.plist.xml new file mode 100644 index 0000000000..df446d9b76 --- /dev/null +++ b/src/Build/Resources/MacOSX/Helper-Launchd.plist.xml @@ -0,0 +1,16 @@ + + + + + Label + org.idrix.VeraCrypt.helper + + + MachServices + + org.idrix.VeraCrypt.helper + + + + diff --git a/src/Build/Resources/MacOSX/Info.plist.xml b/src/Build/Resources/MacOSX/Info.plist.xml index 04ed21c1c2..ad48098e8c 100644 --- a/src/Build/Resources/MacOSX/Info.plist.xml +++ b/src/Build/Resources/MacOSX/Info.plist.xml @@ -96,5 +96,15 @@ NSPrincipalClass NSApplication + + + SMPrivilegedExecutables + + org.idrix.VeraCrypt.helper + identifier "org.idrix.VeraCrypt.helper" and anchor apple generic and certificate leaf[subject.OU] = "_TEAMID_" + diff --git a/src/Core/Core.make b/src/Core/Core.make index def349d812..3dd2065471 100644 --- a/src/Core/Core.make +++ b/src/Core/Core.make @@ -26,6 +26,7 @@ OBJS += Unix/$(PLATFORM)/Core$(PLATFORM).o OBJS += Unix/$(PLATFORM)/Core$(PLATFORM).o ifeq "$(PLATFORM)" "MacOSX" OBJS += Unix/FreeBSD/CoreFreeBSD.o +OBJS += Unix/MacOSX/PrivilegedHelperClient.o endif include $(BUILD_INC)/Makefile.inc diff --git a/src/Core/Unix/CoreService.cpp b/src/Core/Unix/CoreService.cpp index 014b5c8f0f..1f6a33af75 100644 --- a/src/Core/Unix/CoreService.cpp +++ b/src/Core/Unix/CoreService.cpp @@ -15,6 +15,9 @@ #include #include #include +#ifdef TC_MACOSX +#include "Core/Unix/MacOSX/PrivilegedHelperClient.h" +#endif #include "Platform/FileStream.h" #include "Platform/MemoryStream.h" #include "Platform/Serializable.h" @@ -61,13 +64,12 @@ namespace VeraCrypt || IsMacOSXDevicePathWithPrefix (path, "/dev/rdisk"); } - // The elevated service runs as root, so it must not be tricked into changing - // ownership of an arbitrary path. Every legitimate macOS caller of the - // elevated SetFileOwner targets a real disk device node (/dev/[r]diskN[sM]), - // so restrict the operation to that. lstat() (not stat) is used so a symlink - // is rejected outright rather than followed, and the st_mode check confirms an - // actual block/character device before the chown. - static void ValidateMacOSXSetFileOwnerTarget (const FilesystemPath &path) + // The elevated service runs as root, so it must not be tricked into operating + // on arbitrary paths. Every legitimate macOS caller that reaches these helpers + // targets a real disk device node (/dev/[r]diskN[sM]), so restrict privileged + // operations to that. lstat() (not stat) rejects symlinks outright, and the + // st_mode check confirms an actual block/character device before use. + static void ValidateMacOSXDeviceNodeTarget (const FilesystemPath &path) { const string pathStr = path; @@ -84,8 +86,7 @@ namespace VeraCrypt static list BuildMacOSXAPFSFormatterArguments (const ExecuteMacOSXAPFSFormatterRequest &request) { - if (!IsMacOSXFormatterDevicePath (request.Device)) - throw ParameterIncorrect (SRC_POS); + ValidateMacOSXDeviceNodeTarget (request.Device); if (request.OwnerUserId > static_cast ((uid_t) -1) || request.OwnerGroupId > static_cast ((gid_t) -1)) @@ -330,7 +331,7 @@ namespace VeraCrypt #ifdef TC_MACOSX // Restrict the root-privileged chown to real disk device nodes. - ValidateMacOSXSetFileOwnerTarget (setFileOwnerRequest->Path); + ValidateMacOSXDeviceNodeTarget (setFileOwnerRequest->Path); #endif coreUnix->SetFileOwner (setFileOwnerRequest->Path, setFileOwnerRequest->Owner); SetFileOwnerResponse().Serialize (outputStream); @@ -449,6 +450,21 @@ namespace VeraCrypt { // Test if the user has an active "sudo" session. bool authCheckDone = false; +#ifdef TC_MACOSX + // macOS: establish the privileged channel here, in the main + // application process. StartElevated() uses the SMJobBless helper + // and XPC, which cannot run in the unprivileged core service (a + // fork()ed child that never calls exec()); delegating elevation to + // it fails before the native authentication dialog is even shown. + // StartElevated() repoints the Service streams at the root core + // service, so the request is then sent below like any other. + authCheckDone = true; + request.FastElevation = false; + StartElevated (request); + ElevatedServiceAvailable = true; + request.Serialize (ServiceInputStream); + return GetResponse (); +#else if (!Core->GetUseDummySudoPassword ()) { // We are using -n to avoid prompting the user for a password. @@ -493,6 +509,7 @@ namespace VeraCrypt request.FastElevation = false; } } +#endif try { @@ -527,6 +544,9 @@ namespace VeraCrypt request.FastElevation = false; +#ifdef TC_MACOSX + throw; +#endif if(!authCheckDone) (*AdminPasswordCallback) (request.AdminPassword); } @@ -562,6 +582,51 @@ namespace VeraCrypt void CoreService::StartElevated (const CoreServiceRequest &request) { +#ifdef TC_MACOSX + try + { + std::string errorMsg; + string appPath = request.ApplicationExecutablePath; + if (appPath.empty() || appPath[0] != '/') + { + appPath = Process::FindSystemBinary("VeraCrypt", errorMsg); + if (appPath.empty()) + throw SystemException(SRC_POS, errorMsg); + } + + // Install (if needed) and drive the SMJobBless privileged helper. + // The helper shows the native macOS authentication dialog at install + // time, validates this app's code signature on every connection, and + // spawns " --core-service" as root, returning a connected + // socket. VeraCrypt never handles the administrator password. + int serviceFD = MacOSXConnectElevatedCoreService (appPath); + throw_sys_if (serviceFD == -1); + + shared_ptr servicePipe (new File()); + servicePipe->AssignSystemHandle (serviceFD, false); + ServiceInputStream = shared_ptr (new FileStream (servicePipe)); + ServiceOutputStream = shared_ptr (new FileStream (servicePipe)); + + // Send sync code + uint8 sync[] = { 0, 0x11, 0x22 }; + ServiceInputStream->Write (ConstBufferPtr (sync, array_capacity (sync))); + + return; + } + catch (Exception &) + { + throw; + } + catch (exception &e) + { + throw ExternalException (SRC_POS, StringConverter::ToExceptionString (e)); + } + catch (...) + { + throw UnknownException (SRC_POS); + } +#endif + unique_ptr inPipe (new Pipe()); unique_ptr outPipe (new Pipe()); Pipe errPipe; diff --git a/src/Core/Unix/MacOSX/PrivilegedHelperClient.h b/src/Core/Unix/MacOSX/PrivilegedHelperClient.h new file mode 100644 index 0000000000..79548b1bb7 --- /dev/null +++ b/src/Core/Unix/MacOSX/PrivilegedHelperClient.h @@ -0,0 +1,24 @@ +/* + Copyright (c) 2026 AM Crypto and are governed by the Apache License 2.0 + the full text of which is contained in the file License.txt included in + VeraCrypt binary and source code distribution packages. +*/ + +#ifndef TC_HEADER_Core_Unix_MacOSX_PrivilegedHelperClient +#define TC_HEADER_Core_Unix_MacOSX_PrivilegedHelperClient + +#include + +namespace VeraCrypt +{ + // Ensures the SMJobBless privileged helper is installed and up to date + // (showing the native macOS authentication dialog when an install/upgrade + // is required), then asks it to spawn "appPath --core-service" as root and + // returns a connected socket file descriptor to that root process. The + // returned descriptor is owned by the caller and must be closed. + // + // Throws a VeraCrypt exception (e.g. ElevationFailed) on any failure. + int MacOSXConnectElevatedCoreService (const std::string &appPath); +} + +#endif // TC_HEADER_Core_Unix_MacOSX_PrivilegedHelperClient diff --git a/src/Core/Unix/MacOSX/PrivilegedHelperClient.mm b/src/Core/Unix/MacOSX/PrivilegedHelperClient.mm new file mode 100644 index 0000000000..6c1dde1230 --- /dev/null +++ b/src/Core/Unix/MacOSX/PrivilegedHelperClient.mm @@ -0,0 +1,160 @@ +/* + Copyright (c) 2026 AM Crypto and are governed by the Apache License 2.0 + the full text of which is contained in the file License.txt included in + VeraCrypt binary and source code distribution packages. +*/ + +// Client-side glue for the VeraCrypt SMJobBless privileged helper. +// +// This translation unit is compiled as Objective-C++ (".mm") so that it can use +// the Blocks-based XPC and ServiceManagement APIs (the rest of CoreService.cpp +// is plain C++ and is not compiled with -fblocks). It exposes a single plain +// C++ entry point, MacOSXConnectElevatedCoreService(), declared in +// PrivilegedHelperClient.h and called from CoreService::StartElevated(). + +#include "PrivilegedHelperClient.h" +#include "PrivilegedHelperProtocol.h" + +// The Apple framework headers must come first: they typedef BOOL (objc.h), +// which must be seen before Common/Tcdefs.h redefines BOOL as a macro. They +// also pull in , whose ERR_SUCCESS macro would otherwise mangle +// the ERR_SUCCESS enumerator in Tcdefs.h, so it is undefined before the +// VeraCrypt headers (reached via SystemException.h) are included. +#include +#include +#include +#include + +#undef ERR_SUCCESS + +#include "Platform/SystemException.h" +#include "Core/CoreException.h" + +namespace VeraCrypt +{ + // Connects to the helper's privileged Mach service. The connection is + // returned resumed; the caller owns it and must xpc_connection_cancel() it. + static xpc_connection_t ConnectToHelper () + { + xpc_connection_t connection = xpc_connection_create_mach_service ( + VC_HELPER_LABEL, NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); + + if (!connection) + throw ElevationFailed (SRC_POS, VC_HELPER_LABEL, 1, "xpc_connection_create_mach_service failed"); + + // A no-op event handler is mandatory; per-request errors are surfaced + // synchronously through the reply objects below. + xpc_connection_set_event_handler (connection, ^(xpc_object_t) { }); + xpc_connection_resume (connection); + return connection; + } + + // Returns the version reported by the currently installed helper, or -1 if + // no helper is installed / reachable. + static int64_t QueryHelperVersion () + { + xpc_connection_t connection = ConnectToHelper (); + + xpc_object_t message = xpc_dictionary_create (NULL, NULL, 0); + xpc_dictionary_set_string (message, VC_HELPER_KEY_COMMAND, VC_HELPER_CMD_GET_VERSION); + + xpc_object_t reply = xpc_connection_send_message_with_reply_sync (connection, message); + + int64_t version = -1; + if (xpc_get_type (reply) == XPC_TYPE_DICTIONARY) + version = xpc_dictionary_get_int64 (reply, VC_HELPER_KEY_VERSION); + + xpc_connection_cancel (connection); + return version; + } + + // Installs (or upgrades) the helper via SMJobBless. This is the call that + // triggers the native macOS authentication dialog. + static void BlessHelper () + { + AuthorizationRef authRef = NULL; + OSStatus status = AuthorizationCreate (NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authRef); + if (status != errAuthorizationSuccess) + throw ElevationFailed (SRC_POS, "AuthorizationCreate", status, ""); + + AuthorizationItem item = { kSMRightBlessPrivilegedHelper, 0, NULL, 0 }; + AuthorizationRights rights = { 1, &item }; + AuthorizationFlags flags = kAuthorizationFlagDefaults + | kAuthorizationFlagInteractionAllowed + | kAuthorizationFlagPreAuthorize + | kAuthorizationFlagExtendRights; + + status = AuthorizationCopyRights (authRef, &rights, kAuthorizationEmptyEnvironment, flags, NULL); + if (status != errAuthorizationSuccess) + { + AuthorizationFree (authRef, kAuthorizationFlagDefaults); + // errAuthorizationCanceled when the user dismisses the dialog. + throw ElevationFailed (SRC_POS, "AuthorizationCopyRights", status, ""); + } + + CFErrorRef cfError = NULL; + Boolean blessed = SMJobBless (kSMDomainSystemLaunchd, CFSTR (VC_HELPER_LABEL), authRef, &cfError); + AuthorizationFree (authRef, kAuthorizationFlagDefaults); + + if (!blessed) + { + long code = cfError ? (long) CFErrorGetCode (cfError) : 0; + string description = "SMJobBless failed"; + if (cfError) + { + CFStringRef desc = CFErrorCopyDescription (cfError); + if (desc) + { + char buffer[512]; + if (CFStringGetCString (desc, buffer, sizeof (buffer), kCFStringEncodingUTF8)) + description = buffer; + CFRelease (desc); + } + CFRelease (cfError); + } + throw ElevationFailed (SRC_POS, "SMJobBless", (int) code, description); + } + } + + // Installs the helper if it is missing or its version does not match the + // version this build expects. + static void EnsureHelperInstalled () + { + if (QueryHelperVersion () == VC_HELPER_VERSION) + return; + + BlessHelper (); + } + + int MacOSXConnectElevatedCoreService (const std::string &appPath) + { + EnsureHelperInstalled (); + + xpc_connection_t connection = ConnectToHelper (); + + xpc_object_t message = xpc_dictionary_create (NULL, NULL, 0); + xpc_dictionary_set_string (message, VC_HELPER_KEY_COMMAND, VC_HELPER_CMD_OPEN_CORE_SERVICE); + xpc_dictionary_set_string (message, VC_HELPER_KEY_APP_PATH, appPath.c_str()); + xpc_dictionary_set_int64 (message, VC_HELPER_KEY_VERSION, VC_HELPER_VERSION); + + xpc_object_t reply = xpc_connection_send_message_with_reply_sync (connection, message); + + if (xpc_get_type (reply) != XPC_TYPE_DICTIONARY) + { + xpc_connection_cancel (connection); + throw ElevationFailed (SRC_POS, VC_HELPER_LABEL, 1, "Privileged helper did not return a valid reply"); + } + + int serviceFD = xpc_dictionary_dup_fd (reply, VC_HELPER_KEY_SERVICE_FD); + if (serviceFD < 0) + { + const char *helperError = xpc_dictionary_get_string (reply, VC_HELPER_KEY_ERROR); + string description = helperError ? helperError : "Privileged helper refused to open the core service"; + xpc_connection_cancel (connection); + throw ElevationFailed (SRC_POS, VC_HELPER_LABEL, 1, description); + } + + xpc_connection_cancel (connection); + return serviceFD; + } +} diff --git a/src/Core/Unix/MacOSX/PrivilegedHelperProtocol.h b/src/Core/Unix/MacOSX/PrivilegedHelperProtocol.h new file mode 100644 index 0000000000..ed7edbd130 --- /dev/null +++ b/src/Core/Unix/MacOSX/PrivilegedHelperProtocol.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2026 AM Crypto and are governed by the Apache License 2.0 + the full text of which is contained in the file License.txt included in + VeraCrypt binary and source code distribution packages. +*/ + +#ifndef TC_HEADER_Core_Unix_MacOSX_PrivilegedHelperProtocol +#define TC_HEADER_Core_Unix_MacOSX_PrivilegedHelperProtocol + +// Shared constants for the VeraCrypt privileged helper (SMJobBless / launchd). +// Included by both the helper (PrivilegedHelper/Helper.cpp) and the client glue +// (Core/Unix/MacOSX/PrivilegedHelperClient.mm). + +// launchd Label and Mach service name. Must match Helper-Launchd.plist.xml, +// Helper-Info.plist.xml (CFBundleIdentifier) and the SMPrivilegedExecutables +// key of the application's Info.plist. +#define VC_HELPER_LABEL "org.idrix.VeraCrypt.helper" + +// Absolute path where SMJobBless installs the helper tool and its launchd job. +#define VC_HELPER_TOOL_PATH "/Library/PrivilegedHelperTools/" VC_HELPER_LABEL +#define VC_HELPER_PLIST_PATH "/Library/LaunchDaemons/" VC_HELPER_LABEL ".plist" + +// XPC message keys. +#define VC_HELPER_KEY_COMMAND "command" +#define VC_HELPER_KEY_APP_PATH "app-path" +#define VC_HELPER_KEY_VERSION "version" +#define VC_HELPER_KEY_SERVICE_FD "service-fd" +#define VC_HELPER_KEY_ERROR "error" + +// XPC commands (value of VC_HELPER_KEY_COMMAND). +#define VC_HELPER_CMD_OPEN_CORE_SERVICE "open-core-service" +#define VC_HELPER_CMD_GET_VERSION "get-version" +#define VC_HELPER_CMD_UNINSTALL "uninstall" + +// Protocol/helper version. Bump whenever Helper.cpp changes so that an outdated +// installed helper is detected by the client and re-blessed (standard pattern). +#define VC_HELPER_VERSION 1 + +#endif // TC_HEADER_Core_Unix_MacOSX_PrivilegedHelperProtocol diff --git a/src/Main/Main.make b/src/Main/Main.make index 36b8c1f6e9..a59ef30fc7 100755 --- a/src/Main/Main.make +++ b/src/Main/Main.make @@ -253,13 +253,23 @@ endif echo -n APPLTRUE >$(APPNAME).app/Contents/PkgInfo ifdef VC_LEGACY_BUILD - sed -e 's/_VERSION_/$(patsubst %a,%.1,$(patsubst %b,%.2,$(TC_VERSION)))/' ../Build/Resources/MacOSX/Info.plist.legacy.xml >$(APPNAME).app/Contents/Info.plist + sed -e 's/_VERSION_/$(patsubst %a,%.1,$(patsubst %b,%.2,$(TC_VERSION)))/' -e 's/_TEAMID_/$(VC_OSX_TEAM_ID)/g' ../Build/Resources/MacOSX/Info.plist.legacy.xml >$(APPNAME).app/Contents/Info.plist else - sed -e 's/_VERSION_/$(patsubst %a,%.1,$(patsubst %b,%.2,$(TC_VERSION)))/' ../Build/Resources/MacOSX/Info.plist.xml >$(APPNAME).app/Contents/Info.plist + sed -e 's/_VERSION_/$(patsubst %a,%.1,$(patsubst %b,%.2,$(TC_VERSION)))/' -e 's/_TEAMID_/$(VC_OSX_TEAM_ID)/g' ../Build/Resources/MacOSX/Info.plist.xml >$(APPNAME).app/Contents/Info.plist endif + + # Build the SMJobBless privileged helper and embed it in the bundle at the + # location SMJobBless expects (Contents/Library/LaunchServices/