mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-09-15 08:50:57 +00:00
.
This commit is contained in:
parent
11074999ad
commit
2efba1bc21
@ -6,17 +6,37 @@ private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent.Age
|
||||
|
||||
func handleRequest(_ request: XPCListener.IncomingSessionRequest) -> XPCListener.IncomingSessionRequest.Decision {
|
||||
logger.log("Parser received inbound request")
|
||||
return request.accept { message in
|
||||
return request.accept { xpcDictionary in
|
||||
xpcDictionary.handoffReply(to: .global(qos: .userInteractive)) {
|
||||
logger.log("Parser accepted inbound request")
|
||||
do {
|
||||
let result = try SSHAgentInputParser().parse(data: message)
|
||||
let parser = SSHAgentInputParser()
|
||||
let result = try parser.parse(data: xpcDictionary.decode(as: Data.self))
|
||||
logger.log("Parser parsed message as type \(result.debugDescription)")
|
||||
return result
|
||||
} catch {
|
||||
xpcDictionary.reply(result)
|
||||
} catch let error as SSHAgentInputParser.AgentParsingError {
|
||||
logger.error("Parser failed with error \(error)")
|
||||
return nil
|
||||
xpcDictionary.reply(error)
|
||||
} catch {
|
||||
// This should never actually happen because SSHAgentInputParser is a typed thrower, but the type system doesn't seem to know that across framework boundaries?
|
||||
logger.error("Parser failed with unknown error \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct WrappedError<Wrapped: Codable & Error>: Codable {
|
||||
|
||||
public struct DescriptionOnlyError: Error, Codable {
|
||||
let localizedDescription: String
|
||||
}
|
||||
|
||||
public let wrapped: Wrapped
|
||||
|
||||
public init(_ error: Wrapped) {
|
||||
wrapped = error
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
do {
|
||||
|
@ -45,6 +45,8 @@ extension Agent {
|
||||
response.append(SSHAgent.Response.agentSignResponse.data)
|
||||
response.append(try await sign(data: context.dataToSign, keyBlob: context.keyBlob, provenance: provenance))
|
||||
logger.debug("Agent returned \(SSHAgent.Response.agentSignResponse.debugDescription)")
|
||||
case .unknown(let value):
|
||||
logger.error("Agent received unknown request of type \(value).")
|
||||
default:
|
||||
logger.debug("Agent received valid request of type \(request.debugDescription), but not currently supported.")
|
||||
throw UnhandledRequestError()
|
||||
|
@ -7,25 +7,25 @@ final class OpenSSHReader {
|
||||
|
||||
/// Initialize the reader with an OpenSSH data payload.
|
||||
/// - Parameter data: The data to read.
|
||||
public init(data: Data) {
|
||||
init(data: Data) {
|
||||
remaining = Data(data)
|
||||
}
|
||||
|
||||
/// Reads the next chunk of data from the playload.
|
||||
/// - Returns: The next chunk of data.
|
||||
public func readNextChunk(convertEndianness: Bool = true) throws -> Data {
|
||||
func readNextChunk(convertEndianness: Bool = true) throws(OpenSSHReaderError) -> Data {
|
||||
let littleEndianLength = try readNextBytes(as: UInt32.self)
|
||||
let length = convertEndianness ? Int(littleEndianLength.bigEndian) : Int(littleEndianLength)
|
||||
guard remaining.count >= length else { throw EndOfData() }
|
||||
guard remaining.count >= length else { throw .beyondBounds }
|
||||
let dataRange = 0..<length
|
||||
let ret = Data(remaining[dataRange])
|
||||
remaining.removeSubrange(dataRange)
|
||||
return ret
|
||||
}
|
||||
|
||||
public func readNextBytes<T>(as: T.Type) throws -> T {
|
||||
func readNextBytes<T>(as: T.Type) throws(OpenSSHReaderError) -> T {
|
||||
let size = MemoryLayout<T>.size
|
||||
guard remaining.count >= size else { throw EndOfData() }
|
||||
guard remaining.count >= size else { throw .beyondBounds }
|
||||
let lengthRange = 0..<size
|
||||
let lengthChunk = remaining[lengthRange]
|
||||
remaining.removeSubrange(lengthRange)
|
||||
@ -33,10 +33,12 @@ final class OpenSSHReader {
|
||||
}
|
||||
|
||||
|
||||
public func readNextChunkAsString() throws -> String {
|
||||
func readNextChunkAsString() throws -> String {
|
||||
try String(decoding: readNextChunk(), as: UTF8.self)
|
||||
}
|
||||
|
||||
public struct EndOfData: Error {}
|
||||
|
||||
}
|
||||
|
||||
public enum OpenSSHReaderError: Error, Codable {
|
||||
case beyondBounds
|
||||
}
|
||||
|
@ -16,10 +16,10 @@ public struct SSHAgentInputParser: SSHAgentInputParserProtocol {
|
||||
|
||||
}
|
||||
|
||||
public func parse(data: Data) throws -> SSHAgent.Request {
|
||||
public func parse(data: Data) throws(AgentParsingError) -> SSHAgent.Request {
|
||||
logger.debug("Parsing new data")
|
||||
guard data.count > 4 else {
|
||||
throw InvalidDataProvidedError()
|
||||
throw .invalidData
|
||||
}
|
||||
let specifiedLength = (data[0..<4].bytes.unsafeLoad(as: UInt32.self).bigEndian) + 4
|
||||
let rawRequestInt = data[4]
|
||||
@ -29,7 +29,11 @@ public struct SSHAgentInputParser: SSHAgentInputParserProtocol {
|
||||
case SSHAgent.Request.requestIdentities.protocolID:
|
||||
return .requestIdentities
|
||||
case SSHAgent.Request.signRequest(.empty).protocolID:
|
||||
do {
|
||||
return .signRequest(try signatureRequestContext(from: body))
|
||||
} catch {
|
||||
throw .openSSHReader(error)
|
||||
}
|
||||
case SSHAgent.Request.addIdentity.protocolID:
|
||||
return .addIdentity
|
||||
case SSHAgent.Request.removeIdentity.protocolID:
|
||||
@ -59,7 +63,7 @@ public struct SSHAgentInputParser: SSHAgentInputParserProtocol {
|
||||
|
||||
extension SSHAgentInputParser {
|
||||
|
||||
func signatureRequestContext(from data: Data) throws -> SSHAgent.Request.SignatureRequestContext {
|
||||
func signatureRequestContext(from data: Data) throws(OpenSSHReaderError) -> SSHAgent.Request.SignatureRequestContext {
|
||||
let reader = OpenSSHReader(data: data)
|
||||
let rawKeyBlob = try reader.readNextChunk()
|
||||
let keyBlob = certificatePublicKeyBlob(from: rawKeyBlob) ?? rawKeyBlob
|
||||
@ -95,8 +99,11 @@ extension SSHAgentInputParser {
|
||||
|
||||
extension SSHAgentInputParser {
|
||||
|
||||
struct AgentUnknownRequestError: Error {}
|
||||
struct AgentUnhandledRequestError: Error {}
|
||||
struct InvalidDataProvidedError: Error {}
|
||||
public enum AgentParsingError: Error, Codable {
|
||||
case unknownRequest
|
||||
case unhandledRequest
|
||||
case invalidData
|
||||
case openSSHReader(OpenSSHReaderError)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,13 +35,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
logger.debug("SecretAgent finished launching")
|
||||
Task {
|
||||
let inputParser = try XPCAgentInputParser()
|
||||
Task {
|
||||
try? await Task.sleep(for: .seconds(1))
|
||||
var len = (5 as UInt32).littleEndian
|
||||
var raw = SSHAgent.Request.requestIdentities.protocolID
|
||||
let data = Data(bytes: &len, count: MemoryLayout<UInt32>.size) + Data(bytes: &raw, count: MemoryLayout<UInt8>.size)
|
||||
print(try? await inputParser.parse(data: data))
|
||||
}
|
||||
for await session in socketController.sessions {
|
||||
Task {
|
||||
do {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import SecretAgentKit
|
||||
|
||||
/// Delegates all agent input parsing to an XPC service which wraps OpenSSH
|
||||
public final class XPCAgentInputParser: SSHAgentInputParserProtocol {
|
||||
|
||||
private let session: XPCSession
|
||||
@ -8,11 +9,15 @@ public final class XPCAgentInputParser: SSHAgentInputParserProtocol {
|
||||
|
||||
public init() throws {
|
||||
if #available(macOS 26.0, *) {
|
||||
session = try XPCSession(xpcService: "com.maxgoedjen.Secretive.AgentRequestParser", targetQueue: queue, options: .inactive, requirement: .isFromSameTeam())
|
||||
session = try XPCSession(xpcService: "com.maxgoedjen.Secretive.AgentRequestParser", targetQueue: queue, requirement: .isFromSameTeam())
|
||||
} else {
|
||||
session = try XPCSession(xpcService: "com.maxgoedjen.Secretive.AgentRequestParser", targetQueue: queue, options: .inactive)
|
||||
session = try XPCSession(xpcService: "com.maxgoedjen.Secretive.AgentRequestParser", targetQueue: queue)
|
||||
}
|
||||
Task {
|
||||
// Warm up the XPC endpoint.
|
||||
_ = try? await parse(data: Data())
|
||||
|
||||
}
|
||||
try session.activate()
|
||||
}
|
||||
|
||||
public func parse(data: Data) async throws -> SSHAgent.Request {
|
||||
@ -21,10 +26,12 @@ public final class XPCAgentInputParser: SSHAgentInputParserProtocol {
|
||||
try session.send(data) { result in
|
||||
switch result {
|
||||
case .success(let result):
|
||||
do {
|
||||
continuation.resume(returning: try result.decode(as: SSHAgent.Request.self))
|
||||
} catch {
|
||||
if let result = try? result.decode(as: SSHAgent.Request.self) {
|
||||
continuation.resume(returning: result)
|
||||
} else if let error = try? result.decode(as: SSHAgentInputParser.AgentParsingError.self) {
|
||||
continuation.resume(throwing: error)
|
||||
} else {
|
||||
continuation.resume(throwing: UnknownMessageError())
|
||||
}
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
@ -40,4 +47,6 @@ public final class XPCAgentInputParser: SSHAgentInputParserProtocol {
|
||||
session.cancel(reason: "Done")
|
||||
}
|
||||
|
||||
struct UnknownMessageError: Error {}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user