From 004c20d8e26c94f1bee7c7924aaa56691289c66b Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 3 Jul 2022 22:23:35 +0300 Subject: [PATCH] Handle GateKeeper and write permissions in the auto updater --- Cocoa/AppDelegate.m | 55 ++++++++++++++++++++++++++++++++++++++++++++- Makefile | 2 +- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/Cocoa/AppDelegate.m b/Cocoa/AppDelegate.m index 1670472..65e5934 100644 --- a/Cocoa/AppDelegate.m +++ b/Cocoa/AppDelegate.m @@ -5,6 +5,7 @@ #import #import #import +#import #define UPDATE_SERVER "https://sameboy.github.io" @@ -31,6 +32,7 @@ static uint32_t color_to_int(NSColor *color) UPDATE_FAILED, } _updateState; NSString *_downloadDirectory; + AuthorizationRef _auth; } - (void) applicationDidFinishLaunching:(NSNotification *)notification @@ -366,8 +368,55 @@ static uint32_t color_to_int(NSColor *color) [self.updateWindow performClose:sender]; } +- (bool)executePath:(NSString *)path withArguments:(NSArray *)arguments +{ + if (!_auth) { + NSTask *task = [[NSTask alloc] init]; + task.launchPath = path; + task.arguments = arguments; + [task launch]; + [task waitUntilExit]; + return task.terminationStatus == 0 && task.terminationReason == NSTaskTerminationReasonExit; + } + + char *argv[arguments.count + 1]; + argv[arguments.count] = NULL; + for (unsigned i = 0; i < arguments.count; i++) { + argv[i] = (char *)arguments[i].UTF8String; + } + + return AuthorizationExecuteWithPrivileges(_auth, path.UTF8String, kAuthorizationFlagDefaults, argv, NULL) == errAuthorizationSuccess; +} + +- (void)deauthorize +{ + if (_auth) { + AuthorizationFree(_auth, kAuthorizationFlagDefaults); + _auth = nil; + } +} + - (IBAction)installUpdate:(id)sender { + bool needsAuthorization = false; + if ([self executePath:@"/usr/sbin/spctl" withArguments:@[@"--status"]]) { // Succeeds when GateKeeper is on + // GateKeeper is on, we need to --add ourselves as root, else we might get a GateKeeper crash + needsAuthorization = true; + } + else if (access(_dyld_get_image_name(0), W_OK)) { + // We don't have write access, so we need authorization to update as root + needsAuthorization = true; + } + + if (needsAuthorization && !_auth) { + AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagPreAuthorize | kAuthorizationFlagInteractionAllowed, &_auth); + // Make sure we can modify the bundle + if (![self executePath:@"/usr/sbin/chown" withArguments:@[@"-R", [NSString stringWithFormat:@"%d:%d", getuid(), getgid()], [NSBundle mainBundle].bundlePath]]) { + [self deauthorize]; + return; + } + } + [self.updateProgressSpinner startAnimation:nil]; self.updateProgressButton.title = @"Cancel"; self.updateProgressButton.enabled = true; @@ -386,8 +435,8 @@ static uint32_t color_to_int(NSColor *color) appropriateForURL:[[NSBundle mainBundle] bundleURL] create:true error:nil] path]; - NSTask *unzipTask; if (!_downloadDirectory) { + [self deauthorize]; dispatch_sync(dispatch_get_main_queue(), ^{ self.updateProgressButton.enabled = false; self.updateProgressLabel.stringValue = @"Failed to extract update."; @@ -399,12 +448,14 @@ static uint32_t color_to_int(NSColor *color) return; } + NSTask *unzipTask; unzipTask = [[NSTask alloc] init]; unzipTask.launchPath = @"/usr/bin/unzip"; unzipTask.arguments = @[location.path, @"-d", _downloadDirectory]; [unzipTask launch]; [unzipTask waitUntilExit]; if (unzipTask.terminationStatus != 0 || unzipTask.terminationReason != NSTaskTerminationReasonExit) { + [self deauthorize]; [[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil]; dispatch_sync(dispatch_get_main_queue(), ^{ self.updateProgressButton.enabled = false; @@ -449,6 +500,7 @@ static uint32_t color_to_int(NSColor *color) NSError *error = nil; [[NSFileManager defaultManager] moveItemAtPath:contentsPath toPath:contentsTempPath error:&error]; if (error) { + [self deauthorize]; [[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil]; _downloadDirectory = nil; dispatch_sync(dispatch_get_main_queue(), ^{ @@ -463,6 +515,7 @@ static uint32_t color_to_int(NSColor *color) } [[NSFileManager defaultManager] moveItemAtPath:updateContentsPath toPath:contentsPath error:&error]; if (error) { + [self deauthorize]; [[NSFileManager defaultManager] moveItemAtPath:contentsTempPath toPath:contentsPath error:nil]; [[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil]; _downloadDirectory = nil; diff --git a/Makefile b/Makefile index e566773..6637bce 100644 --- a/Makefile +++ b/Makefile @@ -163,7 +163,7 @@ endif CFLAGS += -F/Library/Frameworks -mmacosx-version-min=10.9 -isysroot $(SYSROOT) OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(SYSROOT) -LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore -weak_framework Metal -weak_framework MetalKit -mmacosx-version-min=10.9 -isysroot $(SYSROOT) +LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore -framework Security -weak_framework Metal -weak_framework MetalKit -mmacosx-version-min=10.9 -isysroot $(SYSROOT) GL_LDFLAGS := -framework OpenGL endif CFLAGS += -Wno-deprecated-declarations