From 2b5fdf541db4d35806234e41593a2abc945b94d3 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Tue, 17 Mar 2020 00:56:55 -0700 Subject: [PATCH] Request Attribution (#59) --- SecretAgent/AppDelegate.swift | 2 +- SecretAgent/Notifier.swift | 8 ++-- SecretAgentKit/Agent.swift | 10 +++-- SecretAgentKit/SigningRequestProvenance.swift | 39 +++++++++++++++++++ SecretAgentKit/SigningRequestTracer.swift | 35 +++++++++++++++++ SecretAgentKit/SigningWitness.swift | 2 +- Secretive.xcodeproj/project.pbxproj | 8 ++++ 7 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 SecretAgentKit/SigningRequestProvenance.swift create mode 100644 SecretAgentKit/SigningRequestTracer.swift diff --git a/SecretAgent/AppDelegate.swift b/SecretAgent/AppDelegate.swift index 379b259..e022e6f 100644 --- a/SecretAgent/AppDelegate.swift +++ b/SecretAgent/AppDelegate.swift @@ -14,7 +14,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { }() let notifier = Notifier() lazy var agent: Agent = { - Agent(storeList: storeList/*, notifier: notifier*/) + Agent(storeList: storeList, witness: notifier) }() lazy var socketController: SocketController = { let path = (NSHomeDirectory() as NSString).appendingPathComponent("socket.ssh") as String diff --git a/SecretAgent/Notifier.swift b/SecretAgent/Notifier.swift index b759f8a..a558e77 100644 --- a/SecretAgent/Notifier.swift +++ b/SecretAgent/Notifier.swift @@ -11,11 +11,11 @@ class Notifier { } } - func notify(accessTo secret: AnySecret) { + func notify(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) { let notificationCenter = UNUserNotificationCenter.current() let notificationContent = UNMutableNotificationContent() notificationContent.title = "Signed Request" - notificationContent.body = "\(secret.name) was used to sign a request." + notificationContent.body = "\(secret.name) was used to sign a request from \(provenance.origin.name)." let request = UNNotificationRequest(identifier: UUID().uuidString, content: notificationContent, trigger: nil) notificationCenter.add(request, withCompletionHandler: nil) } @@ -24,8 +24,8 @@ class Notifier { extension Notifier: SigningWitness { - func witness(accessTo secret: AnySecret) throws { - notify(accessTo: secret) + func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws { + notify(accessTo: secret, by: provenance) } } diff --git a/SecretAgentKit/Agent.swift b/SecretAgentKit/Agent.swift index 73d6240..2da04fd 100644 --- a/SecretAgentKit/Agent.swift +++ b/SecretAgentKit/Agent.swift @@ -2,12 +2,15 @@ import Foundation import CryptoKit import OSLog import SecretKit +import SecretAgentKit +import AppKit public class Agent { fileprivate let storeList: SecretStoreList fileprivate let witness: SigningWitness? fileprivate let writer = OpenSSHKeyWriter() + fileprivate let requestTracer = SigningRequestTracer() public init(storeList: SecretStoreList, witness: SigningWitness? = nil) { os_log(.debug, "Agent is running") @@ -40,7 +43,7 @@ extension Agent { os_log(.debug, "Agent returned %@", SSHAgent.ResponseType.agentIdentitiesAnswer.debugDescription) case .signRequest: response.append(SSHAgent.ResponseType.agentSignResponse.data) - response.append(try sign(data: data)) + response.append(try sign(data: data, from: fileHandle.fileDescriptor)) os_log(.debug, "Agent returned %@", SSHAgent.ResponseType.agentSignResponse.debugDescription) } } catch { @@ -74,7 +77,7 @@ extension Agent { return countData + keyData } - func sign(data: Data) throws -> Data { + func sign(data: Data, from pid: Int32) throws -> Data { let reader = OpenSSHReader(data: data) let hash = try reader.readNextChunk() guard let (store, secret) = secret(matching: hash) else { @@ -82,8 +85,9 @@ extension Agent { throw AgentError.noMatchingKey } + let provenance = requestTracer.provenance(from: pid) if let witness = witness { - try witness.witness(accessTo: secret) + try witness.witness(accessTo: secret, by: provenance) } let dataToSign = try reader.readNextChunk() diff --git a/SecretAgentKit/SigningRequestProvenance.swift b/SecretAgentKit/SigningRequestProvenance.swift new file mode 100644 index 0000000..a60ef77 --- /dev/null +++ b/SecretAgentKit/SigningRequestProvenance.swift @@ -0,0 +1,39 @@ +import Foundation +import AppKit + +public struct SigningRequestProvenance { + + public var chain: [Process] + public init(root: Process) { + self.chain = [root] + } + +} + +extension SigningRequestProvenance { + + public var origin: Process { + chain.last! + } + +} + +extension SigningRequestProvenance { + + public struct Process { + + public let pid: Int32 + public let name: String + public let path: String + let parentPID: Int32? + + init(pid: Int32, name: String, path: String, parentPID: Int32?) { + self.pid = pid + self.name = name + self.path = path + self.parentPID = parentPID + } + + } + +} diff --git a/SecretAgentKit/SigningRequestTracer.swift b/SecretAgentKit/SigningRequestTracer.swift new file mode 100644 index 0000000..eeca1a5 --- /dev/null +++ b/SecretAgentKit/SigningRequestTracer.swift @@ -0,0 +1,35 @@ +import Foundation +import AppKit + +struct SigningRequestTracer { + + func provenance(from pid: Int32) -> SigningRequestProvenance { + let pidPointer = UnsafeMutableRawPointer.allocate(byteCount: 4, alignment: 1) + var len = socklen_t(MemoryLayout.size) + getsockopt(pid, SOCK_STREAM, LOCAL_PEERPID, pidPointer, &len) + let pid = pidPointer.load(as: Int32.self) + let firstInfo = process(from: pid) + + var provenance = SigningRequestProvenance(root: firstInfo) + while NSRunningApplication(processIdentifier: provenance.origin.pid) == nil && provenance.origin.parentPID != nil { + provenance.chain.append(process(from: provenance.origin.parentPID!)) + } + return provenance + } + + func pidAndNameInfo(from pid: Int32) -> kinfo_proc { + var len = MemoryLayout.size + let infoPointer = UnsafeMutableRawPointer.allocate(byteCount: len, alignment: 1) + var name: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid] + sysctl(&name, UInt32(name.count), infoPointer, &len, nil, 0) + return infoPointer.load(as: kinfo_proc.self) + } + + func process(from pid: Int32) -> SigningRequestProvenance.Process { + var pidAndNameInfo = self.pidAndNameInfo(from: pid) + let ppid = pidAndNameInfo.kp_eproc.e_ppid != 0 ? pidAndNameInfo.kp_eproc.e_ppid : nil + let procName = String(cString: &pidAndNameInfo.kp_proc.p_comm.0) + return SigningRequestProvenance.Process(pid: pid, name: procName, path: "", parentPID: ppid) + } + +} diff --git a/SecretAgentKit/SigningWitness.swift b/SecretAgentKit/SigningWitness.swift index 67577c3..ffc73e3 100644 --- a/SecretAgentKit/SigningWitness.swift +++ b/SecretAgentKit/SigningWitness.swift @@ -3,6 +3,6 @@ import SecretKit public protocol SigningWitness { - func witness(accessTo secret: AnySecret) throws + func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws } diff --git a/Secretive.xcodeproj/project.pbxproj b/Secretive.xcodeproj/project.pbxproj index d6261ef..e9c8698 100644 --- a/Secretive.xcodeproj/project.pbxproj +++ b/Secretive.xcodeproj/project.pbxproj @@ -35,6 +35,8 @@ 507CE4ED2420A3C70029F750 /* Agent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A3B79F24026B9900D209EA /* Agent.swift */; }; 507CE4EE2420A3CA0029F750 /* SocketController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A3B79D24026B9900D209EA /* SocketController.swift */; }; 507CE4F02420A4C50029F750 /* SigningWitness.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4EF2420A4C50029F750 /* SigningWitness.swift */; }; + 507CE4F42420A8C10029F750 /* SigningRequestProvenance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */; }; + 507CE4F62420A96F0029F750 /* SigningRequestTracer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */; }; 508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */; }; 508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */; }; 508A58B5241ED48F0069DC07 /* PreviewAgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B4241ED48F0069DC07 /* PreviewAgentStatusChecker.swift */; }; @@ -203,6 +205,8 @@ 50731665241DF8660023809E /* Updater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Updater.swift; sourceTree = ""; }; 50731668241E00C20023809E /* NoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeView.swift; sourceTree = ""; }; 507CE4EF2420A4C50029F750 /* SigningWitness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningWitness.swift; sourceTree = ""; }; + 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningRequestProvenance.swift; sourceTree = ""; }; + 507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningRequestTracer.swift; sourceTree = ""; }; 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewUpdater.swift; sourceTree = ""; }; 508A58AB241E121B0069DC07 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentStatusChecker.swift; sourceTree = ""; }; @@ -468,6 +472,8 @@ 5099A089240242C20062B6F2 /* SSHAgentProtocol.swift */, 50A3B79D24026B9900D209EA /* SocketController.swift */, 507CE4EF2420A4C50029F750 /* SigningWitness.swift */, + 507CE4F32420A8C10029F750 /* SigningRequestProvenance.swift */, + 507CE4F52420A96F0029F750 /* SigningRequestTracer.swift */, 50A3B79F24026B9900D209EA /* Agent.swift */, 5099A06F240242BA0062B6F2 /* Info.plist */, ); @@ -850,6 +856,8 @@ 5099A08A240242C20062B6F2 /* SSHAgentProtocol.swift in Sources */, 507CE4ED2420A3C70029F750 /* Agent.swift in Sources */, 507CE4F02420A4C50029F750 /* SigningWitness.swift in Sources */, + 507CE4F62420A96F0029F750 /* SigningRequestTracer.swift in Sources */, + 507CE4F42420A8C10029F750 /* SigningRequestProvenance.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };