WireGuardApp: use file to communicate launch-by-login-helper

Apple event params are broken on recent macOS versions.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2021-09-22 19:22:44 +02:00
parent d882a486a9
commit 7171df84fa
6 changed files with 47 additions and 40 deletions

View File

@ -35,6 +35,10 @@ extension FileManager {
return sharedFolderURL?.appendingPathComponent("last-error.txt") return sharedFolderURL?.appendingPathComponent("last-error.txt")
} }
static var loginHelperTimestampURL: URL? {
return sharedFolderURL?.appendingPathComponent("login-helper-timestamp.bin")
}
static func deleteFile(at url: URL) -> Bool { static func deleteFile(at url: URL) -> Bool {
do { do {
try FileManager.default.removeItem(at: url) try FileManager.default.removeItem(at: url)

View File

@ -65,19 +65,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} }
} }
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows: Bool) -> Bool {
if let appleEvent = NSAppleEventManager.shared().currentAppleEvent {
if LaunchedAtLoginDetector.isReopenedByLoginItemHelper(reopenAppleEvent: appleEvent) {
return false
}
}
if hasVisibleWindows {
return true
}
showManageTunnelsWindow(completion: nil)
return false
}
@objc func confirmAndQuit() { @objc func confirmAndQuit() {
let alert = NSAlert() let alert = NSAlert()
alert.messageText = tr("macConfirmAndQuitAlertMessage") alert.messageText = tr("macConfirmAndQuitAlertMessage")

View File

@ -4,25 +4,16 @@
import Cocoa import Cocoa
class LaunchedAtLoginDetector { class LaunchedAtLoginDetector {
static let launchCode = "LaunchedByWireGuardLoginItemHelper"
static func isLaunchedAtLogin(openAppleEvent: NSAppleEventDescriptor) -> Bool { static func isLaunchedAtLogin(openAppleEvent: NSAppleEventDescriptor) -> Bool {
guard isOpenEvent(openAppleEvent) else { return false } let now = clock_gettime_nsec_np(CLOCK_UPTIME_RAW)
guard let propData = openAppleEvent.paramDescriptor(forKeyword: keyAEPropData) else { return false } guard openAppleEvent.eventClass == kCoreEventClass && openAppleEvent.eventID == kAEOpenApplication else { return false }
return propData.stringValue == launchCode guard let url = FileManager.loginHelperTimestampURL else { return false }
guard let data = try? Data(contentsOf: url) else { return false }
_ = FileManager.deleteFile(at: url)
guard data.count == 8 else { return false }
let then = data.withUnsafeBytes { ptr in
ptr.load(as: UInt64.self)
} }
return now - then <= 5000000000
static func isReopenedByLoginItemHelper(reopenAppleEvent: NSAppleEventDescriptor) -> Bool {
guard isReopenEvent(reopenAppleEvent) else { return false }
guard let propData = reopenAppleEvent.paramDescriptor(forKeyword: keyAEPropData) else { return false }
return propData.stringValue == launchCode
} }
} }
private func isOpenEvent(_ event: NSAppleEventDescriptor) -> Bool {
return event.eventClass == kCoreEventClass && event.eventID == kAEOpenApplication
}
private func isReopenEvent(_ event: NSAppleEventDescriptor) -> Bool {
return event.eventClass == kCoreEventClass && event.eventID == kAEReopenApplication
}

View File

@ -32,5 +32,7 @@
<true/> <true/>
<key>com.wireguard.macos.app_id</key> <key>com.wireguard.macos.app_id</key>
<string>$(APP_ID_MACOS)</string> <string>$(APP_ID_MACOS)</string>
<key>com.wireguard.macos.app_group_id</key>
<string>$(DEVELOPMENT_TEAM).group.$(APP_ID_MACOS)</string>
</dict> </dict>
</plist> </plist>

View File

@ -4,5 +4,9 @@
<dict> <dict>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.application-groups</key>
<array>
<string>$(DEVELOPMENT_TEAM).group.$(APP_ID_MACOS)</string>
</array>
</dict> </dict>
</plist> </plist>

View File

@ -5,13 +5,32 @@
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
NSString *appIdInfoDictionaryKey = @"com.wireguard.macos.app_id"; NSString *appId = [NSBundle.mainBundle objectForInfoDictionaryKey:@"com.wireguard.macos.app_id"];
NSString *appId = [NSBundle.mainBundle objectForInfoDictionaryKey:appIdInfoDictionaryKey]; NSString *appGroupId = [NSBundle.mainBundle objectForInfoDictionaryKey:@"com.wireguard.macos.app_group_id"];
if (!appId || !appGroupId)
NSString *launchCode = @"LaunchedByWireGuardLoginItemHelper"; return 1;
NSAppleEventDescriptor *paramDescriptor = [NSAppleEventDescriptor descriptorWithString:launchCode]; NSURL *containerUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId];
if (!containerUrl)
return 2;
uint64_t now = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
if (![[NSData dataWithBytes:&now length:sizeof(now)] writeToURL:[containerUrl URLByAppendingPathComponent:@"login-helper-timestamp.bin"] atomically:YES])
return 3;
if (@available(macOS 10.15, *)) {
NSCondition *condition = [[NSCondition alloc] init];
NSURL *appURL = [NSWorkspace.sharedWorkspace URLForApplicationWithBundleIdentifier:appId];
if (!appURL)
return 4;
NSWorkspaceOpenConfiguration *openConfiguration = [NSWorkspaceOpenConfiguration configuration];
openConfiguration.activates = NO;
openConfiguration.addsToRecentItems = NO;
openConfiguration.hides = YES;
[NSWorkspace.sharedWorkspace openApplicationAtURL:appURL configuration:openConfiguration completionHandler:^(NSRunningApplication * _Nullable app, NSError * _Nullable error) {
[condition signal];
}];
[condition wait];
} else {
[NSWorkspace.sharedWorkspace launchAppWithBundleIdentifier:appId options:NSWorkspaceLaunchWithoutActivation [NSWorkspace.sharedWorkspace launchAppWithBundleIdentifier:appId options:NSWorkspaceLaunchWithoutActivation
additionalEventParamDescriptor:paramDescriptor launchIdentifier:NULL]; additionalEventParamDescriptor:NULL launchIdentifier:NULL];
}
return 0; return 0;
} }