mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-06-23 05:08:58 +02:00
WIP
This commit is contained in:
@@ -36,11 +36,11 @@ import XPCWrappers
|
||||
self.currentVersion = currentVersion
|
||||
Task {
|
||||
if checkOnLaunch {
|
||||
try await checkForUpdates()
|
||||
try? await checkForUpdates()
|
||||
}
|
||||
while !Task.isCancelled {
|
||||
try? await Task.sleep(for: .seconds(Int(checkFrequency)))
|
||||
try await checkForUpdates()
|
||||
try? await checkForUpdates()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import launch
|
||||
|
||||
/// A controller that manages socket configuration and request dispatching.
|
||||
public struct SocketController {
|
||||
@@ -12,7 +13,8 @@ public struct SocketController {
|
||||
private let sessionsContinuation: AsyncStream<Session>.Continuation
|
||||
|
||||
/// The active SocketPort. Must be retained to be kept valid.
|
||||
private let port: SocketPort
|
||||
/// Only applicable for legacy non-launchd sockets.
|
||||
private let port: SocketPort?
|
||||
|
||||
/// The FileHandle for the main socket.
|
||||
private let fileHandle: FileHandle
|
||||
@@ -23,10 +25,15 @@ public struct SocketController {
|
||||
/// Tracer which determines who originates a socket connection.
|
||||
private let requestTracer = SigningRequestTracer()
|
||||
|
||||
/// Initializes a socket controller with a specified path.
|
||||
/// - Parameter path: The path to use as a socket.
|
||||
public init(path: String) {
|
||||
public enum Socket {
|
||||
case launchd(String)
|
||||
case path(String)
|
||||
}
|
||||
|
||||
public init(_ socket: Socket) {
|
||||
(sessions, sessionsContinuation) = AsyncStream<Session>.makeStream()
|
||||
switch socket {
|
||||
case .path(let path):
|
||||
logger.debug("Socket controller setting up at \(path)")
|
||||
if let _ = try? FileManager.default.removeItem(atPath: path) {
|
||||
logger.debug("Socket controller removed existing socket")
|
||||
@@ -34,8 +41,25 @@ public struct SocketController {
|
||||
let exists = FileManager.default.fileExists(atPath: path)
|
||||
assert(!exists)
|
||||
logger.debug("Socket controller path is clear")
|
||||
port = SocketPort(path: path)
|
||||
let port = SocketPort(path: path)
|
||||
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
||||
self.port = port
|
||||
logger.debug("Socket listening at \(path)")
|
||||
case .launchd(let name):
|
||||
logger.debug("Socket controller setting for launchd-controlled socket \(name)")
|
||||
port = nil
|
||||
var fileDescriptors: UnsafeMutablePointer<Int32>? = nil
|
||||
var count = 0
|
||||
let result = unsafe launch_activate_socket(name, &fileDescriptors, &count)
|
||||
guard result == kOSReturnSuccess, let socket = unsafe fileDescriptors?.pointee else {
|
||||
fatalError()
|
||||
}
|
||||
fileHandle = FileHandle(fileDescriptor: socket, closeOnDealloc: true)
|
||||
}
|
||||
listen()
|
||||
}
|
||||
|
||||
func listen() {
|
||||
Task { @MainActor [fileHandle, sessionsContinuation, logger] in
|
||||
// Create the sequence before triggering the notification to
|
||||
// ensure it will not be missed.
|
||||
@@ -51,7 +75,7 @@ public struct SocketController {
|
||||
fileHandle.acceptConnectionInBackgroundAndNotify()
|
||||
}
|
||||
}
|
||||
logger.debug("Socket listening at \(path)")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -94,7 +118,7 @@ extension SocketController {
|
||||
guard !data.isEmpty else {
|
||||
logger.debug("Socket controller received empty data, ending continuation.")
|
||||
messagesContinuation.finish()
|
||||
try fileHandle.close()
|
||||
try? fileHandle.close()
|
||||
return
|
||||
}
|
||||
messagesContinuation.yield(data)
|
||||
@@ -126,8 +150,8 @@ private extension SocketPort {
|
||||
convenience init(path: String) {
|
||||
var addr = sockaddr_un()
|
||||
|
||||
let length = unsafe withUnsafeMutablePointer(to: &addr.sun_path.0) { pointer in
|
||||
unsafe path.withCString { cstring in
|
||||
let length = withUnsafeMutablePointer(to: &addr.sun_path.0) { pointer in
|
||||
path.withCString { cstring in
|
||||
let len = unsafe strlen(cstring)
|
||||
unsafe strncpy(pointer, cstring, len)
|
||||
return len
|
||||
@@ -144,3 +168,7 @@ private extension SocketPort {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Changes the header from `UnsafeMutablePointer<UnsafeMutablePointer<Int32>?>?` -> `UnsafeMutablePointer<UnsafeMutablePointer<Int32>?>!`
|
||||
@_silgen_name("launch_activate_socket")
|
||||
func launch_activate_socket(_ name: UnsafePointer<CChar>, _ fds: UnsafeMutablePointer<UnsafeMutablePointer<Int32>?>!, _ cnt: UnsafeMutablePointer<Int>!) -> Int32
|
||||
|
||||
@@ -11,7 +11,7 @@ extension ProcessInfo {
|
||||
}
|
||||
|
||||
guard let value = SecTaskCopyValueForEntitlement(task, "com.apple.developer.team-identifier" as CFString, nil) as? String else {
|
||||
assertionFailure("SecTaskCopyValueForEntitlement(com.apple.developer.team-identifier) failed")
|
||||
// assertionFailure("SecTaskCopyValueForEntitlement(com.apple.developer.team-identifier) failed")
|
||||
return fallbackTeamID
|
||||
}
|
||||
|
||||
|
||||
@@ -41,10 +41,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
@MainActor private lazy var agent: Agent = {
|
||||
Agent(storeList: storeList, certificateStore: EnvironmentValues._certificateStore, witness: notifier)
|
||||
}()
|
||||
private lazy var socketController: SocketController = {
|
||||
let path = URL.socketPath as String
|
||||
return SocketController(path: path)
|
||||
}()
|
||||
private var shutdownTask: Task<Void, Error>?
|
||||
private let socketController = SocketController(.launchd("SecureListener"))
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "AppDelegate")
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
@@ -52,16 +50,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
Task {
|
||||
for await session in socketController.sessions {
|
||||
Task {
|
||||
let inputParser = try await XPCAgentInputParser()
|
||||
do {
|
||||
let inputParser = try await XPCAgentInputParser()
|
||||
for await message in session.messages {
|
||||
let request = try await inputParser.parse(data: message)
|
||||
let agentResponse = await agent.handle(request: request, provenance: session.provenance)
|
||||
try session.write(agentResponse)
|
||||
}
|
||||
} catch {
|
||||
try session.close()
|
||||
try? session.close()
|
||||
}
|
||||
startCountdownClock()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,5 +89,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func startCountdownClock() {
|
||||
// FIXME: ACCOUNT FOR STORED AUTH
|
||||
logger.log("Beginning countdown clock")
|
||||
shutdownTask?.cancel()
|
||||
shutdownTask = Task { [logger] in
|
||||
try await Task.sleep(for: .seconds(30))
|
||||
logger.log("Shutting down")
|
||||
await NSApplication.shared.terminate(nil)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
<string>1</string>
|
||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||
<true/>
|
||||
<key>com.apple.security.smartcard</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
|
||||
<string>2</string>
|
||||
<key>com.apple.security.smartcard</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
5008C23E2E525D8900507AC2 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 5008C23D2E525D8200507AC2 /* Localizable.xcstrings */; };
|
||||
5008C2412E52D18700507AC2 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 5008C23D2E525D8200507AC2 /* Localizable.xcstrings */; };
|
||||
501421622781262300BBAA70 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501421612781262300BBAA70 /* Brief */; };
|
||||
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
501421652781268000BBAA70 /* SecretAgent.app in Copy SecretAgent */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; };
|
||||
50153E22250DECA300525160 /* SecretListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListItemView.swift */; };
|
||||
501578132E6C0479004A37D0 /* XPCInputParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501578122E6C0479004A37D0 /* XPCInputParser.swift */; };
|
||||
5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; };
|
||||
502452F92FE2026E009EE753 /* com.maxgoedjen.Secretive.SecretAgent.plist in Copy SecretAgent plist */ = {isa = PBXBuildFile; fileRef = 502452F32FE1FF89009EE753 /* com.maxgoedjen.Secretive.SecretAgent.plist */; };
|
||||
504788F22E681F3A00B4556F /* Instructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F12E681F3A00B4556F /* Instructions.swift */; };
|
||||
504788F42E681F6900B4556F /* ToolConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F32E681F6900B4556F /* ToolConfigurationView.swift */; };
|
||||
504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F52E68206F00B4556F /* GettingStartedView.swift */; };
|
||||
@@ -181,6 +182,17 @@
|
||||
name = "Embed XPC Services";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
502452F82FE2024D009EE753 /* Copy SecretAgent plist */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = Contents/Library/LaunchAgents;
|
||||
dstSubfolderSpec = 1;
|
||||
files = (
|
||||
502452F92FE2026E009EE753 /* com.maxgoedjen.Secretive.SecretAgent.plist in Copy SecretAgent plist */,
|
||||
);
|
||||
name = "Copy SecretAgent plist";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
50617DBF23FCE4AB0099B055 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -201,14 +213,15 @@
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
50C385AF240E438B00AF2719 /* CopyFiles */ = {
|
||||
50C385AF240E438B00AF2719 /* Copy SecretAgent */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = Contents/Library/LoginItems;
|
||||
dstSubfolderSpec = 1;
|
||||
files = (
|
||||
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */,
|
||||
501421652781268000BBAA70 /* SecretAgent.app in Copy SecretAgent */,
|
||||
);
|
||||
name = "Copy SecretAgent";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
@@ -224,6 +237,7 @@
|
||||
50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = "<group>"; };
|
||||
501578122E6C0479004A37D0 /* XPCInputParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCInputParser.swift; sourceTree = "<group>"; };
|
||||
5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; };
|
||||
502452F32FE1FF89009EE753 /* com.maxgoedjen.Secretive.SecretAgent.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = com.maxgoedjen.Secretive.SecretAgent.plist; sourceTree = "<group>"; };
|
||||
504788F12E681F3A00B4556F /* Instructions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instructions.swift; sourceTree = "<group>"; };
|
||||
504788F32E681F6900B4556F /* ToolConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolConfigurationView.swift; sourceTree = "<group>"; };
|
||||
504788F52E68206F00B4556F /* GettingStartedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GettingStartedView.swift; sourceTree = "<group>"; };
|
||||
@@ -445,6 +459,7 @@
|
||||
508BF28D25B4F005009EFB7E /* InternetAccessPolicy.plist */,
|
||||
50E4C4C72E777E4200C73783 /* AppIcon.icon */,
|
||||
50617D8F23FCE48E0099B055 /* Secretive.entitlements */,
|
||||
502452F32FE1FF89009EE753 /* com.maxgoedjen.Secretive.SecretAgent.plist */,
|
||||
5008C23D2E525D8200507AC2 /* Localizable.xcstrings */,
|
||||
50617D8823FCE48E0099B055 /* Preview Content */,
|
||||
);
|
||||
@@ -569,7 +584,8 @@
|
||||
50617D7C23FCE48D0099B055 /* Frameworks */,
|
||||
50617D7D23FCE48D0099B055 /* Resources */,
|
||||
50617DBF23FCE4AB0099B055 /* Embed Frameworks */,
|
||||
50C385AF240E438B00AF2719 /* CopyFiles */,
|
||||
50C385AF240E438B00AF2719 /* Copy SecretAgent */,
|
||||
502452F82FE2024D009EE753 /* Copy SecretAgent plist */,
|
||||
501577C92E6BC5B4004A37D0 /* Embed XPC Services */,
|
||||
);
|
||||
buildRules = (
|
||||
@@ -1523,7 +1539,7 @@
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1560,7 +1576,7 @@
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1598,7 +1614,7 @@
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_ENHANCED_SECURITY = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = YES;
|
||||
ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
|
||||
ENABLE_POINTER_AUTHENTICATION = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
launchStyle = "1"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
|
||||
@@ -87,6 +87,9 @@
|
||||
ReferencedContainer = "container:Secretive.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<MetalAPIValidationSettings
|
||||
isEnabled = "No">
|
||||
</MetalAPIValidationSettings>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Debug"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SwiftUI
|
||||
import ServiceManagement
|
||||
import SecretKit
|
||||
import SecureEnclaveSecretKit
|
||||
import SmartCardSecretKit
|
||||
@@ -8,7 +9,7 @@ import CertificateKit
|
||||
@main
|
||||
struct Secretive: App {
|
||||
|
||||
@Environment(\.agentLaunchController) var agentLaunchController
|
||||
// @Environment(\.agentLaunchController) var agentLaunchController
|
||||
@Environment(\.justUpdatedChecker) var justUpdatedChecker
|
||||
|
||||
@SceneBuilder var body: some Scene {
|
||||
@@ -18,15 +19,10 @@ struct Secretive: App {
|
||||
.environment(EnvironmentValues._certificateStore)
|
||||
.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
|
||||
Task {
|
||||
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
||||
@AppStorage("explicitlyDisabled") var explicitlyDisabled = false
|
||||
guard hasRunSetup && !explicitlyDisabled else { return }
|
||||
agentLaunchController.check()
|
||||
guard !agentLaunchController.developmentBuild else { return }
|
||||
if justUpdatedChecker.justUpdatedBuild || !agentLaunchController.running {
|
||||
// Relaunch the agent, since it'll be running from earlier update still
|
||||
try await agentLaunchController.forceLaunch()
|
||||
}
|
||||
let service = SMAppService.agent(plistName: "com.maxgoedjen.Secretive.SecretAgent.plist")
|
||||
try? service.unregister()
|
||||
try! service.register()
|
||||
print("Status: \(service.status)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,14 +100,6 @@ struct AgentNotRunningView: View {
|
||||
explicitlyDisabled = false
|
||||
guard !loading else { return }
|
||||
loading = true
|
||||
Task {
|
||||
try await agentLaunchController.forceLaunch()
|
||||
loading = false
|
||||
|
||||
if !agentLaunchController.running {
|
||||
triedRestart = true
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
if !loading {
|
||||
Text(.agentDetailsStartAgentButton)
|
||||
|
||||
18
Sources/Secretive/com.maxgoedjen.Secretive.SecretAgent.plist
Normal file
18
Sources/Secretive/com.maxgoedjen.Secretive.SecretAgent.plist
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.maxgoedjen.Secretive.SecretAgent</string>
|
||||
<key>BundleProgram</key>
|
||||
<string>Contents/Library/LoginItems/SecretAgent.app/Contents/MacOS/SecretAgent</string>
|
||||
<key>Sockets</key>
|
||||
<dict>
|
||||
<key>SecureListener</key>
|
||||
<dict>
|
||||
<key>SecureSocketWithKey</key>
|
||||
<string>SECRETAGENT_SOCK</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user