From 5843c0f078ff7059d1c671307069c5b90601ea37 Mon Sep 17 00:00:00 2001 From: Josh Kaplan Date: Sat, 22 Jan 2022 01:19:55 +1300 Subject: [PATCH 1/6] Update README.md for next release --- README.md | 58 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 26a817e..ce2c848 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -SecureXPC provides an easy way to perform secure XPC Mach service communication. -[`Codable`](https://developer.apple.com/documentation/swift/codable) conforming types are used to send messages and -receive replies. This framework is ideal for communicating with helper tools installed via -[`SMJobBless`](https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless). +Use pure Swift to easily communicate with XPC Services and XPC Mach services, with customized support for helper tools +installed via [`SMJobBless`](https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless). A +client-server model is used with [`Codable`](https://developer.apple.com/documentation/swift/codable) conforming types +to send messages and receive replies to registered routes. -To see a runnable sample app using this framework, check out -[SwiftAuthorizationSample](https://github.com/trilemma-dev/SwiftAuthorizationSample). +macOS 10.10 and later is supported. Starting with macOS 10.15, clients can use `async` functions to make calls while +servers can register `async` handlers for their routes. [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ftrilemma-dev%2FSecureXPC%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/trilemma-dev/SecureXPC) @@ -16,19 +16,18 @@ these routes. ## Routes In a file shared by the client and server define one or more routes: ```swift -let route = XPCRouteWithMessageWithReply("bezaddle", - messageType: String.self, - replyType: Bool.self) +let route = XPCRoute.named("bedazzle") + .withMessageType(String.self) + .withReplyType(Bool.self) ``` ## Server In one program create a server, register those routes, and then start the server: ```swift ... - let server = XPCMachServer(machServiceName: "com.example.service", - clientRequirements: requirements) + let server = <# server retrieval here #> server.registerRoute(route, handler: bedazzle) - server.start() + server.startAndBlock() } private func bedazzle(message: String) throws -> Bool { @@ -36,25 +35,44 @@ private func bedazzle(message: String) throws -> Bool { } ``` -If this program is a helper tool installed by `SMJobBless`, then in many cases it can be initialized automatically: -```swift -let server = XPCMachServer.forThisBlessedHelperTool() -``` +There are multiple types of servers which can be retrieved: + - `XPCServer.forThisXPCService()` + - For an XPC Service, which is a private helper available only to the main application that contains it + - `XPCServer.forThisBlessedHelperTool()` + - For a helper tool installed via + [`SMJobBless`](https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless) + - To see a runnable sample app of this use case, check out + [SwiftAuthorizationSample](https://github.com/trilemma-dev/SwiftAuthorizationSample) + - `XPCServer.forThisMachService(named:clientRequirements:)` + - For Launch Agents, Launch Daemons, and more advanced `SMJobBless` helper tool configurations + - `XPCServer.makeAnonymous()` + - Typically used for testing purposes + - `XPCServer.makeAnonymous(clientRequirements:)` + - Enables applications not managed by `launchd` to communicate with each other, see documentation for more details. ## Client In another program create a client, then call one of those routes: ```swift -let client = XPCMachClient(machServiceName: "com.example.service") -try client.sendMessage("Get Schwifty", route: route, withReply: { result in +let client = <# client retrieval here #> +try client.sendMessage("Get Schwifty", route: route, withResponse: { result in switch result { - case let .success(reply): + case .success(let reply): <# use the reply #> - case let .failure(error): + case .failure(let error): <# handle the error #> } }) ``` +There are multiple types of servers which can be created: + - `XPCClient.forXPCService(named:)` + - For communicating with an XPC Service. This corresponds to servers created with `XPCServer.forThisXPCService()`. + - `XPCClient.forMachService(named:)` + - For communicating with an XPC Mach service. This corresponds to servers created with + `XPCServer.forThisBlessedHelperTool()` or `XPCServer.forThisMachService(named:clientRequirements:)`. + - `XPCClient.forEndpoint(_:)` + - This is the only way to communicate with an anonymous server. It can also be used with an XPC Mach service. + --- # `Codable` vs `NSSecureCoding` From f59f23d7f617c55cea789b6f8c2589364d0d43bd Mon Sep 17 00:00:00 2001 From: Josh Kaplan Date: Sat, 22 Jan 2022 01:33:48 +1300 Subject: [PATCH 2/6] Few improvements to client retrieval documentation in `README.md` --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ce2c848..ed96f71 100644 --- a/README.md +++ b/README.md @@ -64,14 +64,17 @@ try client.sendMessage("Get Schwifty", route: route, withResponse: { result in }) ``` -There are multiple types of servers which can be created: +There are multiple types of clients which can be retrieved: - `XPCClient.forXPCService(named:)` - - For communicating with an XPC Service. This corresponds to servers created with `XPCServer.forThisXPCService()`. + - For communicating with an XPC Service + - This corresponds to servers created with `XPCServer.forThisXPCService()` - `XPCClient.forMachService(named:)` - - For communicating with an XPC Mach service. This corresponds to servers created with - `XPCServer.forThisBlessedHelperTool()` or `XPCServer.forThisMachService(named:clientRequirements:)`. + - For communicating with an XPC Mach service + - This corresponds to servers created with `XPCServer.forThisBlessedHelperTool()` or + `XPCServer.forThisMachService(named:clientRequirements:)` - `XPCClient.forEndpoint(_:)` - - This is the only way to communicate with an anonymous server. It can also be used with an XPC Mach service. + - This is the only way to communicate with an anonymous server + - It can also be used with an XPC Mach service --- From 4d13b82731d12116a0e31883b9ddcc2a438b6453 Mon Sep 17 00:00:00 2001 From: Josh Kaplan Date: Sat, 22 Jan 2022 13:17:01 +1300 Subject: [PATCH 3/6] Adds a bit of info about `async` variants, other minor improvements --- README.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ed96f71..74e9397 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -Use pure Swift to easily communicate with XPC Services and XPC Mach services, with customized support for helper tools -installed via [`SMJobBless`](https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless). A -client-server model is used with [`Codable`](https://developer.apple.com/documentation/swift/codable) conforming types +Use pure Swift to easily and securely communicate with XPC Services and XPC Mach services, with customized support for +helper tools installed via [`SMJobBless`](https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless). +A client-server model is used with [`Codable`](https://developer.apple.com/documentation/swift/codable) conforming types to send messages and receive replies to registered routes. -macOS 10.10 and later is supported. Starting with macOS 10.15, clients can use `async` functions to make calls while +macOS 10.10 and later is supported. Starting with macOS 10.15, clients can use `async` functions to make calls and servers can register `async` handlers for their routes. [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ftrilemma-dev%2FSecureXPC%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/trilemma-dev/SecureXPC) @@ -22,11 +22,11 @@ let route = XPCRoute.named("bedazzle") ``` ## Server -In one program create a server, register those routes, and then start the server: +In one program retrieve a server, register those routes, and then start the server: ```swift ... let server = <# server retrieval here #> - server.registerRoute(route, handler: bedazzle) + try server.registerRoute(route, handler: bedazzle) server.startAndBlock() } @@ -35,6 +35,8 @@ private func bedazzle(message: String) throws -> Bool { } ``` +On macOS 10.15 and later `async` functions and closures can be registered as the handler for a route. + There are multiple types of servers which can be retrieved: - `XPCServer.forThisXPCService()` - For an XPC Service, which is a private helper available only to the main application that contains it @@ -48,13 +50,13 @@ There are multiple types of servers which can be retrieved: - `XPCServer.makeAnonymous()` - Typically used for testing purposes - `XPCServer.makeAnonymous(clientRequirements:)` - - Enables applications not managed by `launchd` to communicate with each other, see documentation for more details. + - Enables applications not managed by `launchd` to communicate with each other, see documentation for more details ## Client -In another program create a client, then call one of those routes: +In another program retrieve a client, then call one of those routes: ```swift let client = <# client retrieval here #> -try client.sendMessage("Get Schwifty", route: route, withResponse: { result in +client.sendMessage("Get Schwifty", route: route, withResponse: { result in switch result { case .success(let reply): <# use the reply #> @@ -64,6 +66,12 @@ try client.sendMessage("Get Schwifty", route: route, withResponse: { result in }) ``` +On macOS 10.15 and later the `async` variants can be used: +```swift +let client = <# client retrieval here #> +let reply = try await client.sendMessage("Get Schwifty", route: route) +``` + There are multiple types of clients which can be retrieved: - `XPCClient.forXPCService(named:)` - For communicating with an XPC Service @@ -80,7 +88,7 @@ There are multiple types of clients which can be retrieved: # `Codable` vs `NSSecureCoding` SecureXPC uses types conforming to Swift's `Codable` protocol to serialize data across the XPC connection. Due to the -nature of how `Codable` is defined, it is not possible for the same instance to be referenced from multiple other +nature of how `Codable` is defined, it is not possible for the same instance to be referenced from multiple other deserialized instances. This is in contrast to how [`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding) behaves, which is used by [`NSXPCConnection`](https://developer.apple.com/documentation/foundation/nsxpcconnection) for serialization. From 6111141455c3d0d23e6c90e5167f68db42db2d3c Mon Sep 17 00:00:00 2001 From: Josh Kaplan Date: Sat, 22 Jan 2022 13:18:14 +1300 Subject: [PATCH 4/6] Minor grammatical improvement --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 74e9397..7ceda81 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Use pure Swift to easily and securely communicate with XPC Services and XPC Mach services, with customized support for helper tools installed via [`SMJobBless`](https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless). A client-server model is used with [`Codable`](https://developer.apple.com/documentation/swift/codable) conforming types -to send messages and receive replies to registered routes. +to send messages to registered routes and receive replies. macOS 10.10 and later is supported. Starting with macOS 10.15, clients can use `async` functions to make calls and servers can register `async` handlers for their routes. From 4ace274628553790fce16b6e8468fa8c554ae08d Mon Sep 17 00:00:00 2001 From: Josh Kaplan Date: Sat, 22 Jan 2022 23:11:12 +1300 Subject: [PATCH 5/6] Incorporates feedback from @amomchilov --- README.md | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 7ceda81..f0583d5 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,17 @@ -Use pure Swift to easily and securely communicate with XPC Services and XPC Mach services, with customized support for -helper tools installed via [`SMJobBless`](https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless). -A client-server model is used with [`Codable`](https://developer.apple.com/documentation/swift/codable) conforming types -to send messages to registered routes and receive replies. +Use pure Swift to easily and securely communicate with XPC Services and XPC Mach services. A client-server model +enables you to use your own [`Codable`](https://developer.apple.com/documentation/swift/codable) conforming types to +send messages to routes you define and receive replies. -macOS 10.10 and later is supported. Starting with macOS 10.15, clients can use `async` functions to make calls and -servers can register `async` handlers for their routes. +SecureXPC uses [Swift concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html) on macOS 10.15 and +later allowing clients to make non-blocking asynchronous calls to servers. A closure-based API is also available +providing compatibility back to OS X 10.10. + +This framework is ideal for communicating with helper tools installed via +[`SMJobBless`](https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless). It's built with +security in mind, minimizing the opportunities for +[exploits](https://objectivebythesea.com/v3/talks/OBTS_v3_wReguĊ‚a.pdf). Server-side security checks are performed +against the actual calling process instead of relying on PIDs which are known to be +[insecure](https://saelo.github.io/presentations/warcon18_dont_trust_the_pid.pdf). [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ftrilemma-dev%2FSecureXPC%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/trilemma-dev/SecureXPC) @@ -35,25 +42,33 @@ private func bedazzle(message: String) throws -> Bool { } ``` -On macOS 10.15 and later `async` functions and closures can be registered as the handler for a route. +On macOS 10.15 and later `async` functions and closures can also be registered as the handler for a route. There are multiple types of servers which can be retrieved: - `XPCServer.forThisXPCService()` - - For an XPC Service, which is a private helper available only to the main application that contains it + - For an XPC Service, which is a private helper tool available only to the main application that contains it - `XPCServer.forThisBlessedHelperTool()` - For a helper tool installed via [`SMJobBless`](https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless) - - To see a runnable sample app of this use case, check out + - To see a sample app for this use case, check out [SwiftAuthorizationSample](https://github.com/trilemma-dev/SwiftAuthorizationSample) - `XPCServer.forThisMachService(named:clientRequirements:)` - - For Launch Agents, Launch Daemons, and more advanced `SMJobBless` helper tool configurations + - For + [Launch Daemons and Agents](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html) + as well as more advanced `SMJobBless` helper tool configurations - `XPCServer.makeAnonymous()` - Typically used for testing purposes - `XPCServer.makeAnonymous(clientRequirements:)` - Enables applications not managed by `launchd` to communicate with each other, see documentation for more details ## Client -In another program retrieve a client, then call one of those routes: +In another program retrieve a client, then call one of the registered routes: +```swift +let client = <# client retrieval here #> +let reply = try await client.sendMessage("Get Schwifty", route: route) +``` + +Closure-based variants are available for macOS 10.14 and earlier: ```swift let client = <# client retrieval here #> client.sendMessage("Get Schwifty", route: route, withResponse: { result in @@ -66,12 +81,6 @@ client.sendMessage("Get Schwifty", route: route, withResponse: { result in }) ``` -On macOS 10.15 and later the `async` variants can be used: -```swift -let client = <# client retrieval here #> -let reply = try await client.sendMessage("Get Schwifty", route: route) -``` - There are multiple types of clients which can be retrieved: - `XPCClient.forXPCService(named:)` - For communicating with an XPC Service @@ -82,6 +91,8 @@ There are multiple types of clients which can be retrieved: `XPCServer.forThisMachService(named:clientRequirements:)` - `XPCClient.forEndpoint(_:)` - This is the only way to communicate with an anonymous server + - This corresponds to servers created with `XPCServer.makeAnonymous()` or + `XPCServer.makeAnonymous(clientRequirements:)` - It can also be used with an XPC Mach service --- From a736334e94eaf5dc846568e0ecf8ea82a649433c Mon Sep 17 00:00:00 2001 From: Josh Kaplan Date: Tue, 25 Jan 2022 19:04:18 +1300 Subject: [PATCH 6/6] Updates README.md to match recent changes - Example code registering a route no longer has a `try` - XPC Service(s) -> XPC service(s) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f0583d5..3d9764c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Use pure Swift to easily and securely communicate with XPC Services and XPC Mach services. A client-server model +Use pure Swift to easily and securely communicate with XPC services and XPC Mach services. A client-server model enables you to use your own [`Codable`](https://developer.apple.com/documentation/swift/codable) conforming types to send messages to routes you define and receive replies. @@ -33,7 +33,7 @@ In one program retrieve a server, register those routes, and then start the serv ```swift ... let server = <# server retrieval here #> - try server.registerRoute(route, handler: bedazzle) + server.registerRoute(route, handler: bedazzle) server.startAndBlock() } @@ -46,7 +46,7 @@ On macOS 10.15 and later `async` functions and closures can also be registered a There are multiple types of servers which can be retrieved: - `XPCServer.forThisXPCService()` - - For an XPC Service, which is a private helper tool available only to the main application that contains it + - For an XPC service, which is a private helper tool available only to the main application that contains it - `XPCServer.forThisBlessedHelperTool()` - For a helper tool installed via [`SMJobBless`](https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless) @@ -83,7 +83,7 @@ client.sendMessage("Get Schwifty", route: route, withResponse: { result in There are multiple types of clients which can be retrieved: - `XPCClient.forXPCService(named:)` - - For communicating with an XPC Service + - For communicating with an XPC service - This corresponds to servers created with `XPCServer.forThisXPCService()` - `XPCClient.forMachService(named:)` - For communicating with an XPC Mach service