Skip to content

Commit 7bccbd4

Browse files
committed
2 parents 84432ea + 6522903 commit 7bccbd4

File tree

9 files changed

+89
-11
lines changed

9 files changed

+89
-11
lines changed

Crypt.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@
338338
SWIFT_COMPILATION_MODE = singlefile;
339339
SWIFT_OBJC_BRIDGING_HEADER = "Crypt/Crypt-Bridging-Header.h";
340340
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
341-
SWIFT_SWIFT3_OBJC_INFERENCE = On;
341+
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
342342
SWIFT_VERSION = 4.0;
343343
WRAPPER_EXTENSION = bundle;
344344
};
@@ -367,7 +367,7 @@
367367
PROVISIONING_PROFILE_SPECIFIER = "";
368368
SKIP_INSTALL = YES;
369369
SWIFT_OBJC_BRIDGING_HEADER = "Crypt/Crypt-Bridging-Header.h";
370-
SWIFT_SWIFT3_OBJC_INFERENCE = On;
370+
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
371371
SWIFT_VERSION = 4.0;
372372
WRAPPER_EXTENSION = bundle;
373373
};

Crypt/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>
22-
<string>3.1.2</string>
22+
<string>3.2.0</string>
2323
<key>NSHumanReadableCopyright</key>
2424
<string>Copyright © 2018 The Crypt Project. All rights reserved.</string>
2525
<key>NSPrincipalClass</key>

