Detecting and affecting Lockdown Mode in macOS Ventura

Lockdown mode is new feature for macOS Ventura and for many MacAdmins we’ve been wondering how to detect this state. Why? Lockdown mode affects how macOS and Mac apps behave. This is something a helpdesk might like to know when trying to troubleshoot an issue. Also, due to some ambiguous wording by Apple, they made it seem like MDM Config Profiles could not be installed at all when in Lockdown mode, however this is not always the case. The hunt was on!

Detecting Lockdown Mode

I was looking everywhere last week: ps process lists, nvram, system_profiler, kextstat, launchctl, sysdiagnose, a defaults read dump, etc. I was looking high and low for “lock” “down” and “mode” and I got a hit in the com.apple.Safari domain in the sandboxed ~/Library/Containers/Safari path. While it turns out that Safari will in some cases write the button label LockdownModeToolbarIdentifier to that pref domain, it requires Safari to be launched and for the toolbar to be in non-default layout, otherwise the label name is never written! So that was a dead end.

Then a little birdie on MacAdmins pointed me in the right direction and blogged about it and wrote a Jamf extension attribute! 😅 Turns out I had missed the value sitting at the top of the defaults read dump! (d’oh) It was there the whole time in .GlobalPreferences, I just hadn’t done a diff like I should have! That would have revealed the key uses the LDM acronym/mnemonic: LDMGlobalEnabled Funnily enough, when I searched for this key on Google I got 5 hits and all of them for iOS, like this one at the Apple dev forums. However they were all about Swift and iOS, here’s how to do it in shell for the current user:

defaults read .GlobalPreferences.plist LDMGlobalEnabled 2>/dev/null

It’s a boolean value, that will not exist if Lockdown mode has never been enabled, when enabled it will report 1 from defaults and when disabled the key will remain and report 0. What stands out is that this is a per-user preference. Since it makes you reboot I had supposed it was a system-wide setting but sure enough if you log out and into another user, Lockdown mode is disabled. Perhaps that makes sense but I’m not quite sure about that?

Affecting Lockdown Mode

This totally blew me away: You can enable and disable macOS Lockdown mode by writing to your .GlobalPreferences preference domain!

#turn lockdown mode off
defaults write .GlobalPreferences.plist LDMGlobalEnabled -bool false
#turn lockdown mode on
defaults write .GlobalPreferences.plist LDMGlobalEnabled -bool true

That’s right, it’s not written to a rootless/SIP protected file like TCC.db! Just run the command as the user and it’ll turn toggle the behavior for most things. Here’s some details of my findings:

  • Configuration profiles – a restart of System Settings is not required, it will prohibit the manual installation of a .mobileconfig profile file. When Apple says “Configuration profiles can’t be installed” this is what they mean: User installed “double-click” installations of .mobileconfig files cannot be done. When they say “the device can’t be enrolled in Mobile Device Management or device supervision while in Lockdown Mode”, this only applies to these user-initiated MDM enrollments using a web browser that downloads .mobileconfig files. Lockdown mode does not prohibit enrollment into MDM that’s assigned via Apple Business Manager (ABM/DEP). You can initiate enrollment with the Terminal command: sudo profiles renew -type enrollment A Mac in Lockdown mode will be able to successfully enroll into an MDM assigned by ABM. Once enrolled, new Config Profiles can also be installed via that same MDM, even in Lockdown Mode.
  • Messages – a restart of Messages is not required, all messages will be blocked immediately, attachments or not. I’m not sure if that’s a bug or not since Apple only mentions attachments, not plain messages. It does not matter if the sender is in your Contacts or whether you have initiated contact with them before (like in Facetime). Messages will be delivered to any other devices not in Lockdown mode. If Lockdown mode is turned off, those blocked messages may be delivered if sent recently enough but will appear out of sequence. For example, a device that never had Lockdown Mode turned on would see messages: 1,2,3,4,5 while a device that turns it on and then off would see: 1,2,5,3,4
  • Facetime – restart not required, it will immediately begin blocking calls from anyone you have not called previously from that device. Unlike Messages though, it will show a Notification of the blockage.
  • Safari – app restart required. This differs from everything else, however Safari also gives the best visual indications that Lockdown mode is enabled! On the Start Page you’ll see “Lockdown Ready”, once at at website you’ll likely see “Lockdown Enabled” unless you’ve uncheck Enable Lockdown Mode in the top menubar SafariSettings for <site>… in which case you’ll see “Lockdown Off” in red.
Safari’s Lockdown Mode Toolbar states
  • Safari – Another subtle visual cue of Lockdown mode, that aligns with Apple’s “web fonts might not be displayed” guidance, can be seen on a Jamf user-initiated MDM enrollment screen, instead of a check mark you’ll see a square, take heed and turn back now! Since once you get the .mobileconfig files and fumble your way to System SettingsPrivacy & Security, scroll to the bottom of the list to Profiles (UX gripe: it used to just open the dang panel when you double clicked on them!) you’ll be blocked from installing it as seen above.
  • System Settings – an app restart is required for Privacy & Security to reflect the current state of LDMGlobalEnabled, if it was on and you disable via defaults once you launch System Settings again, it’ll let you turn it back on with a reboot and everything!

Wrapping Up

