11#[ allow(
22 dead_code,
3- safe_packed_borrows ,
3+ unaligned_references ,
44 non_upper_case_globals,
55 non_camel_case_types,
66 non_snake_case,
@@ -11,20 +11,34 @@ extern crate xpc_connection_sys;
1111mod message;
1212pub use message:: * ;
1313
14+ #[ macro_use]
15+ mod dlsym;
16+
1417use block:: ConcreteBlock ;
1518use futures:: {
1619 channel:: mpsc:: { unbounded as unbounded_channel, UnboundedReceiver , UnboundedSender } ,
1720 Stream ,
1821} ;
19- use std:: ffi:: CStr ;
20- use std:: { ffi:: c_void, ops:: Deref } ;
21- use std:: { pin:: Pin , task:: Poll } ;
22+ use std:: {
23+ ffi:: { CStr , CString } ,
24+ ops:: Deref ,
25+ os:: raw:: { c_char, c_int} ,
26+ pin:: Pin ,
27+ sync:: atomic:: AtomicUsize ,
28+ task:: Poll ,
29+ } ;
30+
2231use xpc_connection_sys:: {
23- xpc_connection_cancel, xpc_connection_create_mach_service, xpc_connection_resume,
24- xpc_connection_send_message, xpc_connection_set_event_handler, xpc_connection_t, xpc_object_t,
25- xpc_release, XPC_CONNECTION_MACH_SERVICE_LISTENER , XPC_CONNECTION_MACH_SERVICE_PRIVILEGED ,
32+ dispatch_queue_t, xpc_connection_cancel, xpc_connection_create_mach_service,
33+ xpc_connection_resume, xpc_connection_send_message, xpc_connection_set_event_handler,
34+ xpc_connection_t, xpc_object_t, xpc_release, XPC_CONNECTION_MACH_SERVICE_LISTENER ,
35+ XPC_CONNECTION_MACH_SERVICE_PRIVILEGED ,
2636} ;
2737
38+ dlsym ! {
39+ fn xpc_connection_set_peer_code_sig( * const c_char) -> c_int
40+ }
41+
2842// A connection's event handler could still be waiting or running when we want
2943// to drop a connection. We must cancel the handler and wait for the final
3044// call to a handler to occur, which is always a message containing an
@@ -87,12 +101,35 @@ impl Stream for XpcListener {
87101
88102impl XpcListener {
89103 /// The connection must be a listener.
90- fn from_raw ( connection : xpc_connection_t ) -> XpcListener {
104+ fn from_raw ( connection : xpc_connection_t , requirement : Option < & ' static str > ) -> XpcListener {
91105 let ( sender, receiver) = unbounded_channel ( ) ;
92106 let sender_clone = sender. clone ( ) ;
93107
108+ let mut already_validated = false ;
109+
110+ if let Some ( requirement) = requirement {
111+ if let Some ( f) = crate :: xpc_connection_set_peer_code_sig. get ( ) {
112+ let requirement = CString :: new ( requirement) . expect ( "Invalid requirement string" ) ;
113+ unsafe {
114+ f ( requirement. as_ptr ( ) ) ;
115+ }
116+
117+ already_validated = true ;
118+ }
119+ }
120+
94121 let block = ConcreteBlock :: new ( move |event| match xpc_object_to_message ( event) {
95- Message :: Client ( client) => sender_clone. unbounded_send ( client) . ok ( ) ,
122+ Message :: Client ( mut client) => {
123+ if already_validated
124+ || Self :: validate_client_using_audit_token ( & client, & requirement)
125+ {
126+ sender_clone. unbounded_send ( client) . ok ( )
127+ } else {
128+ unsafe { xpc_connection_cancel ( client. connection ) } ;
129+ client. event_handler_is_running = false ;
130+ None
131+ }
132+ }
96133 _ => None ,
97134 } ) ;
98135
@@ -113,13 +150,68 @@ impl XpcListener {
113150 }
114151 }
115152
116- pub fn listen ( name : impl AsRef < CStr > ) -> Self {
153+ /// If `requirement` is set then clients will have their code signature
154+ /// validated before being available. See Apple's documentation on the
155+ /// language [here](https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/RequirementLang/RequirementLang.html).
156+ ///
157+ /// On macOS 12 this uses `xpc_connection_set_peer_code_sig`, and if the
158+ /// `audit_token` feature is enabled then this will use a custom
159+ /// implementation on older versions of macOS.
160+ ///
161+ /// # Panics
162+ ///
163+ /// * If `audit_token` feature is used and the `requirement` isn't parsable
164+ /// as a `SecRequirement`. This will occur during client validation.
165+ pub fn listen (
166+ name : impl AsRef < CStr > ,
167+ requirement : Option < & ' static str > ,
168+ queue : Option < dispatch_queue_t > ,
169+ ) -> XpcListener {
117170 let name = name. as_ref ( ) ;
118171 let flags = XPC_CONNECTION_MACH_SERVICE_LISTENER as u64 ;
119- let connection = unsafe {
120- xpc_connection_create_mach_service ( name. as_ref ( ) . as_ptr ( ) , std:: ptr:: null_mut ( ) , flags)
172+ let queue = queue. unwrap_or ( std:: ptr:: null_mut ( ) ) ;
173+
174+ let connection =
175+ unsafe { xpc_connection_create_mach_service ( name. as_ref ( ) . as_ptr ( ) , queue, flags) } ;
176+
177+ Self :: from_raw ( connection, requirement)
178+ }
179+
180+ #[ inline]
181+ #[ cfg( feature = "audit_token" ) ]
182+ fn validate_client_using_audit_token ( client : & XpcClient , requirement : & Option < & str > ) -> bool {
183+ use core_foundation:: { base:: TCFType , data:: CFData } ;
184+ use security_framework:: os:: macos:: code_signing:: { Flags , GuestAttributes , SecCode } ;
185+
186+ let requirement = match requirement {
187+ Some ( r) => r,
188+ None => return true ,
121189 } ;
122- Self :: from_raw ( connection)
190+
191+ let requirement = requirement
192+ . parse ( )
193+ . expect ( "Unable to parse the requirement" ) ;
194+
195+ let token_data = CFData :: from_buffer ( & client. audit_token ( ) ) ;
196+ let mut attrs = GuestAttributes :: new ( ) ;
197+ attrs. set_audit_token ( token_data. as_concrete_TypeRef ( ) ) ;
198+
199+ if let Ok ( code_object) = SecCode :: copy_guest_with_attribues ( None , & attrs, Flags :: NONE ) {
200+ return code_object
201+ . check_validity ( Flags :: NONE , & requirement)
202+ . is_ok ( ) ;
203+ }
204+
205+ false
206+ }
207+
208+ #[ inline]
209+ #[ cfg( not( feature = "audit_token" ) ) ]
210+ fn validate_client_using_audit_token ( _client : & XpcClient , _requirement : & Option < & str > ) -> bool {
211+ // TODO: log an error:
212+ // Attempted to use code signature requirements on an unsupported
213+ // version of macOS without the `audit_token` feature enabled
214+ false
123215 }
124216}
125217
@@ -223,6 +315,8 @@ impl XpcClient {
223315
224316 #[ cfg( feature = "audit_token" ) ]
225317 pub fn audit_token ( & self ) -> [ u8 ; 32 ] {
318+ use libc:: c_void;
319+
226320 // This is a private API, but it's also required in order to
227321 // authenticate XPC clients without requiring a handshake.
228322 // See https://developer.apple.com/forums/thread/72881 for more info.
0 commit comments