Crypt/Mechanisms/Check.swift

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Check: CryptMechanism {
2828
// Preference bundle id
2929
fileprivate let bundleid = "com.grahamgilbert.crypt"
3030

31-
func run() {
31+
@objc func run() {
3232
os_log("Starting run of Crypt.Check...", log: Check.log, type: .default)
3333

3434
// check for ServerUrl
@@ -72,15 +72,32 @@ class Check: CryptMechanism {
7272

7373
// Check to see if our recovery key exists at the OutputPath Preference.
7474
let recoveryKeyExists: Bool = checkFileExists(path: filepath)
75+
let genKey = getGenerateNewKey()
76+
let generateKey : Bool = genKey.generateKey
77+
let forcedKey : Bool = genKey.forcedKey
7578

76-
if !recoveryKeyExists && !removePlist && rotateKey {
79+
if (!recoveryKeyExists && !removePlist && rotateKey) || generateKey {
80+
if forcedKey {
81+
os_log("WARNING!!!!!! GenerateNewKey is set to True, but it's a Managed Preference, you probably don't want to do this. Please change to a non Managed value.", log: Check.log, type: .error)
82+
self.needsEncryption = false
83+
_ = allowLogin()
84+
return;
85+
}
7786
// If key is missing from disk and we aren't supposed to remove it we should generate a new key...
78-
os_log("Key is missing at %{public}@, and RemovePlist is False. And RotateKey is True, Attempting to generate a new key...", log: Check.log, type: .error, String(describing: filepath))
87+
os_log("Conditions for making a new key have been met. Attempting to generate a new key...", log: Check.log, type: .default)
7988
do {
8089
try _ = rotateRecoveryKey(the_settings, filepath: filepath)
8190
} catch let error as NSError {
8291
os_log("Caught error trying to rotate recovery key: %{public}@", log: Check.log, type: .error, error.localizedDescription)
8392
}
93+
94+
if generateKey {
95+
os_log("We've rotated the key and GenerateNewKey was True, setting to False to avoid multiple generations", log: Check.log, type: .default)
96+
// set to false for house keeping
97+
_ = CFPreferencesSetValue("GenerateNewKey" as CFString, false as CFPropertyList, bundleid as CFString, kCFPreferencesAnyUser, kCFPreferencesAnyHost)
98+
// delete from root if set there.
99+
_ = CFPreferencesSetAppValue("GenerateNewKey" as CFString, nil, bundleid as CFString)
100+
}
84101
self.needsEncryption = false
85102
_ = allowLogin()
86103
return;
@@ -228,6 +245,13 @@ class Check: CryptMechanism {
228245
return removeplist
229246
}
230247

248+
fileprivate func getGenerateNewKey() -> (generateKey: Bool, forcedKey: Bool) {
249+
let forcedKey : Bool = CFPreferencesAppValueIsForced("GenerateNewKey" as CFString, bundleid as CFString)
250+
guard let genkey : Bool = CFPreferencesCopyAppValue("GenerateNewKey" as CFString, bundleid as CFString) as? Bool
251+
else {return (false, forcedKey)}
252+
return (genkey, forcedKey)
253+
}
254+
231255
func rotateRecoveryKey(_ theSettings : NSDictionary, filepath : String) throws -> Bool {
232256
os_log("Attempting to Rotate Recovery Key...", log: Check.log, type: .default)
233257
let inputPlist = try PropertyListSerialization.data(fromPropertyList: theSettings,

Crypt/Mechanisms/CryptGUI.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import Foundation
2020

2121
class CryptGUI: CryptMechanism {
22-
func run() {
22+
@objc func run() {
2323
if (self.needsEncryption) {
2424
let promptWindowController = PromptWindowController.init()
2525
promptWindowController.mechanism = self.mechanism

Crypt/Mechanisms/CryptMechanism.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class CryptMechanism: NSObject {
3434
var mechanism:UnsafePointer<MechanismRecord>
3535

3636
// init the class with a MechanismRecord
37-
init(mechanism:UnsafePointer<MechanismRecord>) {
37+
@objc init(mechanism:UnsafePointer<MechanismRecord>) {
3838
os_log("initWithMechanismRecord", log: CryptMechanism.log, type: .default)
3939
self.mechanism = mechanism
4040
}

Crypt/Mechanisms/Enablement.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Enablement: CryptMechanism {
2929
private static let log = OSLog(subsystem: "com.grahamgilbert.crypt", category: "Enablement")
3030
// This is the only public function. It will be called from the
3131
// ObjC AuthorizationPlugin class
32-
func run() {
32+
@objc func run() {
3333

3434
if self.needsEncryption {
3535

Example Crypt Profile.mobileconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
<true/>
4242
<key>OutputPath</key>
4343
<string>/var/root/crypt_output.plist</string>
44+
<key>PostRunCommand</key>
45+
<array>
46+
<string>/usr/local/munki/managedsoftwareupdate</string>
47+
<string>--auto</string>
48+
</array>
4449
<key>KeyEscrowInterval</key>
4550
<integer>1</integer>
4651
<key>PayloadEnabled</key>

Package/checkin

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import sys
88
import datetime
99
import syslog
1010
import platform
11+
import json
1112
from distutils.version import LooseVersion
1213

1314
import FoundationPlist
@@ -133,14 +134,42 @@ def escrow_key(plist):
133134
task = subprocess.Popen(cmd, stdout=subprocess.PIPE,
134135
stderr=subprocess.PIPE)
135136
(output, error) = task.communicate()
137+
136138
if task.returncode == 0:
137139
logging.info('Key escrow successful.')
140+
server_initiated_rotation(output)
138141
return True
139142
else:
140143
logging.error('Key escrow unsuccessful.')
141144
return False
142145

143146

147+
def server_initiated_rotation(output):
148+
"""
149+
Rotate the key if the server tells us to.
150+
We need the old key to be present on disk and RotateUsedKey to be True
151+
"""
152+
try:
153+
json_output = json.loads(output)
154+
except ValueError:
155+
return ''
156+
157+
if not pref('RotateUsedKey') or pref('RemovePlist'):
158+
# Don't do anything if we don't care about the good stuff
159+
return ''
160+
161+
output_plist = pref('OutputPath')
162+
if not os.path.isfile(output_plist):
163+
# Need this to be here too (which it should, but you never know..)
164+
return ''
165+
166+
if json_output.get('rotation_required', False):
167+
logging.info('Removing output plist for rotation at next login.')
168+
os.remove(output_plist)
169+
170+
post_run_command()
171+
172+
144173
def using_recovery_key():
145174
"""Check if FileVault is currently unlocked using
146175
the recovery key.
@@ -158,6 +187,18 @@ def using_recovery_key():
158187
return False
159188

160189

190+
def post_run_command():
191+
run_command = pref('PostRunCommand')
192+
output_plist = pref('OutputPath')
193+
if run_command and not os.path.isfile(output_plist):
194+
logging.info('Running {}'.format(run_command))
195+
try:
196+
output = subprocess.check_output(run_command)
197+
logging.info(output)
198+
except subprocess.CalledProcessError as e:
199+
logging.error('Failed to run PostRunCommand: {}'.format(e))
200+
201+
161202
def get_recovery_key(key_location):
162203
"""Returns recovery key as a string... If we failed
163204
to get the proper information, returns an empty string"""
@@ -194,7 +235,7 @@ def rotate_invalid_key(plist_path):
194235
logging.warning('macOS version is too old to run reliably')
195236
return False
196237

197-
if os.path.exists(plist_path):
238+
if os.path.isfile(plist_path):
198239
recovery_key = get_recovery_key(plist_path)
199240
else:
200241
logging.warning('Recovery key is not present on disk')
@@ -299,6 +340,7 @@ def main():
299340
if pref('RotateUsedKey') and pref('ValidateKey') and \
300341
not pref('RemovePlist'):
301342
rotate_invalid_key(plist_path)
343+
post_run_command()
302344

303345
if os.path.isfile(plist_path):
304346
plist = FoundationPlist.readPlist(plist_path)
@@ -326,14 +368,17 @@ def main():
326368
escrow_interval))
327369
sys.exit(0)
328370
escrow_result = escrow_key(plist=plist)
329-
if escrow_result:
371+
if escrow_result and os.path.isfile(plist_path):
330372
remove_plist = pref('RemovePlist')
331373
plist['escrow_success'] = True
332374
plist['last_run'] = datetime.datetime.now()
333375
FoundationPlist.writePlist(plist, plist_path)
334376
if remove_plist is True:
335377
os.remove(plist_path)
336378
logging.info('Removing plist due to configuration.')
379+
else:
380+
os.chmod(plist_path, 0600)
381+
logging.info('Ensuring permissions on plist.')
337382

338383

339384
if __name__ == '__main__':

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ As of version 3.0.0 you can now define the time interval in Hours for how often
8181
$ sudo defaults write /Library/Preferences/com.grahamgilbert.crypt KeyEscrowInterval -int 2
8282
```
8383

84+
### PostRunCommand
85+
86+
(Introduced in version 3.2.0) This is a command that is run after Crypt has detected an error condition with a stored key that cannot be resolved silently - either it has failed validation or the server has instructed the client to rotate the key. These cannot be resolved silently on APFS volumes, so the user will need to log in again. If you have a tool that can enforce a logout or a reboot, you can run it here. This preference can either be a string if your command has no spaces, or an array if there are spaces in the command.
87+
8488

8589
## Uninstalling
8690

0 commit comments

Comments
 (0)