-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Auto-dismount volumes on system sleep and screen lock on macOS #1818
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,7 @@ | |
| #include "FatalErrorHandler.h" | ||
| #ifdef TC_MACOSX | ||
| #include "MacOSXSecureTextFieldHotkeys.h" | ||
| #include "MacOSXSleepLock.h" | ||
| #endif | ||
| #include "Forms/DeviceSelectionDialog.h" | ||
| #include "Forms/KeyfileGeneratorDialog.h" | ||
|
|
@@ -184,6 +185,7 @@ namespace VeraCrypt | |
| { | ||
| #ifdef TC_MACOSX | ||
| UninstallMacOSXSecureTextFieldHotkeys(); | ||
| UninstallMacOSXSleepLockHandler(); | ||
| #endif | ||
| try | ||
| { | ||
|
|
@@ -1038,6 +1040,38 @@ namespace VeraCrypt | |
| } | ||
| } | ||
|
|
||
| #ifdef TC_MACOSX | ||
| // Called from MacOSXSleepLock.mm on the main thread when the system is about | ||
| // to sleep. wxWidgets does not deliver wxEVT_POWER_SUSPENDING on macOS | ||
| // (wxHAS_POWER_EVENTS is undefined there), so this observer fills that gap. | ||
| // AutoDismountVolumes is called with alwaysForce=false so that the | ||
| // ForceAutoDismount preference decides whether volumes with open files are | ||
| // detached, matching the Windows auto-dismount events (bForceAutoDismount). | ||
| void OnMacOSXSystemWillSleep () | ||
| { | ||
| if (Gui && Gui->GetPreferences().BackgroundTaskEnabled && Gui->GetPreferences().DismountOnPowerSaving) | ||
| { | ||
| VolumeInfoList mountedVolumes = Core->GetMountedVolumes(); | ||
| if (!mountedVolumes.empty()) | ||
| Gui->AutoDismountVolumes (mountedVolumes, false); | ||
| } | ||
| } | ||
|
|
||
| // Called from MacOSXSleepLock.mm on the main thread when the user session | ||
| // has been verified as locked. macOS has no equivalent of the Windows | ||
| // screen-saver polling used by MainFrame::OnTimer, so screen-lock dismount | ||
| // is wired up here instead. Force semantics as above. | ||
| void OnMacOSXScreenLocked () | ||
| { | ||
| if (Gui && Gui->GetPreferences().BackgroundTaskEnabled && Gui->GetPreferences().DismountOnScreenSaver) | ||
| { | ||
| VolumeInfoList mountedVolumes = Core->GetMountedVolumes(); | ||
| if (!mountedVolumes.empty()) | ||
| Gui->AutoDismountVolumes (mountedVolumes, false); | ||
| } | ||
| } | ||
|
Comment on lines
+1050
to
+1072
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This calls |
||
| #endif | ||
|
|
||
| bool GraphicUserInterface::OnInit () | ||
| { | ||
| Gui = this; | ||
|
|
@@ -1193,6 +1227,11 @@ namespace VeraCrypt | |
| #ifdef wxHAS_POWER_EVENTS | ||
| Gui->Connect (wxEVT_POWER_SUSPENDING, wxPowerEventHandler (GraphicUserInterface::OnPowerSuspending)); | ||
| #endif | ||
| #ifdef TC_MACOSX | ||
| // macOS lacks wxHAS_POWER_EVENTS; use native observers for sleep and | ||
| // screen lock so volumes can be auto-dismounted on those events. | ||
| InstallMacOSXSleepLockHandler(); | ||
| #endif | ||
|
|
||
| mMainFrame = new MainFrame (nullptr); | ||
| #if defined(TC_UNIX) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| /* | ||
| Copyright (c) 2013-2026 AM Crypto. All rights reserved. | ||
|
|
||
| Governed by the Apache License 2.0 the full text of which is | ||
| contained in the file License.txt included in VeraCrypt binary and source | ||
| code distribution packages. | ||
| */ | ||
|
|
||
| #ifndef TC_HEADER_Main_MacOSXSleepLock | ||
| #define TC_HEADER_Main_MacOSXSleepLock | ||
|
|
||
| #ifdef TC_MACOSX | ||
| namespace VeraCrypt | ||
| { | ||
| // Register/unregister observers for system sleep and screen lock. | ||
| // Implemented in MacOSXSleepLock.mm. | ||
| void InstallMacOSXSleepLockHandler (); | ||
| void UninstallMacOSXSleepLockHandler (); | ||
|
|
||
| // Invoked by the observers above when the corresponding event occurs. | ||
| // Implemented on the C++ side (GraphicUserInterface.cpp) so that all | ||
| // wxWidgets / Core logic stays out of the Objective-C++ file. | ||
| void OnMacOSXSystemWillSleep (); | ||
| void OnMacOSXScreenLocked (); | ||
| } | ||
| #endif | ||
|
|
||
| #endif // TC_HEADER_Main_MacOSXSleepLock |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| /* | ||
| Copyright (c) 2013-2026 AM Crypto. All rights reserved. | ||
|
|
||
| Governed by the Apache License 2.0 the full text of which is | ||
| contained in the file License.txt included in VeraCrypt binary and source | ||
| code distribution packages. | ||
| */ | ||
|
|
||
| #include "System.h" | ||
| #include "MacOSXSleepLock.h" | ||
|
|
||
| #ifdef TC_MACOSX | ||
| #import <Cocoa/Cocoa.h> | ||
| #import <ApplicationServices/ApplicationServices.h> | ||
|
|
||
| // Queries the window server for the actual lock state of the current session. | ||
| // The com.apple.screenIsLocked distributed notification can be posted by any | ||
| // local process, so it is treated only as a hint and the dismount is performed | ||
| // only if the session really is locked. | ||
| static bool SessionIsLocked () | ||
| { | ||
| bool locked = false; | ||
| CFDictionaryRef session = CGSessionCopyCurrentDictionary (); | ||
| if (session) | ||
| { | ||
| CFTypeRef value = CFDictionaryGetValue (session, CFSTR ("CGSSessionScreenIsLocked")); | ||
| locked = value && CFGetTypeID (value) == CFBooleanGetTypeID () && CFBooleanGetValue ((CFBooleanRef) value); | ||
| CFRelease (session); | ||
| } | ||
| return locked; | ||
| } | ||
|
|
||
| @interface VCSleepLockObserver : NSObject | ||
| - (void) systemWillSleep: (NSNotification *) notification; | ||
| - (void) screenLocked: (NSNotification *) notification; | ||
| - (void) dismountIfSessionLocked; | ||
| @end | ||
|
|
||
| @implementation VCSleepLockObserver | ||
|
|
||
| // The callbacks below are entered from AppKit notification dispatch; a C++ | ||
| // exception must not unwind through those Objective-C frames, so every call | ||
| // into VeraCrypt code is guarded here. The dismount is best-effort: showing | ||
| // UI is not reliable while the session is locking or the system is going to | ||
| // sleep, so failures are ignored. | ||
|
|
||
| - (void) systemWillSleep: (NSNotification *) notification | ||
| { | ||
| (void) notification; | ||
| try | ||
| { | ||
| VeraCrypt::OnMacOSXSystemWillSleep (); | ||
| } | ||
| catch (...) { } | ||
| } | ||
|
|
||
| - (void) screenLocked: (NSNotification *) notification | ||
| { | ||
| (void) notification; | ||
| // The window server can update the session dictionary slightly after the | ||
| // notification is delivered, so if the session does not report itself as | ||
| // locked yet, re-check once after a short delay instead of giving up. | ||
| if (SessionIsLocked ()) | ||
| { | ||
| [self dismountIfSessionLocked]; | ||
| } | ||
| else | ||
| { | ||
| [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector (dismountIfSessionLocked) object: nil]; | ||
| [self performSelector: @selector (dismountIfSessionLocked) withObject: nil afterDelay: 1.0]; | ||
| } | ||
| } | ||
|
|
||
| - (void) dismountIfSessionLocked | ||
| { | ||
| if (!SessionIsLocked ()) | ||
| return; | ||
|
|
||
| try | ||
| { | ||
| VeraCrypt::OnMacOSXScreenLocked (); | ||
| } | ||
| catch (...) { } | ||
| } | ||
|
Comment on lines
+47
to
+84
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add an exception boundary before returning to Cocoa notification delivery. These callbacks enter the VeraCrypt dismount path, and |
||
|
|
||
| @end | ||
|
|
||
| namespace VeraCrypt | ||
| { | ||
| // Non-ARC build (see the .mm compile rule in Build/Include/Makefile.inc), | ||
| // so the observer is retained by alloc/init and released in Uninstall. | ||
| static VCSleepLockObserver *SleepLockObserver = nil; | ||
|
|
||
| void InstallMacOSXSleepLockHandler () | ||
| { | ||
| if (SleepLockObserver) | ||
| return; | ||
|
|
||
| SleepLockObserver = [[VCSleepLockObserver alloc] init]; | ||
|
|
||
| // System sleep (lid close, idle sleep, Apple menu > Sleep). Delivered on | ||
| // the main thread by NSWorkspace's own notification center, which only | ||
| // the system feeds (it is per-process, unlike the distributed center). | ||
| [[[NSWorkspace sharedWorkspace] notificationCenter] | ||
| addObserver: SleepLockObserver | ||
| selector: @selector (systemWillSleep:) | ||
| name: NSWorkspaceWillSleepNotification | ||
| object: nil]; | ||
|
|
||
| // Screen lock. macOS exposes no public notification for this; the | ||
| // com.apple.screenIsLocked distributed notification is the long-standing | ||
| // de-facto mechanism used to detect it. It is untrusted input: the | ||
| // handler verifies the actual session lock state before dismounting. | ||
| [[NSDistributedNotificationCenter defaultCenter] | ||
| addObserver: SleepLockObserver | ||
| selector: @selector (screenLocked:) | ||
| name: @"com.apple.screenIsLocked" | ||
| object: nil]; | ||
| } | ||
|
Comment on lines
+110
to
+119
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please treat this distributed notification as untrusted and verify that the session is actually locked before dismounting. Otherwise another local process may be able to post the same notification and trigger VeraCrypt forced dismount. |
||
|
|
||
| void UninstallMacOSXSleepLockHandler () | ||
| { | ||
| if (!SleepLockObserver) | ||
| return; | ||
|
|
||
| [NSObject cancelPreviousPerformRequestsWithTarget: SleepLockObserver]; | ||
| [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver: SleepLockObserver]; | ||
| [[NSDistributedNotificationCenter defaultCenter] removeObserver: SleepLockObserver]; | ||
| [SleepLockObserver release]; | ||
| SleepLockObserver = nil; | ||
| } | ||
| } | ||
| #endif | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please align the visible preference text with the macOS behavior. If this means screen lock, please use the existing IDC_PREF_UNMOUNT_SESSION_LOCKED label on macOS, or also handle the actual screen saver notification. Also, making the power saving checkbox visible exposes the existing
LINUX_ENTERING_POVERSAWINGtypo, which should be fixed or avoided.