Skip to content

Commit bf0b756

Browse files
committed
retry
1 parent 492cb38 commit bf0b756

File tree

7 files changed

+78
-18
lines changed

7 files changed

+78
-18
lines changed

Coder Desktop/Coder Desktop/Coder_Desktop.entitlements

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,9 @@
88
</array>
99
<key>com.apple.developer.system-extension.install</key>
1010
<true/>
11-
<key>com.apple.security.app-sandbox</key>
12-
<true/>
1311
<key>com.apple.security.application-groups</key>
1412
<array>
1513
<string>$(TeamIdentifierPrefix)com.coder.Coder-Desktop</string>
1614
</array>
17-
<key>com.apple.security.files.user-selected.read-only</key>
18-
<true/>
19-
<key>com.apple.security.network.client</key>
20-
<true/>
2115
</dict>
2216
</plist>

Coder Desktop/Coder Desktop/NetworkExtension.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ enum NetworkExtensionState: Equatable {
2424
/// An actor that handles configuring, enabling, and disabling the VPN tunnel via the
2525
/// NetworkExtension APIs.
2626
extension CoderVPNService {
27-
func hasNetworkExtensionConfig() async -> Bool {
27+
func loadNetworkExtensionConfig() async {
2828
do {
29-
_ = try await getTunnelManager()
30-
return true
29+
let tm = try await getTunnelManager()
30+
neState = .disabled
31+
serverAddress = tm.protocolConfiguration?.serverAddress
3132
} catch {
32-
return false
33+
neState = .unconfigured
3334
}
3435
}
3536

Coder Desktop/Coder Desktop/VPNService.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,13 @@ final class CoderVPNService: NSObject, VPNService {
6363
// only stores a weak reference to the delegate.
6464
var systemExtnDelegate: SystemExtensionDelegate<CoderVPNService>?
6565

66+
var serverAddress: String?
67+
6668
override init() {
6769
super.init()
6870
installSystemExtension()
6971
Task {
70-
neState = if await hasNetworkExtensionConfig() {
71-
.disabled
72-
} else {
73-
.unconfigured
74-
}
72+
await loadNetworkExtensionConfig()
7573
}
7674
xpc.connect()
7775
xpc.getPeerState()
@@ -115,6 +113,7 @@ final class CoderVPNService: NSObject, VPNService {
115113
func configureTunnelProviderProtocol(proto: NETunnelProviderProtocol?) {
116114
Task {
117115
if let proto {
116+
serverAddress = proto.serverAddress
118117
await configureNetworkExtension(proto: proto)
119118
// this just configures the VPN, it doesn't enable it
120119
tunnelState = .disabled

Coder Desktop/Coder Desktop/XPCInterface.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,37 @@ import VPNLib
6464
svc.onExtensionPeerUpdate(data)
6565
}
6666
}
67+
68+
// The NE has verified the dylib and knows better than mac's Gatekeeper
69+
func removeQuarantine(path: String, reply: @escaping (Bool) -> Void) {
70+
let reply = CallbackWrapper(reply)
71+
Task { @MainActor in
72+
let prompt = """
73+
Coder Desktop wants to execute code downloaded from \
74+
\(svc.serverAddress ?? "the Coder deployment"). The code has been \
75+
verified to be signed by Coder.
76+
"""
77+
let source = """
78+
do shell script "xattr -d com.apple.quarantine \(path)" \
79+
with prompt "\(prompt)" \
80+
with administrator privileges
81+
"""
82+
do {
83+
try await withCheckedThrowingContinuation { continuation in
84+
guard let script = NSAppleScript(source: source) else {
85+
return
86+
}
87+
// Run on a background thread
88+
Task.detached {
89+
var error: NSDictionary?
90+
script.executeAndReturnError(&error)
91+
continuation.resume()
92+
}
93+
}
94+
reply(true)
95+
} catch {
96+
reply(false)
97+
}
98+
}
99+
}
67100
}

Coder Desktop/VPN/Manager.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ actor Manager {
4646
} catch {
4747
throw .validation(error)
4848
}
49+
50+
// HACK: The downloaded dylib may be quarantined, but we've validated it's signature
51+
// so it's safe to execute. However, the SE must be sandboxed, so we defer to the app.
52+
try await removeQuarantine(dest)
53+
4954
do {
5055
try tunnelHandle = TunnelHandle(dylibPath: dest)
5156
} catch {
@@ -228,6 +233,8 @@ enum ManagerError: Error {
228233
case serverInfo(String)
229234
case errorResponse(msg: String)
230235
case noTunnelFileDescriptor
236+
case noApp
237+
case permissionDenied
231238

232239
var description: String {
233240
switch self {
@@ -249,6 +256,10 @@ enum ManagerError: Error {
249256
msg
250257
case .noTunnelFileDescriptor:
251258
"Could not find a tunnel file descriptor"
259+
case .noApp:
260+
"The VPN must be started with the app open to perform first-time setup"
261+
case .permissionDenied:
262+
"Permission was not granted to run the CoderVPN dylib"
252263
}
253264
}
254265
}
@@ -273,3 +284,24 @@ func writeVpnLog(_ log: Vpn_Log) {
273284
let fields = log.fields.map { "\($0.name): \($0.value)" }.joined(separator: ", ")
274285
logger.log(level: level, "\(log.message, privacy: .public): \(fields, privacy: .public)")
275286
}
287+
288+
private func removeQuarantine(_ dest: URL) async throws(ManagerError) {
289+
var flag: AnyObject?
290+
let file = NSURL(fileURLWithPath: dest.path)
291+
try? file.getResourceValue(&flag, forKey: kCFURLQuarantinePropertiesKey as URLResourceKey)
292+
if flag != nil {
293+
guard let conn = globalXPCListenerDelegate.conn else {
294+
throw .noApp
295+
}
296+
// Wait for unsandboxed app to accept our file
297+
do {
298+
try await withCheckedThrowingContinuation { [dest] continuation in
299+
conn.removeQuarantine(path: dest.path) { _ in
300+
continuation.resume()
301+
}
302+
}
303+
} catch {
304+
throw .permissionDenied
305+
}
306+
}
307+
}

Coder Desktop/VPNLib/Util.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
public struct CallbackWrapper<T, U>: @unchecked Sendable {
2-
private let block: (T?) -> U
2+
private let block: (T) -> U
33

4-
public init(_ block: @escaping (T?) -> U) {
4+
public init(_ block: @escaping (T) -> U) {
55
self.block = block
66
}
77

8-
public func callAsFunction(_ error: T?) -> U {
8+
public func callAsFunction(_ error: T) -> U {
99
block(error)
1010
}
1111
}

Coder Desktop/VPNLib/XPC.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ import Foundation
1010
@objc public protocol VPNXPCClientCallbackProtocol {
1111
// data is a serialized `Vpn_PeerUpdate`
1212
func onPeerUpdate(_ data: Data)
13+
func removeQuarantine(path: String, reply: @escaping (Bool) -> Void)
1314
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy