mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-08-31 01:20:57 +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 witness: SigningWitness?
|
||||||
private let publicKeyWriter = OpenSSHPublicKeyWriter()
|
private let publicKeyWriter = OpenSSHPublicKeyWriter()
|
||||||
private let signatureWriter = OpenSSHSignatureWriter()
|
private let signatureWriter = OpenSSHSignatureWriter()
|
||||||
private let requestTracer = SigningRequestTracer()
|
|
||||||
private let certificateHandler = OpenSSHCertificateHandler()
|
private let certificateHandler = OpenSSHCertificateHandler()
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Agent")
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Agent")
|
||||||
|
|
||||||
@ -34,28 +33,24 @@ extension Agent {
|
|||||||
|
|
||||||
/// Handles an incoming request.
|
/// Handles an incoming request.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - reader: A ``FileHandleReader`` to read the content of the request.
|
/// // FIXME: UPDATE DOCS
|
||||||
/// - writer: A ``FileHandleWriter`` to write the response to.
|
public func handle(data: Data, provenance: SigningRequestProvenance) async throws -> Data {
|
||||||
/// - Return value:
|
|
||||||
/// - Boolean if data could be read
|
|
||||||
@discardableResult public func handle(reader: FileHandleReader, writer: FileHandleWriter) async -> Bool {
|
|
||||||
logger.debug("Agent handling new data")
|
logger.debug("Agent handling new data")
|
||||||
let data = Data(reader.availableData)
|
guard data.count > 4 else {
|
||||||
guard data.count > 4 else { return false}
|
throw AgentError.couldNotRead
|
||||||
|
}
|
||||||
let requestTypeInt = data[4]
|
let requestTypeInt = data[4]
|
||||||
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
|
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
|
||||||
writer.write(SSHAgent.ResponseType.agentFailure.data.lengthAndData)
|
|
||||||
logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
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)")
|
logger.debug("Agent handling request of type \(requestType.debugDescription)")
|
||||||
let subData = Data(data[5...])
|
let subData = Data(data[5...])
|
||||||
let response = await handle(requestType: requestType, data: subData, reader: reader)
|
let response = await handle(requestType: requestType, data: subData, provenance: provenance)
|
||||||
writer.write(response)
|
return response
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Depending on the launch context (such as after macOS update), the agent may need to reload secrets before acting
|
||||||
await reloadSecretsIfNeccessary()
|
await reloadSecretsIfNeccessary()
|
||||||
var response = Data()
|
var response = Data()
|
||||||
@ -66,7 +61,6 @@ extension Agent {
|
|||||||
response.append(await identities())
|
response.append(await identities())
|
||||||
logger.debug("Agent returned \(SSHAgent.ResponseType.agentIdentitiesAnswer.debugDescription)")
|
logger.debug("Agent returned \(SSHAgent.ResponseType.agentIdentitiesAnswer.debugDescription)")
|
||||||
case .signRequest:
|
case .signRequest:
|
||||||
let provenance = requestTracer.provenance(from: reader)
|
|
||||||
response.append(SSHAgent.ResponseType.agentSignResponse.data)
|
response.append(SSHAgent.ResponseType.agentSignResponse.data)
|
||||||
response.append(try await sign(data: data, provenance: provenance))
|
response.append(try await sign(data: data, provenance: provenance))
|
||||||
logger.debug("Agent returned \(SSHAgent.ResponseType.agentSignResponse.debugDescription)")
|
logger.debug("Agent returned \(SSHAgent.ResponseType.agentSignResponse.debugDescription)")
|
||||||
@ -184,6 +178,7 @@ extension Agent {
|
|||||||
|
|
||||||
/// An error involving agent operations..
|
/// An error involving agent operations..
|
||||||
enum AgentError: Error {
|
enum AgentError: Error {
|
||||||
|
case couldNotRead
|
||||||
case unhandledType
|
case unhandledType
|
||||||
case noMatchingKey
|
case noMatchingKey
|
||||||
case unsupportedKeyType
|
case unsupportedKeyType
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import OSLog
|
import OSLog
|
||||||
|
import SecretKit
|
||||||
|
|
||||||
/// A controller that manages socket configuration and request dispatching.
|
/// A controller that manages socket configuration and request dispatching.
|
||||||
public final class SocketController {
|
public final class SocketController {
|
||||||
|
|
||||||
/// The active FileHandle.
|
|
||||||
private var fileHandle: FileHandle?
|
|
||||||
/// The active SocketPort.
|
/// The active SocketPort.
|
||||||
private var port: SocketPort?
|
private var port: SocketPort?
|
||||||
/// A handler that will be notified when a new read/write handle is available.
|
/// A handler that will be notified when a new read/write handle is available.
|
||||||
/// False if no data could be read
|
/// 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.
|
/// Logger.
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "SocketController")
|
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.
|
/// Initializes a socket controller with a specified path.
|
||||||
/// - Parameter path: The path to use as a socket.
|
/// - 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.
|
/// - Parameter path: The path to use as a socket.
|
||||||
func configureSocket(at path: String) {
|
func configureSocket(at path: String) {
|
||||||
guard let port = port else { return }
|
guard let port = port else { return }
|
||||||
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
let fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionAccept(notification:)), name: .NSFileHandleConnectionAccepted, object: nil)
|
fileHandle.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.Mode.common])
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionDataAvailable(notification:)), name: .NSFileHandleDataAvailable, object: nil)
|
Task { [handler, logger] in
|
||||||
fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.Mode.common])
|
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.
|
/// 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)!
|
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 {
|
extension FileHandle {
|
||||||
|
@ -33,11 +33,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
logger.debug("SecretAgent finished launching")
|
logger.debug("SecretAgent finished launching")
|
||||||
Task { @MainActor in
|
socketController.handler.withLock { [agent] in $0 = agent.handle }
|
||||||
socketController.handler = { [agent] reader, writer in
|
// Task {
|
||||||
await agent.handle(reader: reader, writer: writer)
|
// for await (message, response) in socketController.messages {
|
||||||
}
|
// let handled =
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
Task {
|
Task {
|
||||||
for await _ in NotificationCenter.default.notifications(named: .secretStoreReloaded) {
|
for await _ in NotificationCenter.default.notifications(named: .secretStoreReloaded) {
|
||||||
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true)
|
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true)
|
||||||
|
Loading…
Reference in New Issue
Block a user