mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-08-30 17:10:56 +00:00
WIP
This commit is contained in:
parent
c32ceb6ad8
commit
a8662c9a2f
@ -11,7 +11,6 @@ public final class Agent: Sendable {
|
||||
private let witness: SigningWitness?
|
||||
private let publicKeyWriter = OpenSSHPublicKeyWriter()
|
||||
private let signatureWriter = OpenSSHSignatureWriter()
|
||||
private let requestTracer = SigningRequestTracer()
|
||||
private let certificateHandler = OpenSSHCertificateHandler()
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Agent")
|
||||
|
||||
@ -34,28 +33,24 @@ extension Agent {
|
||||
|
||||
/// Handles an incoming request.
|
||||
/// - Parameters:
|
||||
/// - reader: A ``FileHandleReader`` to read the content of the request.
|
||||
/// - writer: A ``FileHandleWriter`` to write the response to.
|
||||
/// - Return value:
|
||||
/// - Boolean if data could be read
|
||||
@discardableResult public func handle(reader: FileHandleReader, writer: FileHandleWriter) async -> Bool {
|
||||
/// // FIXME: UPDATE DOCS
|
||||
public func handle(data: Data, provenance: SigningRequestProvenance) async throws -> Data {
|
||||
logger.debug("Agent handling new data")
|
||||
let data = Data(reader.availableData)
|
||||
guard data.count > 4 else { return false}
|
||||
guard data.count > 4 else {
|
||||
throw AgentError.couldNotRead
|
||||
}
|
||||
let requestTypeInt = data[4]
|
||||
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
|
||||
writer.write(SSHAgent.ResponseType.agentFailure.data.lengthAndData)
|
||||
logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
||||
return true
|
||||
return SSHAgent.ResponseType.agentFailure.data.lengthAndData
|
||||
}
|
||||
logger.debug("Agent handling request of type \(requestType.debugDescription)")
|
||||
let subData = Data(data[5...])
|
||||
let response = await handle(requestType: requestType, data: subData, reader: reader)
|
||||
writer.write(response)
|
||||
return true
|
||||
let response = await handle(requestType: requestType, data: subData, provenance: provenance)
|
||||
return response
|
||||
}
|
||||
|
||||
func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) async -> Data {
|
||||
func handle(requestType: SSHAgent.RequestType, data: Data, provenance: SigningRequestProvenance) async -> Data {
|
||||
// Depending on the launch context (such as after macOS update), the agent may need to reload secrets before acting
|
||||
await reloadSecretsIfNeccessary()
|
||||
var response = Data()
|
||||
@ -66,7 +61,6 @@ extension Agent {
|
||||
response.append(await identities())
|
||||
logger.debug("Agent returned \(SSHAgent.ResponseType.agentIdentitiesAnswer.debugDescription)")
|
||||
case .signRequest:
|
||||
let provenance = requestTracer.provenance(from: reader)
|
||||
response.append(SSHAgent.ResponseType.agentSignResponse.data)
|
||||
response.append(try await sign(data: data, provenance: provenance))
|
||||
logger.debug("Agent returned \(SSHAgent.ResponseType.agentSignResponse.debugDescription)")
|
||||
@ -184,6 +178,7 @@ extension Agent {
|
||||
|
||||
/// An error involving agent operations..
|
||||
enum AgentError: Error {
|
||||
case couldNotRead
|
||||
case unhandledType
|
||||
case noMatchingKey
|
||||
case unsupportedKeyType
|
||||
|
@ -1,19 +1,21 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import SecretKit
|
||||
|
||||
/// A controller that manages socket configuration and request dispatching.
|
||||
public final class SocketController {
|
||||
|
||||
/// The active FileHandle.
|
||||
private var fileHandle: FileHandle?
|
||||
/// The active SocketPort.
|
||||
private var port: SocketPort?
|
||||
/// A handler that will be notified when a new read/write handle is available.
|
||||
/// False if no data could be read
|
||||
public var handler: (@Sendable (FileHandleReader, FileHandleWriter) async -> Bool)?
|
||||
public var handler: OSAllocatedUnfairLock<(@Sendable (Data, SigningRequestProvenance) async throws -> Data)?> = .init(initialState: nil)
|
||||
/// Logger.
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "SocketController")
|
||||
|
||||
private let requestTracer = SigningRequestTracer()
|
||||
|
||||
// Async sequence of message?
|
||||
|
||||
/// Initializes a socket controller with a specified path.
|
||||
/// - Parameter path: The path to use as a socket.
|
||||
@ -34,10 +36,43 @@ public final class SocketController {
|
||||
/// - Parameter path: The path to use as a socket.
|
||||
func configureSocket(at path: String) {
|
||||
guard let port = port else { return }
|
||||
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionAccept(notification:)), name: .NSFileHandleConnectionAccepted, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionDataAvailable(notification:)), name: .NSFileHandleDataAvailable, object: nil)
|
||||
fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.Mode.common])
|
||||
let fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
||||
fileHandle.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.Mode.common])
|
||||
Task { [handler, logger] in
|
||||
for await notification in NotificationCenter.default.notifications(named: .NSFileHandleConnectionAccepted) {
|
||||
logger.debug("Socket controller accepted connection")
|
||||
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { continue }
|
||||
let provenance = SigningRequestTracer().provenance(from: new)
|
||||
guard let handler = handler.withLock({ $0 }) else {
|
||||
// FIXME: THIS
|
||||
fatalError()
|
||||
}
|
||||
let response = try await handler(Data(fileHandle.availableData), provenance)
|
||||
try fileHandle.write(contentsOf: response)
|
||||
await new.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||
await fileHandle.acceptConnectionInBackgroundAndNotifyOnMainActor()
|
||||
}
|
||||
}
|
||||
Task { [logger, handler] in
|
||||
for await notification in NotificationCenter.default.notifications(named: .NSFileHandleDataAvailable) {
|
||||
logger.debug("Socket controller has new data available")
|
||||
guard let new = notification.object as? FileHandle else { return }
|
||||
logger.debug("Socket controller received new file handle")
|
||||
guard let handler = handler.withLock({ $0 }) else {
|
||||
// FIXME: THIS
|
||||
fatalError()
|
||||
}
|
||||
do {
|
||||
let provenance = SigningRequestTracer().provenance(from: new)
|
||||
let response = try await handler(Data(fileHandle.availableData), provenance)
|
||||
try fileHandle.write(contentsOf: response)
|
||||
logger.debug("Socket controller handled data, wait for more data")
|
||||
await new.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||
} catch {
|
||||
logger.debug("Socket controller called with empty data, socked closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a SocketPort for a path.
|
||||
@ -64,34 +99,6 @@ public final class SocketController {
|
||||
return SocketPort(protocolFamily: AF_UNIX, socketType: SOCK_STREAM, protocol: 0, address: data)!
|
||||
}
|
||||
|
||||
/// Handles a new connection being accepted, invokes the handler, and prepares to accept new connections.
|
||||
/// - Parameter notification: A `Notification` that triggered the call.
|
||||
@objc func handleConnectionAccept(notification: Notification) {
|
||||
logger.debug("Socket controller accepted connection")
|
||||
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
|
||||
Task { [handler, fileHandle] in
|
||||
_ = await handler?(new, new)
|
||||
await new.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||
await fileHandle?.acceptConnectionInBackgroundAndNotifyOnMainActor()
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles a new connection providing data and invokes the handler callback.
|
||||
/// - Parameter notification: A `Notification` that triggered the call.
|
||||
@objc func handleConnectionDataAvailable(notification: Notification) {
|
||||
logger.debug("Socket controller has new data available")
|
||||
guard let new = notification.object as? FileHandle else { return }
|
||||
logger.debug("Socket controller received new file handle")
|
||||
Task { [handler, logger = logger] in
|
||||
if((await handler?(new, new)) == true) {
|
||||
logger.debug("Socket controller handled data, wait for more data")
|
||||
await new.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||
} else {
|
||||
logger.debug("Socket controller called with empty data, socked closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FileHandle {
|
||||
|
@ -33,11 +33,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
logger.debug("SecretAgent finished launching")
|
||||
Task { @MainActor in
|
||||
socketController.handler = { [agent] reader, writer in
|
||||
await agent.handle(reader: reader, writer: writer)
|
||||
}
|
||||
}
|
||||
socketController.handler.withLock { [agent] in $0 = agent.handle }
|
||||
// Task {
|
||||
// for await (message, response) in socketController.messages {
|
||||
// let handled =
|
||||
// }
|
||||
// }
|
||||
Task {
|
||||
for await _ in NotificationCenter.default.notifications(named: .secretStoreReloaded) {
|
||||
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true)
|
||||
|
Loading…
Reference in New Issue
Block a user