Chromium Code Reviews| 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..b67815afd71c5d641b791fb7e91609f59da3661f |
| --- /dev/null |
| +++ b/chrome/installer/mac/app/AuthorizedInstall.m |
| @@ -0,0 +1,132 @@ |
| +// 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) /Users/username/Applications is returned if: |
| +// c) The user doesn't authenticate and is not an admin. |
|
Elly Fong-Jones
2016/08/24 16:39:22
This comment is so good. You can refer to /Users/u
ivanhernandez
2016/08/24 18:23:17
Done.
|
| +- (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"]]; |
|
Elly Fong-Jones
2016/08/24 16:39:22
no more Chromo? :)
ivanhernandez
2016/08/24 18:23:17
This app is moving up in the world.
|
| + |
| + 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]; |
| + [self sendMessageToTool:@"\n"]; |
|
Elly Fong-Jones
2016/08/24 16:39:22
that's odd - why is the newline necessary? is the
ivanhernandez
2016/08/24 18:23:16
Good point, having sendMessageToTool append the ne
|
| + return destinationAppBundlePath_; |
| +} |
| + |
| +// Sends a message to the tool's stdin. |
| +- (void)sendMessageToTool:(NSString*)message { |
| + [communicationFile_ |
| + writeData:[message dataUsingEncoding:NSUTF8StringEncoding]]; |
| +} |
| + |
| +@end |