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