Index: chrome/installer/mac/app/AuthorizedInstall.m |
diff --git a/chrome/installer/mac/app/AuthorizedInstall.m b/chrome/installer/mac/app/AuthorizedInstall.m |
new file mode 100644 |
index 0000000000000000000000000000000000000000..07dde8a3ed86185d0311e1d93f2c2b257555a67e |
--- /dev/null |
+++ b/chrome/installer/mac/app/AuthorizedInstall.m |
@@ -0,0 +1,133 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#import "AuthorizedInstall.h" |
+ |
+@interface AuthorizedInstall () { |
+ NSFileHandle* communicationFile_; |
+ NSString* destinationAppBundlePath_; |
+} |
+@end |
+ |
+@implementation AuthorizedInstall |
+// Does the setup needed to authorize a tool to run as admin. |
+- (OSStatus)setUpAuthorization:(AuthorizationRef*)authRef { |
+ OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, |
+ kAuthorizationFlagDefaults, authRef); |
+ |
+ AuthorizationItem items = {kAuthorizationRightExecute, 0, NULL, 0}; |
+ AuthorizationRights rights = {1, &items}; |
+ AuthorizationFlags flags = |
+ kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | |
+ kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights; |
+ |
+ status = AuthorizationCopyRights(*authRef, &rights, NULL, flags, NULL); |
+ return status; |
+} |
+ |
+// Starts up the proccess with privileged permissions. |
+- (void)startPrivilegedTool:(const char*)toolPath |
+ withArguments:(const char**)args |
+ authorization:(AuthorizationRef)authRef |
+ status:(OSStatus)status { |
+ if (status != errAuthorizationSuccess) |
+ return; |
+ |
+ FILE* file; |
+#pragma clang diagnostic push |
+#pragma clang diagnostic ignored "-Wdeprecated-declarations" |
+ status = AuthorizationExecuteWithPrivileges( |
+ authRef, toolPath, kAuthorizationFlagDefaults, (char* const*)args, &file); |
+#pragma clang diagnostic pop |
+ communicationFile_ = [[NSFileHandle alloc] initWithFileDescriptor:fileno(file) |
+ closeOnDealloc:YES]; |
+} |
+ |
+// Starts up same proccess as above without privileged permissions. |
+- (void)startUnprivilegedTool:(NSString*)toolPath withArguments:(NSArray*)args { |
+ NSPipe* pipe = [NSPipe pipe]; |
+ NSTask* task = [[NSTask alloc] init]; |
+ [task setArguments:args]; |
+ [task setLaunchPath:toolPath]; |
+ [task setStandardInput:pipe]; |
+ [task launch]; |
+ communicationFile_ = [pipe fileHandleForWriting]; |
+} |
+ |
+// Determines which "Applications" folder to use based on authorization. |
+// There are three possible scenarios and two possible return values. |
+// 1) /Applications is returned if: |
+// a) The user authenticates the app. |
+// b) The user doesn't authenticate but is an admin. |
+// 2) $HOME/Applications is returned if: |
+// c) The user doesn't authenticate and is not an admin. |
+- (NSString*)getApplicationsFolder:(BOOL)isAuthorized { |
+ NSFileManager* manager = [NSFileManager defaultManager]; |
+ NSArray* applicationDirectories = NSSearchPathForDirectoriesInDomains( |
+ NSApplicationDirectory, NSLocalDomainMask, YES); |
+ if (isAuthorized || |
+ [manager isWritableFileAtPath:applicationDirectories.firstObject]) { |
+ return applicationDirectories.firstObject; |
+ } else { |
+ NSString* usersApplicationsDirectory = |
+ [NSString pathWithComponents:@[ NSHomeDirectory(), @"Applications" ]]; |
+ if (![manager fileExistsAtPath:usersApplicationsDirectory]) { |
+ [manager createDirectoryAtPath:usersApplicationsDirectory |
+ withIntermediateDirectories:NO |
+ attributes:nil |
+ error:nil]; |
+ } |
+ return usersApplicationsDirectory; |
+ } |
+} |
+ |
+// Attempts to gain authorization to run installation tool with elevated |
+// permissions. |
+// Then starts the tool with the appropiate paths for the tools elevation |
+// status. |
+- (BOOL)loadInstallationTool { |
+ AuthorizationRef authRef = NULL; |
+ OSStatus status = [self setUpAuthorization:&authRef]; |
+ BOOL isAuthorized = (status == errAuthorizationSuccess); |
+ |
+ NSString* toolPath = |
+ [[NSBundle mainBundle] pathForResource:@"copy_to_disk" ofType:@"sh"]; |
+ NSFileManager* manager = [NSFileManager defaultManager]; |
+ if (![manager fileExistsAtPath:toolPath]) { |
+ return false; |
+ } |
+ |
+ NSString* applicationsDirectory = [self getApplicationsFolder:isAuthorized]; |
+ destinationAppBundlePath_ = [NSString pathWithComponents: @[ |
+ applicationsDirectory, @"Google Chrome.app"]]; |
+ |
+ if (isAuthorized) { |
+ const char* args[] = {[applicationsDirectory UTF8String], NULL}; |
+ [self startPrivilegedTool:[toolPath UTF8String] |
+ withArguments:args |
+ authorization:authRef |
+ status:status]; |
+ } else { |
+ NSArray* args = @[ applicationsDirectory ]; |
+ [self startUnprivilegedTool:toolPath withArguments:args]; |
+ } |
+ |
+ AuthorizationFree(authRef, kAuthorizationFlagDestroyRights); |
+ return true; |
+} |
+ |
+- (NSString*)startInstall:(NSString*)appBundlePath { |
+ [self sendMessageToTool:appBundlePath]; |
+ return destinationAppBundlePath_; |
+} |
+ |
+// Sends a message to the tool's stdin. The tool is using 'read' to wait for |
+// input. 'read' adds to its buffer until it receives a newline to continue so |
+// append '\n' to the message to end the read. |
+- (void)sendMessageToTool:(NSString*)message { |
+ [communicationFile_ writeData:[[message stringByAppendingString:@"\n"] |
+ dataUsingEncoding:NSUTF8StringEncoding]]; |
+} |
+ |
+@end |