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"
11 #include <Cocoa/Cocoa.h>
12 #include <crt_externs.h>
13 #include <ServiceManagement/ServiceManagement.h>
14 #include <Security/Authorization.h>
18 using namespace mozilla;
20 void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid) {
21 MacAutoreleasePool pool;
24 NSString* launchPath = [NSString stringWithUTF8String:aArgv[0]];
25 NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:aArgc - 1];
26 for (int i = 1; i < aArgc; i++) {
27 [arguments addObject:[NSString stringWithUTF8String:aArgv[i]]];
29 NSTask* child = [NSTask launchedTaskWithLaunchPath:launchPath
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,
49 if (status != errAuthorizationSuccess) {
50 // AuthorizationCreate really shouldn't fail.
51 NSLog(@"AuthorizationCreate failed! NSOSStatusErrorDomain / %d",
57 AuthorizationItem authItem = {kSMRightBlessPrivilegedHelper, 0, NULL, 0};
58 AuthorizationRights authRights = {1, &authItem};
59 AuthorizationFlags flags =
60 kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed |
61 kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
63 // Obtain the right to install our privileged helper tool.
64 status = AuthorizationCopyRights(authRef, &authRights,
65 kAuthorizationEmptyEnvironment, flags, NULL);
66 if (status != errAuthorizationSuccess) {
67 NSLog(@"AuthorizationCopyRights failed! NSOSStatusErrorDomain / %d",
71 // This does all the work of verifying the helper tool against the
72 // application and vice-versa. Once verification has passed, the embedded
73 // launchd.plist is extracted and placed in /Library/LaunchDaemons and then
74 // loaded. The executable is placed in /Library/PrivilegedHelperTools.
75 result = (BOOL)SMJobBless(kSMDomainSystemLaunchd,
76 (CFStringRef) @"org.mozilla.updater", authRef,
79 NSLog(@"Unable to install helper!");
87 void AbortElevatedUpdate() {
88 mozilla::MacAutoreleasePool pool;
90 id updateServer = nil;
92 const int numRetries = 10; // Number of IPC connection retries before
94 while (currTry < numRetries) {
96 updateServer = (id)[NSConnection
97 rootProxyForConnectionWithRegisteredName:@"org.mozilla.updater.server"
99 usingNameServer:[NSSocketPortNameServer
101 if (updateServer && [updateServer respondsToSelector:@selector(abort)]) {
102 [updateServer performSelector:@selector(abort)];
105 NSLog(@"Server doesn't exist or doesn't provide correct selectors.");
106 sleep(1); // Wait 1 second.
108 } @catch (NSException* e) {
109 NSLog(@"Encountered exception, retrying: %@: %@", e.name, e.reason);
110 sleep(1); // Wait 1 second.
114 NSLog(@"Unable to clean up updater.");
117 bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid) {
118 LaunchChildMac(aArgc, aArgv, aPid);
119 bool didSucceed = InstallPrivilegedHelper();
121 AbortElevatedUpdate();