| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "AuthorizedInstall.h" |
| 6 |
| 7 @interface AuthorizedInstall () { |
| 8 NSFileHandle* communicationFile_; |
| 9 NSString* destinationAppBundlePath_; |
| 10 } |
| 11 @end |
| 12 |
| 13 @implementation AuthorizedInstall |
| 14 // Does the setup needed to authorize a tool to run as admin. |
| 15 - (OSStatus)setUpAuthorization:(AuthorizationRef*)authRef { |
| 16 OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, |
| 17 kAuthorizationFlagDefaults, authRef); |
| 18 |
| 19 AuthorizationItem items = {kAuthorizationRightExecute, 0, NULL, 0}; |
| 20 AuthorizationRights rights = {1, &items}; |
| 21 AuthorizationFlags flags = |
| 22 kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | |
| 23 kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights; |
| 24 |
| 25 status = AuthorizationCopyRights(*authRef, &rights, NULL, flags, NULL); |
| 26 return status; |
| 27 } |
| 28 |
| 29 // Starts up the proccess with privileged permissions. |
| 30 - (void)startPrivilegedTool:(const char*)toolPath |
| 31 withArguments:(const char**)args |
| 32 authorization:(AuthorizationRef)authRef |
| 33 status:(OSStatus)status { |
| 34 if (status != errAuthorizationSuccess) |
| 35 return; |
| 36 |
| 37 FILE* file; |
| 38 #pragma clang diagnostic push |
| 39 #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
| 40 status = AuthorizationExecuteWithPrivileges( |
| 41 authRef, toolPath, kAuthorizationFlagDefaults, (char* const*)args, &file); |
| 42 #pragma clang diagnostic pop |
| 43 communicationFile_ = [[NSFileHandle alloc] initWithFileDescriptor:fileno(file) |
| 44 closeOnDealloc:YES]; |
| 45 } |
| 46 |
| 47 // Starts up same proccess as above without privileged permissions. |
| 48 - (void)startUnprivilegedTool:(NSString*)toolPath withArguments:(NSArray*)args { |
| 49 NSPipe* pipe = [NSPipe pipe]; |
| 50 NSTask* task = [[NSTask alloc] init]; |
| 51 [task setArguments:args]; |
| 52 [task setLaunchPath:toolPath]; |
| 53 [task setStandardInput:pipe]; |
| 54 [task launch]; |
| 55 communicationFile_ = [pipe fileHandleForWriting]; |
| 56 } |
| 57 |
| 58 // Determines which "Applications" folder to use based on authorization. |
| 59 // There are three possible scenarios and two possible return values. |
| 60 // 1) /Applications is returned if: |
| 61 // a) The user authenticates the app. |
| 62 // b) The user doesn't authenticate but is an admin. |
| 63 // 2) $HOME/Applications is returned if: |
| 64 // c) The user doesn't authenticate and is not an admin. |
| 65 - (NSString*)getApplicationsFolder:(BOOL)isAuthorized { |
| 66 NSFileManager* manager = [NSFileManager defaultManager]; |
| 67 NSArray* applicationDirectories = NSSearchPathForDirectoriesInDomains( |
| 68 NSApplicationDirectory, NSLocalDomainMask, YES); |
| 69 if (isAuthorized || |
| 70 [manager isWritableFileAtPath:applicationDirectories.firstObject]) { |
| 71 return applicationDirectories.firstObject; |
| 72 } else { |
| 73 NSString* usersApplicationsDirectory = |
| 74 [NSString pathWithComponents:@[ NSHomeDirectory(), @"Applications" ]]; |
| 75 if (![manager fileExistsAtPath:usersApplicationsDirectory]) { |
| 76 [manager createDirectoryAtPath:usersApplicationsDirectory |
| 77 withIntermediateDirectories:NO |
| 78 attributes:nil |
| 79 error:nil]; |
| 80 } |
| 81 return usersApplicationsDirectory; |
| 82 } |
| 83 } |
| 84 |
| 85 // Attempts to gain authorization to run installation tool with elevated |
| 86 // permissions. |
| 87 // Then starts the tool with the appropiate paths for the tools elevation |
| 88 // status. |
| 89 - (BOOL)loadInstallationTool { |
| 90 AuthorizationRef authRef = NULL; |
| 91 OSStatus status = [self setUpAuthorization:&authRef]; |
| 92 BOOL isAuthorized = (status == errAuthorizationSuccess); |
| 93 |
| 94 NSString* toolPath = |
| 95 [[NSBundle mainBundle] pathForResource:@"copy_to_disk" ofType:@"sh"]; |
| 96 NSFileManager* manager = [NSFileManager defaultManager]; |
| 97 if (![manager fileExistsAtPath:toolPath]) { |
| 98 return false; |
| 99 } |
| 100 |
| 101 NSString* applicationsDirectory = [self getApplicationsFolder:isAuthorized]; |
| 102 destinationAppBundlePath_ = [NSString pathWithComponents: @[ |
| 103 applicationsDirectory, @"Google Chrome.app"]]; |
| 104 |
| 105 if (isAuthorized) { |
| 106 const char* args[] = {[applicationsDirectory UTF8String], NULL}; |
| 107 [self startPrivilegedTool:[toolPath UTF8String] |
| 108 withArguments:args |
| 109 authorization:authRef |
| 110 status:status]; |
| 111 } else { |
| 112 NSArray* args = @[ applicationsDirectory ]; |
| 113 [self startUnprivilegedTool:toolPath withArguments:args]; |
| 114 } |
| 115 |
| 116 AuthorizationFree(authRef, kAuthorizationFlagDestroyRights); |
| 117 return true; |
| 118 } |
| 119 |
| 120 - (NSString*)startInstall:(NSString*)appBundlePath { |
| 121 [self sendMessageToTool:appBundlePath]; |
| 122 return destinationAppBundlePath_; |
| 123 } |
| 124 |
| 125 // Sends a message to the tool's stdin. The tool is using 'read' to wait for |
| 126 // input. 'read' adds to its buffer until it receives a newline to continue so |
| 127 // append '\n' to the message to end the read. |
| 128 - (void)sendMessageToTool:(NSString*)message { |
| 129 [communicationFile_ writeData:[[message stringByAppendingString:@"\n"] |
| 130 dataUsingEncoding:NSUTF8StringEncoding]]; |
| 131 } |
| 132 |
| 133 @end |
| OLD | NEW |