Skip to content

Commit

Permalink
move crypt mechanism to core
Browse files Browse the repository at this point in the history
  • Loading branch information
wesw-stripe committed Jan 30, 2025
1 parent 2c8e3bb commit 5f25bc5
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 127 deletions.
73 changes: 37 additions & 36 deletions Crypt/Mechanisms/CryptMechanism.swift → Core.swift
Original file line number Diff line number Diff line change
@@ -1,104 +1,104 @@
/*
Crypt

Copyright 2025 The Crypt Project.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
Crypt
Copyright 2025 The Crypt Project.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import Foundation
import Security
import os.log

class CryptMechanism: NSObject {
class Crypt: NSObject {
// This NSString will be used as the domain for the inter-mechanism context data
let contextCryptDomain: NSString = "com.grahamgilbert.crypt"

// Define a pointer to the MechanismRecord. This will be used to get and set
// all the inter-mechanism data. It is also used to allow or deny the login.
var mechanism: UnsafePointer<MechanismRecord>

// init the class with a MechanismRecord
@objc init(mechanism: UnsafePointer<MechanismRecord>) {
os_log("initWithMechanismRecord", log: mechLog, type: .debug)
os_log("initWithMechanismRecord", log: coreLog, type: .debug)
self.mechanism = mechanism
}

// Allow the login. End of the mechanism
func allowLogin() {
os_log("called allowLogin", log: mechLog, type: .default)
os_log("called allowLogin", log: coreLog, type: .default)
_ = self.mechanism.pointee.fPlugin.pointee.fCallbacks.pointee.SetResult(
mechanism.pointee.fEngine, AuthorizationResult.allow)
}

private func getContextData(key: AuthorizationString) -> NSData? {
os_log("getContextData called", log: mechLog, type: .debug)
os_log("getContextData called", log: coreLog, type: .debug)
var value: UnsafePointer<AuthorizationValue>?
let data = withUnsafeMutablePointer(to: &value) { (ptr: UnsafeMutablePointer) -> NSData? in
var flags = AuthorizationContextFlags()
if self.mechanism.pointee.fPlugin.pointee.fCallbacks.pointee.GetContextValue(
self.mechanism.pointee.fEngine, key, &flags, ptr) != errAuthorizationSuccess {
os_log("GetContextValue failed", log: mechLog, type: .error)
os_log("GetContextValue failed", log: coreLog, type: .error)
return nil
}
guard let length = ptr.pointee?.pointee.length else {
os_log("length failed to unwrap", log: mechLog, type: .error)
os_log("length failed to unwrap", log: coreLog, type: .error)
return nil
}
guard let buffer = ptr.pointee?.pointee.data else {
os_log("data failed to unwrap", log: mechLog, type: .error)
os_log("data failed to unwrap", log: coreLog, type: .error)
return nil
}
if length == 0 {
os_log("length is 0", log: mechLog, type: .error)
os_log("length is 0", log: coreLog, type: .error)
return nil
}
return NSData.init(bytes: buffer, length: length)
}
return data
}

var username: NSString? {
get {
os_log("Requesting username...", log: mechLog, type: .debug)
os_log("Requesting username...", log: coreLog, type: .debug)
guard let data = getContextData(key: kAuthorizationEnvironmentUsername) else {
return nil
}
guard let s = NSString.init(bytes: data.bytes,
length: data.length,
encoding: String.Encoding.utf8.rawValue)
else { return nil }
else { return nil }
return s.replacingOccurrences(of: "\0", with: "") as NSString
}
}

var password: NSString? {
get {
os_log("Requesting password...", log: mechLog, type: .debug)
os_log("Requesting password...", log: coreLog, type: .debug)
guard let data = getContextData(key: kAuthorizationEnvironmentPassword) else {
return nil
}
guard let s = NSString.init(bytes: data.bytes,
length: data.length,
encoding: String.Encoding.utf8.rawValue)
else { return nil }
else { return nil }
return s.replacingOccurrences(of: "\0", with: "") as NSString
}
}

var uid: uid_t {
get {
os_log("Requesting uid...", log: mechLog, type: .debug)
os_log("Requesting uid...", log: coreLog, type: .debug)
var uid: UInt32 = UInt32.max - 1 // nobody
guard let data = getContextData(key: kAuthorizationEnvironmentUID) else {
return uid
Expand All @@ -108,3 +108,4 @@ class CryptMechanism: NSObject {
}
}
}

8 changes: 4 additions & 4 deletions Crypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

/* Begin PBXBuildFile section */
1A8643121F15292200108C19 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A8643111F15292200108C19 /* Preferences.swift */; };
C7AD39001D22B3E3000FB736 /* CryptMechanism.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7AD38FF1D22B3E3000FB736 /* CryptMechanism.swift */; };
D2D31D5D1C2303F500839D93 /* CryptAuthPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = D2D31D511C2303F500839D93 /* CryptAuthPlugin.m */; };
D2D31D5F1C2303F500839D93 /* Check.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D31D541C2303F500839D93 /* Check.swift */; };
D50ACC7C2A6C797E00F8E62D /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50ACC7B2A6C797E00F8E62D /* main.swift */; };
Expand All @@ -17,6 +16,7 @@
D50F65B32B8CEF27008887DB /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50F65B22B8CEF27008887DB /* Logging.swift */; };
D54BDA342C31F4190028425F /* Filevault.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54BDA332C31F4190028425F /* Filevault.swift */; };
D579C9312D309A1F00FB6802 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = D579C9302D309A1F00FB6802 /* ArgumentParser */; };
D5C75AA72D4C06E900C77CA0 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5C75AA62D4C06E900C77CA0 /* Core.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -53,7 +53,6 @@

/* Begin PBXFileReference section */
1A8643111F15292200108C19 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
C7AD38FF1D22B3E3000FB736 /* CryptMechanism.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = CryptMechanism.swift; sourceTree = "<group>"; tabWidth = 2; };
D2D31D4F1C2303F500839D93 /* Crypt-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Crypt-Bridging-Header.h"; path = "Crypt/Crypt-Bridging-Header.h"; sourceTree = "<group>"; };
D2D31D501C2303F500839D93 /* CryptAuthPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CryptAuthPlugin.h; path = Crypt/CryptAuthPlugin.h; sourceTree = "<group>"; };
D2D31D511C2303F500839D93 /* CryptAuthPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CryptAuthPlugin.m; path = Crypt/CryptAuthPlugin.m; sourceTree = "<group>"; };
Expand All @@ -65,6 +64,7 @@
D50F65B02B8CE9C4008887DB /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = "<group>"; };
D50F65B22B8CEF27008887DB /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
D54BDA332C31F4190028425F /* Filevault.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filevault.swift; sourceTree = "<group>"; };
D5C75AA62D4C06E900C77CA0 /* Core.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Core.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -89,6 +89,7 @@
1A8643131F15294900108C19 /* Utility */ = {
isa = PBXGroup;
children = (
D5C75AA62D4C06E900C77CA0 /* Core.swift */,
1A8643111F15292200108C19 /* Preferences.swift */,
D50F65B02B8CE9C4008887DB /* Keychain.swift */,
D50F65B22B8CEF27008887DB /* Logging.swift */,
Expand All @@ -100,7 +101,6 @@
D2D31D531C2303F500839D93 /* Mechanisms */ = {
isa = PBXGroup;
children = (
C7AD38FF1D22B3E3000FB736 /* CryptMechanism.swift */,
D2D31D541C2303F500839D93 /* Check.swift */,
);
name = Mechanisms;
Expand Down Expand Up @@ -272,7 +272,7 @@
D54BDA342C31F4190028425F /* Filevault.swift in Sources */,
D2D31D5D1C2303F500839D93 /* CryptAuthPlugin.m in Sources */,
D50F65B12B8CE9C4008887DB /* Keychain.swift in Sources */,
C7AD39001D22B3E3000FB736 /* CryptMechanism.swift in Sources */,
D5C75AA72D4C06E900C77CA0 /* Core.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
96 changes: 17 additions & 79 deletions Crypt/Mechanisms/Check.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,31 @@ import Security
import CoreFoundation
import os.log

class Check: CryptMechanism {
// Log for the Check functions
private static let log = OSLog(subsystem: "com.grahamgilbert.crypt", category: "Check")

// Preference bundle id
fileprivate let bundleid = "com.grahamgilbert.crypt"
class Check: Crypt {

@objc func run() {
os_log("Starting run of Crypt.Check...", log: Check.log, type: .default)
os_log("Starting run of Crypt.Check...", log: checkLog, type: .default)

// check for ServerUrl
let serverURL = (getPref(key: .ServerURL) as? NSString) ?? nil

// check for SkipUsers Preference
let skipUsers: Bool = getSkipUsers()

guard let username = self.username
else { allowLogin(); return }
guard let password = self.password
else { allowLogin(); return }
let the_settings = NSDictionary.init(dictionary: ["Username": username, "Password": password])

// check for SkipUsers Preference
let skipUsers: Bool = getSkipUsers(username: username as String)
// Get status on encryption.
let fdestatus = getFVEnabled()
let fvEnabled: Bool = fdestatus.encrypted
let decrypting: Bool = fdestatus.decrypting
let filepath = getPref(key: .OutputPath) as! String
os_log("OutPutPlist Preferences is set to %{public}@", log: Check.log, type: .default, String(describing: filepath))
os_log("OutPutPlist Preferences is set to %{public}@", log: checkLog, type: .default, String(describing: filepath))

if decrypting {
// If we are decrypting we can't do anything so we can just log in
os_log("We are Decrypting! Not much we can do, exiting for safety...", log: Check.log, type: .error)
os_log("We are Decrypting! Not much we can do, exiting for safety...", log: checkLog, type: .error)
allowLogin()
return
}
Expand All @@ -62,11 +55,11 @@ class Check: CryptMechanism {

// Check for RotateUsedKey Preference
let rotateKey = getPref(key: .RotateUsedKey) as! Bool
os_log("RotateUsedKey Preferences is set to %{public}@", log: Check.log, type: .default, String(describing: rotateKey))
os_log("RotateUsedKey Preferences is set to %{public}@", log: checkLog, type: .default, String(describing: rotateKey))

// Check for RemovePlist Preferences
let removePlist = getPref(key: .RemovePlist) as! Bool
os_log("RemovePlist Preferences is set to %{public}@", log: Check.log, type: .default, String(describing: removePlist))
os_log("RemovePlist Preferences is set to %{public}@", log: checkLog, type: .default, String(describing: removePlist))

let useKeychain = getPref(key: .StoreRecoveryKeyInKeychain) as! Bool

Expand All @@ -77,107 +70,52 @@ class Check: CryptMechanism {

if (!recoveryKeyExists && !removePlist && rotateKey) || generateKey {
if alreadyGeneratedKey {
os_log("We've already generated a new key. If you wish to generate another key please remove RotatedKey from preferences...", log: Check.log, type: .default)
os_log("We've already generated a new key. If you wish to generate another key please remove RotatedKey from preferences...", log: checkLog, type: .default)
allowLogin()
return
}
// If key is missing from disk and we aren't supposed to remove it we should generate a new key...
os_log("Conditions for making a new key have been met. Attempting to generate a new key...", log: Check.log, type: .default)
os_log("Conditions for making a new key have been met. Attempting to generate a new key...", log: checkLog, type: .default)
do {
try _ = rotateRecoveryKey(the_settings, filepath: filepath)
} catch let error as NSError {
os_log("Caught error trying to rotate recovery key: %{public}@", log: Check.log, type: .error, error.localizedDescription)
os_log("Caught error trying to rotate recovery key: %{public}@", log: checkLog, type: .error, error.localizedDescription)
allowLogin()
return
}

if generateKey {
os_log("We've rotated the key and GenerateNewKey was True, setting to RotatedKey to avoid multiple generations", log: Check.log, type: .default)
os_log("We've rotated the key and GenerateNewKey was True, setting to RotatedKey to avoid multiple generations", log: checkLog, type: .default)
// set to false for house keeping, setPref will also sync to disk
_ = setPref(key: .RotatedKey, value: true)
}
allowLogin()
return
}

os_log("All checks for an encrypted machine have passed, Allowing Login...", log: Check.log, type: .default)
os_log("All checks for an encrypted machine have passed, Allowing Login...", log: checkLog, type: .default)
allowLogin()
return
// end of fvEnabled
} else if skipUsers {
os_log("Logged in User is in the Skip List... Not enforcing FileVault...", log: Check.log, type: .error)
os_log("Logged in User is in the Skip List... Not enforcing FileVault...", log: checkLog, type: .error)

allowLogin()
return
} else if serverURL == nil {
// Should we acutally do this?
os_log("Couldn't find ServerURL Pref choosing not to enable FileVault...", log: Check.log, type: .error)
os_log("Couldn't find ServerURL Pref choosing not to enable FileVault...", log: checkLog, type: .error)
allowLogin()
return
}
os_log("FileVault is not enabled, attempting to enable...", log: Check.log, type: .default)
os_log("FileVault is not enabled, attempting to enable...", log: checkLog, type: .default)
do {
try _ = enableFileVault(the_settings, filepath: filepath)
} catch let error as NSError {
os_log("Caught error trying to Enable FileVault: %{public}@", log: Check.log, type: .error, String(describing: error.localizedDescription))
os_log("Caught error trying to Enable FileVault: %{public}@", log: checkLog, type: .error, String(describing: error.localizedDescription))
}

allowLogin()
return
}

// fdesetup Errors
enum FileVaultError: Error {
case fdeSetupFailed(retCode: Int32)
case outputPlistNull
case outputPlistMalformed
}

fileprivate func getUsedKey() -> Bool {
let task = Process()
task.launchPath = "/usr/bin/fdesetup"
task.arguments = ["usingrecoverykey"]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard let output: String = String(data: data, encoding: String.Encoding.utf8)
else { return false }
return (output.range(of: "true") != nil)
}

func trim_string(_ the_string: String) -> String {
let output = the_string.trimmingCharacters(
in: CharacterSet.whitespacesAndNewlines)
os_log("Trimming %{public}@ to %{public}@", log: Check.log, type: .default, String(describing: the_string), String(describing: output))
return output
}

fileprivate func getSkipUsers() -> Bool {
os_log("Checking for any SkipUsers...", log: Check.log, type: .default)
guard let username = self.username
else {
os_log("Cannot get username", log: Check.log, type: .error)
return false
}
os_log("Username is %{public}@...", log: Check.log, type: .error, String(describing: username))

if username as String == "_mbsetupuser" {
os_log("User is _mbsetupuser... Need to Skip...", log: Check.log, type: .error)
return true
}

if username as String == "root" {
os_log("User is root... Need to Skip...", log: Check.log, type: .error)
return true
}
if let skipUsers = getPref(key: .SkipUsers) as? [String] {
for user in skipUsers {
if trim_string(user) == username as String {
return true
}
}
}
return false
}
}
Loading

0 comments on commit 5f25bc5

Please sign in to comment.