1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MacLaunchHelper.h"
8 #include "MacAutoreleasePool.h"
9 #include "mozilla/UniquePtr.h"
12 #include <Cocoa/Cocoa.h>
13 #include <crt_externs.h>
14 #include <ServiceManagement/ServiceManagement.h>
15 #include <Security/Authorization.h>
19 using namespace mozilla;
21 void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid) {
22 MacAutoreleasePool pool;
25 NSString* launchPath = [NSString stringWithUTF8String:aArgv[0]];
26 NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:aArgc - 1];
27 for (int i = 1; i < aArgc; i++) {
28 [arguments addObject:[NSString stringWithUTF8String:aArgv[i]]];
30 NSTask* child = [NSTask launchedTaskWithLaunchPath:launchPath arguments:arguments];
32 *aPid = [child processIdentifier];
33 // We used to use waitpid to wait for the process to terminate. This is
34 // incompatible with NSTask and we wait for the process to exit here
36 [child waitUntilExit];
38 } @catch (NSException* e) {
39 NSLog(@"%@: %@", e.name, e.reason);
43 BOOL InstallPrivilegedHelper() {
44 AuthorizationRef authRef = NULL;
45 OSStatus status = AuthorizationCreate(
46 NULL, kAuthorizationEmptyEnvironment,
47 kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed, &authRef);
48 if (status != errAuthorizationSuccess) {
49 // AuthorizationCreate really shouldn't fail.
50 NSLog(@"AuthorizationCreate failed! NSOSStatusErrorDomain / %d", (int)status);
55 AuthorizationItem authItem = {kSMRightBlessPrivilegedHelper, 0, NULL, 0};
56 AuthorizationRights authRights = {1, &authItem};
57 AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed |
58 kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
60 // Obtain the right to install our privileged helper tool.
62 AuthorizationCopyRights(authRef, &authRights, kAuthorizationEmptyEnvironment, flags, NULL);
63 if (status != errAuthorizationSuccess) {
64 NSLog(@"AuthorizationCopyRights failed! NSOSStatusErrorDomain / %d", (int)status);
67 // This does all the work of verifying the helper tool against the
68 // application and vice-versa. Once verification has passed, the embedded
69 // launchd.plist is extracted and placed in /Library/LaunchDaemons and then
70 // loaded. The executable is placed in /Library/PrivilegedHelperTools.
71 result = (BOOL)SMJobBless(kSMDomainSystemLaunchd, (CFStringRef) @"org.mozilla.updater", authRef,
74 NSLog(@"Unable to install helper!");
82 void AbortElevatedUpdate() {
83 mozilla::MacAutoreleasePool pool;
85 id updateServer = nil;
87 const int numRetries = 10; // Number of IPC connection retries before
89 while (currTry < numRetries) {
91 updateServer = (id)[NSConnection
92 rootProxyForConnectionWithRegisteredName:@"org.mozilla.updater.server"
94 usingNameServer:[NSSocketPortNameServer sharedInstance]];
95 if (updateServer && [updateServer respondsToSelector:@selector(abort)]) {
96 [updateServer performSelector:@selector(abort)];
99 NSLog(@"Server doesn't exist or doesn't provide correct selectors.");
100 sleep(1); // Wait 1 second.
102 } @catch (NSException* e) {
103 NSLog(@"Encountered exception, retrying: %@: %@", e.name, e.reason);
104 sleep(1); // Wait 1 second.
108 NSLog(@"Unable to clean up updater.");
111 bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid) {
112 LaunchChildMac(aArgc, aArgv, aPid);
113 bool didSucceed = InstallPrivilegedHelper();
115 AbortElevatedUpdate();