Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blocking more than just macOS Installers #13

Open
wants to merge 16 commits into
base: add-license-1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
custom: "https://www.buymeacoffee.com/hjuutilainen"
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,38 @@

Detect when `Install macOS Big Sur.app` installer application has launched, terminate the process and display an alert.

![bigsurblocker](https://raw.githubusercontent.com/hjuutilainen/bigsurblocker/main/screenshot.jpg)

This project is heavily inspired by Erik Berglund's [AppBlocker](https://github.com/erikberglund/AppBlocker). It uses the same underlying idea of registering and listening for NSWorkspace notifications when app has started up and then checking the CFBundleIdentifier of the launched app to identify a Big Sur installer launch.

# Why

Apple wants end users to upgrade to the latest macOS as soon as it becomes available. Depending on the software and policies your organization uses, this might be unacceptable. As an administrator, you currently have some options:
- Use an MDM to push a profile to delay updates for maximum of 90 days. This will however postpone _all_ updates, not just the macOS upgrade.
- If your fleet is enrolled in an MDM, you can use `softwareupdate --ignore` to hide certain updates. This will result in a highly broken user experience where the system thinks it has an update pending but it is unable to download and install it. Apple has also decided that only MDM enrolled systems can use the `--ignore` flag.
- If you are already using a binary authorization system such as Googles [Santa](https://github.com/google/santa), you should use it but deploying a system like Santa only for blocking Big Sur might be unfeasible.

# How

The `bigsurblocker` binary is installed in `/usr/local/bin` and is launched for each user through a launch agent. This means that the binary is running in the user session and therefore has the privileges of the current user. It runs silently in the background and listens for app launch notifications. As soon as the user launches the macOS installer application, the binary (forcefully) terminates it and displays a warning message.

By design, it will _not_ block the `startosinstall` command line tool.

# Requirements

The binary requires at least macOS 10.9, however I've only tested this on macOS 10.13 and 10.14.
The binary requires at least macOS 10.9, however it has been tested only on macOS 10.10, 10.11, 10.12, 10.13, 10.14 and 10.15.

Note. It seems that macOS 10.10 and 10.11 have trouble installing a signed and notarized package. Use the unsigned package available from the releases page if deploying on those. The signed and notarized package can be used on macOS 10.12 and later.

# Configuration

All configuration is optional. If needed, the alert title and text can be set through a configuration profile. Use `com.hjuutilainen.bigsurblocker` as the domain and `AlertTitle` and `AlertText` as the keys.

# Installation

Download the prebuilt package and deploy with your favorite method. The package is signed and notarized.
On macOS 10.12 and later, download a prebuilt package from the [Releases page](https://github.com/hjuutilainen/bigsurblocker/releases) and deploy with your favorite method. The package is signed and notarized.

On OS X 10.11 and earlier, download and deploy an unsigned package from the [Releases page](https://github.com/hjuutilainen/bigsurblocker/releases) and deploy with your favorite method

# Uninstall

Expand All @@ -31,3 +50,7 @@ rm -f /usr/local/bin/bigsurblocker

pkgutil --forget com.hjuutilainen.bigsurblocker
```

# License

Big Sur Blocker is licensed under [the MIT License](https://github.com/hjuutilainen/bigsurblocker/blob/main/LICENSE).
2 changes: 1 addition & 1 deletion bigsurblocker-munkipkg/build-info.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bigsurblocker-${version}.pkg",
"version": "20201021",
"version": "20201111",
"identifier": "com.hjuutilainen.bigsurblocker",
"postinstall_action": "none",
"suppress_bundle_relocation": true,
Expand Down
85 changes: 57 additions & 28 deletions bigsurblocker/main.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,90 +8,107 @@
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>
@interface AppDelegate : NSObject <NSApplicationDelegate, NSAlertDelegate>
@property BOOL alertTriggered;
- (BOOL)alertShowHelp:(NSAlert *)alert;
@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)notification {
self.alertTriggered = NO;

NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
NSNotificationCenter *nc = [workspace notificationCenter];

NSOperationQueue *notificationQueue = [NSOperationQueue new];

// Subscribe for notifications when apps are launched
[nc addObserverForName:NSWorkspaceDidLaunchApplicationNotification
object:nil
queue:notificationQueue
usingBlock:^(NSNotification * _Nonnull note) {

// Get information about the launched app
NSDictionary *userInfo = [note userInfo];
NSRunningApplication *runningApp = [userInfo objectForKey:NSWorkspaceApplicationKey];
NSString *bundleID = runningApp.bundleIdentifier;

NSArray *bundleIDsToBlock = @[
@"com.apple.InstallAssistant.BigSur",
@"com.apple.InstallAssistant.Seed.macOS1016Seed1",
];


// Load our user defaults suite. This will allow the alert text
// to be specified with a configuration profile
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.hjuutilainen.bigsurblocker"];

NSArray *bundleIDsToBlock = [userDefaults arrayForKey:@"bundleIDsToBlock"];
if (!bundleIDsToBlock) {
bundleIDsToBlock = @[
@"com.apple.InstallAssistant.BigSur",
@"com.apple.InstallAssistant.macOSBigSur",
@"com.apple.InstallAssistant.Seed.macOS1016Seed1",
];
}
if ([bundleIDsToBlock containsObject:bundleID]) {
NSLog(@"Detected macOS installer app launch");

// Get the localized app name
NSString *appName = runningApp.localizedName;

// Terminate the app
NSLog(@"Terminating \"%@\", \"%@\"", appName, bundleID);

// We could be polite but...
[runningApp forceTerminate];

// Check if we are already displaying an alert
if (self.alertTriggered) {
NSLog(@"Skipping alert. Previous alert still running");
} else {
self.alertTriggered = YES;

// GUI must be run from main thread
dispatch_async(dispatch_get_main_queue(), ^{

// Load our user defaults suite. This will allow the alert text
// to be specified with a configuration profile
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.hjuutilainen.bigsurblocker"];



NSString *messageText = [userDefaults stringForKey:@"AlertTitle"];
if (!messageText) {
messageText = NSLocalizedString(@"The application \"%@\" has been blocked", @"");
}

NSString *informativeText = [userDefaults stringForKey:@"AlertText"];
if (!informativeText) {
informativeText = NSLocalizedString(@"Contact your administrator for more information", @"");
}

// Configure the alert
NSAlert *alert = [[NSAlert alloc] init];

[alert setMessageText:[NSString stringWithFormat:messageText, appName]];
[alert setInformativeText:informativeText];

[alert addButtonWithTitle:NSLocalizedString(@"OK", @"")];

NSString *buttonTitle = [userDefaults stringForKey:@"ButtonTitle"];
if (!buttonTitle) {
buttonTitle = NSLocalizedString(@"OK", @"");
}
[alert addButtonWithTitle:buttonTitle];
[alert setAlertStyle:NSAlertStyleWarning];
[alert setIcon:[NSImage imageNamed:NSImageNameCaution]];

// If a custom help URL is defined in defaults, enable the help button
NSString *helpURLString = [userDefaults stringForKey:@"HelpURL"];
if (helpURLString) {
[alert setDelegate:self];
[alert setShowsHelp:YES];
}


// Show the alert above all other apps and windows
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[[alert window] setLevel:NSStatusWindowLevel];

// Show the alert
//
// Note that the [alert runModal] will not return until the user dismisses the popup window
[alert runModal];

// User dismissed the alert, change status to allow new ones to be displayed
self.alertTriggered = NO;
});
Expand All @@ -100,6 +117,18 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification {
}];
}

- (BOOL)alertShowHelp:(NSAlert *)alert
{
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.hjuutilainen.bigsurblocker"];
NSString *helpURLString = [userDefaults stringForKey:@"HelpURL"];
if (helpURLString) {
NSURL *helpURL = [NSURL URLWithString:helpURLString];
[[NSWorkspace sharedWorkspace] openURL:helpURL];
}

return YES;
}

@end

int main(int argc, const char * argv[]) {
Expand Down
Binary file added screenshot.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.