CH-J Server Managerspráva serverů přes SSH
Menu
Vydané zdroje

CH-J Server Manager

Procházejte adresáře a soubory konkrétního vydání aplikace.

Stáhnout zdroje ZIP
CH-J Proprietary Software License 1.14

Zdroje jsou zveřejněny pod CH-J Proprietary Software License 1.14. Jejich dostupnost nemění licenční podmínky ani neposkytuje další oprávnění.

4,8 KB · 85 řádkůStáhnout soubor
1import Foundation
2import Security
3import LocalAuthentication
5struct Failure: Error { let code: String }
6let service = "de.ch-j.servermanager.vault.biometry.v1"
7func fail(_ code: String) throws -> Never { throw Failure(code: code) }
8func authenticate(_ reason: String) throws {
9 let context = LAContext()
10 context.localizedFallbackTitle = "" // Master-password fallback is in the application.
11 let semaphore = DispatchSemaphore(value: 0)
12 var accepted = false
13 var failure: Error?
14 context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { ok, error in
15 accepted = ok; failure = error; semaphore.signal()
16 }
17 if semaphore.wait(timeout: .now() + 60) == .timedOut { context.invalidate(); try fail("BIOMETRIC_TIMEOUT") }
18 if !accepted {
19 let code = (failure as? LAError)?.code
20 if code == .userCancel || code == .appCancel || code == .systemCancel { try fail("BIOMETRIC_CANCELLED") }
21 if code == .biometryLockout { try fail("BIOMETRIC_DEVICE_UNAVAILABLE") }
22 try fail("BIOMETRIC_FAILED")
23 }
25func keychainFailure(_ status: OSStatus) throws -> Never {
26 if status == errSecUserCanceled { try fail("BIOMETRIC_CANCELLED") }
27 if status == errSecItemNotFound { try fail("BIOMETRIC_ENROLLMENT_INVALIDATED") }
28 if status == errSecNotAvailable || status == errSecInteractionNotAllowed { try fail("BIOMETRIC_DEVICE_UNAVAILABLE") }
29 try fail("BIOMETRIC_CREDENTIAL_FAILED")
31func run() throws -> [String: Any] {
32 let input = FileHandle.standardInput.readDataToEndOfFile()
33 guard input.count <= 4096, let request = try JSONSerialization.jsonObject(with: input) as? [String: Any], let op = request["op"] as? String else { try fail("BIOMETRIC_FAILED") }
34 let reason = request["reason"] as? String ?? "Unlock CH-J Server Manager"
35 if op == "status" {
36 let context = LAContext(); var error: NSError?
37 let available = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
38 let code = available ? "BIOMETRIC_AVAILABLE" : error?.code == LAError.biometryNotEnrolled.rawValue ? "BIOMETRIC_NO_ENROLLMENT" : error?.code == LAError.biometryLockout.rawValue ? "BIOMETRIC_DEVICE_UNAVAILABLE" : "BIOMETRIC_UNAVAILABLE"
39 return ["ok": true, "available": available, "code": code]
40 }
41 if op == "authenticate" { try authenticate(reason); return ["ok": true] }
42 guard let entry = request["entryId"] as? String, entry.count == 64, entry.allSatisfy({ $0.isHexDigit && !$0.isUppercase }) else { try fail("BIOMETRIC_FAILED") }
43 var query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: entry]
44 if op == "remove" {
45 let status = SecItemDelete(query as CFDictionary)
46 if status != errSecSuccess && status != errSecItemNotFound { try keychainFailure(status) }
47 return ["ok": true]
48 }
49 if op == "store" {
50 guard let encoded = request["key"] as? String, var key = Data(base64Encoded: encoded), key.count == 32 else { try fail("BIOMETRIC_INVALID_KEY") }
51 defer { key.resetBytes(in: 0..<key.count) }
52 var error: Unmanaged<CFError>?
53 guard let access = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, .biometryCurrentSet, &error) else { try fail("BIOMETRIC_UNAVAILABLE") }
54 query[kSecAttrAccessControl as String] = access
55 query[kSecValueData as String] = key
56 query[kSecAttrSynchronizable as String] = false
57 let status = SecItemAdd(query as CFDictionary, nil)
58 if status != errSecSuccess { try keychainFailure(status) }
59 return ["ok": true]
60 }
61 if op == "retrieve" {
62 let context = LAContext(); context.localizedFallbackTitle = ""
63 context.touchIDAuthenticationAllowableReuseDuration = 0
64 context.localizedReason = reason
65 query[kSecUseAuthenticationContext as String] = context
66 query[kSecReturnData as String] = true
67 query[kSecMatchLimit as String] = kSecMatchLimitOne
68 var result: CFTypeRef?
69 let status = SecItemCopyMatching(query as CFDictionary, &result)
70 if status != errSecSuccess { try keychainFailure(status) }
71 guard var key = result as? Data, key.count == 32 else { try fail("BIOMETRIC_INVALID_KEY") }
72 defer { key.resetBytes(in: 0..<key.count) }
73 return ["ok": true, "key": key.base64EncodedString()]
74 }
75 try fail("BIOMETRIC_FAILED")
77do {
78 let value = try run()
79 FileHandle.standardOutput.write(try JSONSerialization.data(withJSONObject: value)); exit(0)
80} catch {
81 let code = (error as? Failure)?.code ?? "BIOMETRIC_FAILED"
82 let result = ["ok": false, "code": code] as [String: Any]
83 if let data = try? JSONSerialization.data(withJSONObject: result) { FileHandle.standardOutput.write(data) }
84 exit(1)

SHA-256: 2cbf9359dcf0d0ecad2f5558854d052d73a7f47dfd397510ed49f9cb4abba0f9

SHA-256 archivu: 5ac91caf4fa32a6fdb114f2430deed486fbe7489d5eea343d1f034169fafb5e0