I didn’t try out the other lockdown mode behaviors for things like new Home management invitations or Shared Albums in Photos. Still it’s quite surprising that despite the System Settings GUI making you reboot to turn it on, Lockdown mode is a per-user setting that can seemingly be enabled and disabled dynamically with a simple defaults command run by the user. With the exception of Safari and System Settings it does not require Messages and Facetime to restart! There might be other caveats, it’s hard to tell. Perhaps this is all in the realm of “works as designed” for Apple but when you, the customer, don’t know what that exact design is, it can be quite a surprise!

Update: Looks like they started explaining a bit more about what happens when you enabled Lockdown Mode in macOS Sonoma

One more (unrelated) thing…

Update: As of Dec 13, 2022 Bug 174931 – Implement RegExp lookbehind assertions has been marked “Resolved” and the extensive pull request has all the gory details of the extensive refactoring that was done to implement this. 🎉 Thanks Michael Saboff! 🙏 Now when this will make it into Safari…. we’ll see.

Update 2: Safari 16.4, released March 27, 2023, now supports RegExp lookbehind assertions!

Since this post might get a few eyeballs, I’d also like to shine a light on the perplexing fact that Safari is the only browser that still doesn’t support the four year old ES2018 feature of RegExp lookbehind assertions?! I mean, sure it was a Google engineer who kindly filed this heads up to the WebKit team back in July of 2017 when it was a draft and a full year before it was ratified (Bug 174931 – Implement RegExp lookbehind assertions) but even a silly corporate rivalry couldn’t explain the seeming obstinance in letting this feature languish. I don’t get it, it just doesn’t make sense! There’s a nicely visualized page of where things stand and Safari is keeping company with IE 11 on this one.

Make these red islands green, Apple!

So take a look at the comments on the WebKit bug, some are quite funny, others just spot on, and there’s even one from yours truly. Perhaps add your own? Maybe when a bug gets 100 comments something special happens and we all get cake? 🎂

Jamf & FileVault 2: Tips & Tricks (and more)

Raiders of the Lost Feature Requests

So there’s this old feature request at Jamf Nation (stop me if you’ve heard this one…) it’s almost 6 years old: Add ability to report on FV2 Recovery Keys (and/or access them via API) In fact, maybe you came here from there, watch out don’t loop! Continue!

The pain point is this: Keys are sent back to Jamf Pro (JSS) but then can only be gotten at manually/interactively through the web interface, not via API nor another method. For cases of mass migration to another JSS it sure would be nice to move those keys over rather than decrypt/re-encrypt. Well, I’ve got a few insights regarding this that I’d like to share that may help. ‘Cuz hey it’s 2020 and we’ve learned that hoarding is just silly.

Firstly, it should be pointed out that neither ye olde “Recovery Key Redirection” payload nor it’s replacement “Recovery Key Escrow” are needed to get keys to the JSS. There is another method and it’s what is used by the built-in “Filevault Encryption” policy payload to get the keys back to your JSS. Jamf references this method in this old script at their GitHub. I revamped the core bits a couple years ago in a (nearly 7 year old) feature request: Manually Edit FileVault 2 Recovery Key

Telling the JSS Your Secrets

The takeaway from that is to realize we have a way to explicitly send keys to the JSS by placing 2 XML files in the /Library/Application Support/JAMF/run folder: file_vault_2_id.xml and file_vault_2_recovery_key.xml. Also note, Jamf has updated the process for the better in the last two years: a jamf recon (or two) is no longer required to send the key and validate it, instead JamfDaemon will send it immediately when both the files are detected. Which is nice, but it’s the subsequent recon validations where we have an opportunity to get grabby.

Cold Lamping, Hard Linking

So here’s the fun part: When recon occurs there’s lots of file traffic in /Library/Application Support/JAMF/tmp all sorts of transient scripts hit this folder. What we can do is make hard links to these files as they come in so when the link is removed in tmp another exists elsewhere and the file remains (just in our new location). EAGrabber.sh does exactly that (and a little bit more)

EAGrabber.sh can be easily modified to narrow it’s focus to the FileVault 2 key only, deleting the rest. What you do with the key is up to you: Send it somewhere else for safe keeping or keep it on device temporarily for a migration to another Jamf console. A script on the new JSS could then put that key on-disk into file_vault_2_recovery_key.xml file which will then import and validate, no decrypt/recrypt necessary. Hope this helps.

Cuidado ¡ Achtung ! Alert

Jamf admins take note: Do you have hard coded passwords in your extension attributes or scripts? If so, then all your scripts are belong to us. Now, go read Obfuscation vs. Encryption from Richard Purves. Read it? OK, now consider what happens if you were to add a routine to capture the output of ps aww along with a hard-linking loop like in EAGraber.sh. If you are passing API credentials from policies via parameter, then ps can capture those parameters and even if you try and obscure them, if we’ve captured the script we can de-obfuscate them. This is a good reason to be really careful with what your API accounts can do. If you have an API account with Computer record Read rights that gets passed into a script via policy and you use LAPS, then captured API credentials could be used to harvest LAPS passwords via API. Keep this in mind and we’ll see if any meaningful changes will occur in recon and/or the script running process in the future (if you open a ticket you can reference PI-006270 regarding API credentials in the process list). In the meantime make API actions as short lived as possible and cross your fingers that only you, good and noble #MacAdmins read this blog. 🤞