• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (C) 2025 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 
17 use alloc::ffi::CString;
18 use binder::{ExceptionCode, FromIBinder, ParcelFileDescriptor, StatusCode, Strong};
19 use rpcbinder::{FileDescriptorTransportMode, RpcSession};
20 use std::ffi::CStr;
21 use std::os::fd::IntoRawFd;
22 use trusty_binder_accessor::aidl::trusty::os::ITrustyAccessor::{
23     ITrustyAccessor, ERROR_CONNECTION_INFO_NOT_FOUND, ERROR_FAILED_TO_CONNECT_EACCES,
24 };
25 
26 // At the time of writing, this max is enforced by the trusty kernel
27 // which defines MAX_PORT_PATH_LEN
28 const MAX_TIPC_PORT_NAME_LEN: usize = 64;
29 const TRUSTY_BINDER_RPC_PORT_SUFFIX: &str = ".bnd";
30 const TRUSTED_HAL_COMMON_PREFIX: &str = "android.hardware.security.see";
31 const TRUSTED_HAL_REPLACEMENT_PREFIX: &str = "ahss";
32 
33 #[derive(Debug, PartialEq)]
34 enum Error {
35     /// A connection to the given port could not be established.
36     ConnectionFailed,
37     /// A connection was established to the given port, but setting up a binder client
38     /// failed.
39     BinderSetupFailed(StatusCode),
40 }
41 
42 /// Retrieve a service by name. This function will block until a service is available and
43 /// may wait indefinitely if it never becomes available.
44 ///
45 /// In trusty, services are exposed to other apps by their port names.
46 /// The convention in Android Binder is to use {binder_descriptor}/{service_instance}
47 /// as the pattern for service names. When possible, trusty services that are defined in AIDL
48 /// should adhere to this convention.
49 /// E.g. "android.hardware.security.see.storage.ISecureStorage/tee"
50 ///
51 /// Note that trusty port names cannot be more than 64 characters, so `name` must be
52 /// adhere to that constraint. To prevent collisions between non-binder trusty services,
53 /// we add a suffix of `.bnd` to all service names in this function.
54 ///
55 /// TODO: b/395096422 - Make this more performant once we have a CPP lib or a rust API
56 /// that can access SpIBinder directly.
wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>, StatusCode>57 pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>, StatusCode> {
58     let c_port_name = service_name_to_trusty_c_port(name)?;
59     let port_name = c_port_name.as_c_str().to_str().map_err(|_| StatusCode::BAD_VALUE)?;
60 
61     // First try and see if our service is gated by an ITrustyAccessor implementation.
62     // This is a binder service exposed on the port we've connected to that acts as
63     // an intermediary for resolving connections to the requested service. It was originally
64     // introduced to allow for authentication and authorization of the caller before handing
65     // back a handle to the requested service.
66     let mut session = get_new_rpc_session();
67     match setup_trusty_client(&session, &c_port_name) {
68         Ok(accessor) => {
69             let service_fd = fd_from_accessor(&accessor, name)?;
70 
71             return rpcbinder_from_parcel_fd(service_fd).inspect_err(|&status_code| {
72                 log::error!(
73                     "Failed to setup binder on fd returned from ITrustyAccessor for {:?} {}",
74                     &port_name,
75                     status_code
76                 );
77             });
78         }
79         Err(Error::ConnectionFailed) => {
80             // If the port we requested didn't exist this would have blocked forever. So we assume
81             // that the service listening on this port rejected our connection.
82             return Err(StatusCode::PERMISSION_DENIED);
83         }
84         Err(Error::BinderSetupFailed(s)) => {
85             log::debug!(
86                 "failed to setup client to ITrustyAccessor for {:?} {:?}. Will try a direct \
87             connection.",
88                 &port_name,
89                 s
90             );
91         }
92     }
93 
94     // The binder on the other end was likely not an accessor, try a direct connection.
95     session = get_new_rpc_session();
96     setup_trusty_client(&session, &c_port_name).map_err(|e| {
97         log::error!("failed to setup binder on {:?} {:?}", &port_name, e);
98         match e {
99             // This is an unexpected case. We've already successfully connected to this port once.
100             Error::ConnectionFailed => StatusCode::PERMISSION_DENIED,
101             Error::BinderSetupFailed(s) => s,
102         }
103     })
104 }
105 
fd_from_accessor( accessor: &Strong<dyn ITrustyAccessor>, name: &str, ) -> Result<ParcelFileDescriptor, StatusCode>106 fn fd_from_accessor(
107     accessor: &Strong<dyn ITrustyAccessor>,
108     name: &str,
109 ) -> Result<ParcelFileDescriptor, StatusCode> {
110     // Before we attempt to add a connection, ensure that we've obtained an Accessor
111     // for the correct service name.
112     let accessor_instance_name = accessor.getInstanceName().map_err(|status_code| {
113         log::error!(
114             "Failed to resolve ITrustyAccessor instance name at {:?} {}",
115             name,
116             status_code
117         );
118 
119         StatusCode::NAME_NOT_FOUND
120     })?;
121 
122     if accessor_instance_name != name {
123         log::error!(
124             "Provided service name {:?} does not match the instance name resolved from \
125         ITrustyAccessor {}",
126             name,
127             accessor_instance_name
128         );
129 
130         return Err(StatusCode::NAME_NOT_FOUND);
131     }
132 
133     let service_fd = accessor.addConnection().map_err(|status| {
134         log::error!("Failed to retrieve FD from accessor for {:?} {}", name, status);
135 
136         match status.exception_code() {
137             ExceptionCode::SERVICE_SPECIFIC => match status.service_specific_error() {
138                 ERROR_FAILED_TO_CONNECT_EACCES => StatusCode::PERMISSION_DENIED,
139                 ERROR_CONNECTION_INFO_NOT_FOUND => StatusCode::NAME_NOT_FOUND,
140                 _ => StatusCode::UNKNOWN_ERROR,
141             },
142             ExceptionCode::TRANSACTION_FAILED => status.transaction_error(),
143             _ => StatusCode::NAME_NOT_FOUND,
144         }
145     })?;
146 
147     Ok(service_fd)
148 }
149 
rpcbinder_from_parcel_fd<T: FromIBinder + ?Sized>( parceled_fd: ParcelFileDescriptor, ) -> Result<Strong<T>, StatusCode>150 fn rpcbinder_from_parcel_fd<T: FromIBinder + ?Sized>(
151     parceled_fd: ParcelFileDescriptor,
152 ) -> Result<Strong<T>, StatusCode> {
153     let session = get_new_rpc_session();
154     let raw_fd = parceled_fd.into_raw_fd();
155 
156     session.setup_preconnected_client(move || Some(raw_fd))
157 }
158 
159 /// Pretty much a re-implementation of RpcSession::setup_trusty_client but does not panic
160 /// when a connection fails. As a bonus, we can differentiate between a failed raw connection
161 /// and other binder-specific errors.
setup_trusty_client<T: FromIBinder + ?Sized>( session: &RpcSession, port: &CStr, ) -> Result<Strong<T>, Error>162 fn setup_trusty_client<T: FromIBinder + ?Sized>(
163     session: &RpcSession,
164     port: &CStr,
165 ) -> Result<Strong<T>, Error> {
166     let h = tipc::Handle::connect(port).map_err(|e| {
167         log::error!("Failed to connect to port {:?} {:?}", port, e);
168         Error::ConnectionFailed
169     })?;
170     // Do not close the handle at the end of the scope
171     let fd = h.as_raw_fd();
172     core::mem::forget(h);
173     session.setup_preconnected_client(|| Some(fd)).map_err(|status_code| {
174         log::debug!("failed to setup preconnected client on port {:?} {}", port, status_code);
175 
176         Error::BinderSetupFailed(status_code)
177     })
178 }
179 
get_new_rpc_session() -> RpcSession180 fn get_new_rpc_session() -> RpcSession {
181     let session = RpcSession::new();
182     session.set_file_descriptor_transport_mode(FileDescriptorTransportMode::Trusty);
183 
184     session
185 }
186 
187 // Many trusted hal interfaces (VINTF stable interfaces to the TEE) are too long
188 // for the current max port name (64 characters including a null byte). We work
189 // around this temporarily by taking advantage of a common prefix they all have
190 // and map "android.hardware.security.see" to "ahss".
191 //
192 // So for example, "android.hardware.security.see.authmgr.IAuthMgrAuthorization/default"
193 // will result in a port name of ahss.authmgr.IAuthMgrAuhtorization/default .
try_long_service_name_to_port(service_name: &str) -> Result<String, StatusCode>194 fn try_long_service_name_to_port(service_name: &str) -> Result<String, StatusCode> {
195     let mut port_name = String::new();
196 
197     match service_name.split_once(TRUSTED_HAL_COMMON_PREFIX) {
198         // Since we're in this function, we already know the given service_name is too long
199         // and a None here means we won't shorten it since our prefix wasn't found.
200         None => Err(port_size_err(service_name)),
201         // In this case our pattern was found, but not at the front of the str, which
202         // is not a valid case for our name shortening. Since we're in this function
203         // we now know we have a name that's too long and we can't shorten.
204         Some((pre, _)) if !pre.is_empty() => Err(port_size_err(service_name)),
205         Some((_, post)) => {
206             port_name
207                 .try_reserve(
208                     TRUSTED_HAL_REPLACEMENT_PREFIX.len()
209                         + post.len()
210                         + TRUSTY_BINDER_RPC_PORT_SUFFIX.len(),
211                 )
212                 .map_err(|_| StatusCode::NO_MEMORY)?;
213 
214             port_name.push_str(TRUSTED_HAL_REPLACEMENT_PREFIX);
215             port_name.push_str(post);
216             port_name.push_str(TRUSTY_BINDER_RPC_PORT_SUFFIX);
217 
218             // Check size again. It's possible we weren't able to shorten the name enough.
219             if !is_valid_port_len(port_name.len()) {
220                 return Err(port_size_err(&port_name));
221             }
222 
223             Ok(port_name)
224         }
225     }
226 }
227 
is_valid_port_len(port_len: usize) -> bool228 fn is_valid_port_len(port_len: usize) -> bool {
229     // Note that if the name is equal to the max, we fail because
230     // these ports will eventually be represented as CStrings and passed
231     // to the trusty kernel to check their sizes so we need room for the
232     // nul byte.
233     port_len < MAX_TIPC_PORT_NAME_LEN
234 }
235 
port_size_err(service_name: &str) -> StatusCode236 fn port_size_err(service_name: &str) -> StatusCode {
237     log::error!(
238         "Cannot create port name from {:?} within trusty port length limit of {}",
239         service_name,
240         MAX_TIPC_PORT_NAME_LEN
241     );
242 
243     StatusCode::BAD_VALUE
244 }
245 
246 /// A helper to transform a binder service name to a trusty port name.
247 /// A suffix is added to the service name to identify it as a port that is serving
248 /// binders.
249 ///
250 /// Some known trusted hal services, those that start with android.hardware.security.see
251 /// are transformed to fit within trusty's port length limits.
service_name_to_trusty_port(service_name: &str) -> Result<String, StatusCode>252 pub fn service_name_to_trusty_port(service_name: &str) -> Result<String, StatusCode> {
253     let service_name_len = service_name.len() + TRUSTY_BINDER_RPC_PORT_SUFFIX.len();
254 
255     // TODO: b/403531416 - remove this once longer port names are supported in trusty
256     if !is_valid_port_len(service_name_len) {
257         return try_long_service_name_to_port(service_name);
258     }
259 
260     let mut port_name = String::new();
261 
262     port_name.try_reserve(service_name_len).map_err(|_| StatusCode::NO_MEMORY)?;
263 
264     port_name.push_str(service_name);
265     port_name.push_str(TRUSTY_BINDER_RPC_PORT_SUFFIX);
266 
267     Ok(port_name)
268 }
269 
270 /// Get a CString port name from a service_name.
271 /// See `service_name_to_trusty_port` for details.
service_name_to_trusty_c_port(service_name: &str) -> Result<CString, StatusCode>272 pub fn service_name_to_trusty_c_port(service_name: &str) -> Result<CString, StatusCode> {
273     let mut port_name = service_name_to_trusty_port(service_name)?;
274     // Ensure we have room for the null byte that CString construction will add.
275     port_name.try_reserve_exact(1).map_err(|_| StatusCode::NO_MEMORY)?;
276 
277     CString::new(port_name).map_err(|_| StatusCode::BAD_VALUE)
278 }
279