Skip to content

Commit

Permalink
remote: add SPICE ticket password auth
Browse files Browse the repository at this point in the history
  • Loading branch information
osy committed Feb 11, 2024
1 parent de8d9a9 commit cae50ce
Show file tree
Hide file tree
Showing 12 changed files with 38 additions and 11 deletions.
11 changes: 10 additions & 1 deletion Configuration/UTMQemuConfiguration+Arguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ import Virtualization // for getting network interfaces
"unix=on"
"addr=\(spiceSocketURL.lastPathComponent)"
}
"disable-ticketing=on"
if let _ = qemu.spiceServerPassword {
"password-secret=secspice0"
} else {
"disable-ticketing=on"
}
if !isRemoteSpice {
"image-compression=off"
"playback-compression=off"
Expand Down Expand Up @@ -176,6 +180,11 @@ import Virtualization // for getting network interfaces
f("-vga")
f("none")
}
if let password = qemu.spiceServerPassword {
// assume anyone who can read this is in our trust domain
f("-object")
f("secret,id=secspice0,data=\(password)")
}
}

private func filterDisplayIfRemote(_ display: any QEMUDisplayDevice) -> any QEMUDisplayDevice {
Expand Down
3 changes: 3 additions & 0 deletions Configuration/UTMQemuConfigurationQEMU.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ struct UTMQemuConfigurationQEMU: Codable {

/// Set to TLS public key for SPICE server in SubjectPublicKey. Not saved.
var spiceServerPublicKey: Data?

/// Set to a password shared with the client. Not saved.
var spiceServerPassword: String?

enum CodingKeys: String, CodingKey {
case hasDebugLog = "DebugLog"
Expand Down
4 changes: 2 additions & 2 deletions Platform/macOS/UTMDataExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ extension UTMData {
/// - options: Start options
/// - server: Remote server
/// - Returns: Port number to SPICE server
func startRemote(vm: VMData, options: UTMVirtualMachineStartOptions, forClient client: UTMRemoteServer.Remote) async throws -> (port: UInt16, publicKey: Data) {
func startRemote(vm: VMData, options: UTMVirtualMachineStartOptions, forClient client: UTMRemoteServer.Remote) async throws -> (port: UInt16, publicKey: Data, password: String) {
guard let wrapped = vm.wrapped as? UTMQemuVirtualMachine, type(of: wrapped).capabilities.supportsRemoteSession else {
throw UTMDataError.unsupportedBackend
}
Expand All @@ -94,7 +94,7 @@ extension UTMData {
}
try await wrapped.start(options: options.union(.remoteSession))
vmWindows[vm] = session
return (wrapped.config.qemu.spiceServerPort!, wrapped.config.qemu.spiceServerPublicKey!)
return (wrapped.config.qemu.spiceServerPort!, wrapped.config.qemu.spiceServerPublicKey!, wrapped.config.qemu.spiceServerPassword!)
}

func stop(vm: VMData) {
Expand Down
4 changes: 2 additions & 2 deletions Remote/UTMRemoteClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,9 @@ extension UTMRemoteClient {
return fileUrl
}

func startVirtualMachine(id: UUID, options: UTMVirtualMachineStartOptions) async throws -> (port: UInt16, publicKey: Data) {
func startVirtualMachine(id: UUID, options: UTMVirtualMachineStartOptions) async throws -> (port: UInt16, publicKey: Data, password: String) {
let reply = try await _startVirtualMachine(parameters: .init(id: id, options: options))
return (reply.spiceServerPort, reply.spiceServerPublicKey)
return (reply.spiceServerPort, reply.spiceServerPublicKey, reply.spiceServerPassword)
}

func stopVirtualMachine(id: UUID, method: UTMVirtualMachineStopMethod) async throws {
Expand Down
1 change: 1 addition & 0 deletions Remote/UTMRemoteMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ extension UTMRemoteMessageServer {
struct Reply: Serializable, Codable {
let spiceServerPort: UInt16
let spiceServerPublicKey: Data
let spiceServerPassword: String
}
}

Expand Down
4 changes: 2 additions & 2 deletions Remote/UTMRemoteServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -631,8 +631,8 @@ extension UTMRemoteServer {

private func _startVirtualMachine(parameters: M.StartVirtualMachine.Request) async throws -> M.StartVirtualMachine.Reply {
let vm = try await findVM(withId: parameters.id)
let (port, publicKey) = try await data.startRemote(vm: vm, options: parameters.options, forClient: client)
return .init(spiceServerPort: port, spiceServerPublicKey: publicKey)
let (port, publicKey, password) = try await data.startRemote(vm: vm, options: parameters.options, forClient: client)
return .init(spiceServerPort: port, spiceServerPublicKey: publicKey, spiceServerPassword: password)
}

private func _stopVirtualMachine(parameters: M.StopVirtualMachine.Request) async throws -> M.StopVirtualMachine.Reply {
Expand Down
6 changes: 5 additions & 1 deletion Remote/UTMRemoteSpiceVirtualMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,11 @@ extension UTMRemoteSpiceVirtualMachine {
options.insert(.hasDebugLog)
}
#endif
let ioService = UTMSpiceIO(host: server.host, tlsPort: Int(spiceServer.port), serverPublicKey: spiceServer.publicKey, options: options)
let ioService = UTMSpiceIO(host: server.host,
tlsPort: Int(spiceServer.port),
serverPublicKey: spiceServer.publicKey,
password: spiceServer.password,
options: options)
ioService.logHandler = { (line: String) -> Void in
guard !line.contains("spice_make_scancode") else {
return // do not log key presses for privacy reasons
Expand Down
5 changes: 5 additions & 0 deletions Services/UTMExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,11 @@ extension String {
}
return Int(numeric)
}

static func random(length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map{ _ in letters.randomElement()! })
}
}

extension Encodable {
Expand Down
2 changes: 2 additions & 0 deletions Services/UTMQemuVirtualMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,12 @@ extension UTMQemuVirtualMachine {
let isRunningAsDisposible = options.contains(.bootDisposibleMode)
let isRemoteSession = options.contains(.remoteSession)
let spicePort = isRemoteSession ? try UTMSocketUtils.reservePort() : nil
let spicePassword = isRemoteSession ? String.random(length: 32) : nil
await MainActor.run {
config.qemu.isDisposable = isRunningAsDisposible
config.qemu.spiceServerPort = spicePort
config.qemu.isSpiceServerTlsEnabled = true
config.qemu.spiceServerPassword = spicePassword
}

// start TPM
Expand Down
2 changes: 1 addition & 1 deletion Services/UTMSpiceIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN

- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithSocketUrl:(NSURL *)socketUrl options:(UTMSpiceIOOptions)options NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithHost:(NSString *)host tlsPort:(NSInteger)tlsPort serverPublicKey:(NSData *)serverPublicKey options:(UTMSpiceIOOptions)options NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithHost:(NSString *)host tlsPort:(NSInteger)tlsPort serverPublicKey:(NSData *)serverPublicKey password:(NSString *)password options:(UTMSpiceIOOptions)options NS_DESIGNATED_INITIALIZER;
- (void)changeSharedDirectory:(NSURL *)url;

- (BOOL)startWithError:(NSError * _Nullable *)error;
Expand Down
5 changes: 4 additions & 1 deletion Services/UTMSpiceIO.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ @interface UTMSpiceIO ()
@property (nonatomic, nullable) NSString *host;
@property (nonatomic) NSInteger tlsPort;
@property (nonatomic, nullable) NSData *serverPublicKey;
@property (nonatomic, nullable) NSString *password;
@property (nonatomic) UTMSpiceIOOptions options;
@property (nonatomic, readwrite, nullable) CSDisplay *primaryDisplay;
@property (nonatomic) NSMutableArray<CSDisplay *> *mutableDisplays;
Expand Down Expand Up @@ -74,11 +75,12 @@ - (instancetype)initWithSocketUrl:(NSURL *)socketUrl options:(UTMSpiceIOOptions)
return self;
}

- (instancetype)initWithHost:(NSString *)host tlsPort:(NSInteger)tlsPort serverPublicKey:(NSData *)serverPublicKey options:(UTMSpiceIOOptions)options {
- (instancetype)initWithHost:(NSString *)host tlsPort:(NSInteger)tlsPort serverPublicKey:(NSData *)serverPublicKey password:(NSString *)password options:(UTMSpiceIOOptions)options {
if (self = [super init]) {
self.host = host;
self.tlsPort = tlsPort;
self.serverPublicKey = serverPublicKey;
self.password = password;
self.options = options;
self.mutableDisplays = [NSMutableArray array];
self.mutableSerials = [NSMutableArray array];
Expand All @@ -94,6 +96,7 @@ - (void)initializeSpiceIfNeeded {
self.spiceConnection = [[CSConnection alloc] initWithUnixSocketFile:relativeSocketFile];
} else {
self.spiceConnection = [[CSConnection alloc] initWithHost:self.host tlsPort:[@(self.tlsPort) stringValue] serverPublicKey:self.serverPublicKey];
self.spiceConnection.password = self.password;
}
self.spiceConnection.delegate = self;
self.spiceConnection.audioEnabled = (self.options & UTMSpiceIOOptionsHasAudio) == UTMSpiceIOOptionsHasAudio;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"location" : "https://github.com/utmapp/CocoaSpice.git",
"state" : {
"branch" : "visionos",
"revision" : "9591cdf41282a7e6edbe7b705adbb957592ba347"
"revision" : "9d286ba10b8ed953bf21c04ddd64237372163132"
}
},
{
Expand Down

0 comments on commit cae50ce

Please sign in to comment.