optee_teec/session.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use super::context::InnerContext;
19use crate::{Context, Operation, Param, Result, Uuid, raw, teec_check_with_origin};
20use num_enum::{FromPrimitive, IntoPrimitive};
21use std::{
22 ptr,
23 sync::{Arc, Mutex},
24};
25
26/// Session login methods.
27#[derive(Copy, Clone, Debug, PartialEq, FromPrimitive, IntoPrimitive)]
28#[repr(u32)]
29pub enum ConnectionMethods {
30 /// No login data is provided.
31 LoginPublic = raw::TEEC_LOGIN_PUBLIC,
32 /// Login data about the user running the Client Application process is provided.
33 LoginUser = raw::TEEC_LOGIN_USER,
34 /// Login data about the group running the Client Application process is provided.
35 LoginGroup = raw::TEEC_LOGIN_GROUP,
36 /// Login data about the running Client Application itself is provided.
37 LoginApplication = raw::TEEC_LOGIN_APPLICATION,
38 /// Login data about the user and the running Client Application itself is provided.
39 LoginUserApplication = raw::TEEC_LOGIN_USER_APPLICATION,
40 /// Login data about the group and the running Client Application itself is provided.
41 LoginGroupApplication = raw::TEEC_LOGIN_GROUP_APPLICATION,
42 /// An unnamed or implementation-defined login type.
43 #[num_enum(catch_all)]
44 Unknown(u32),
45}
46
47/// An opaque, thread-movable OP-TEE session handle.
48///
49/// `raw::TEEC_Session` mirrors a C struct containing a `*mut`, so Rust cannot
50/// infer `Send`. libteec treats the handle as plain data: `TEEC_InvokeCommand`
51/// and `TEEC_CloseSession` read `session->imp.session_id` / `session->imp.ctx`
52/// and perform an ioctl, with no thread affinity (see `tee_client_api.c` in
53/// optee_client). The handle is kept opaque here; the binding does not rely on
54/// its layout.
55struct RawTeecSession(raw::TEEC_Session);
56
57// SAFETY: `RawTeecSession` is the sole owner of the handle and only ever hands
58// it out as `&mut` (see `get_mut`), so callers cannot alias it. Moving it
59// between threads is allowed by the libteec API contract: the handle is not
60// thread-affine, and the owning `Context` (which owns the underlying fd) is
61// kept alive separately by `Session::_ctx`. This assertion covers ownership
62// transfer only; it does not imply `Sync`, and `Session` is deliberately not
63// `Sync`.
64unsafe impl Send for RawTeecSession {}
65
66impl RawTeecSession {
67 fn new(raw: raw::TEEC_Session) -> Self {
68 Self(raw)
69 }
70
71 fn get_mut(&mut self) -> &mut raw::TEEC_Session {
72 &mut self.0
73 }
74}
75
76/// Represents a connection between a client application and a trusted application.
77pub struct Session {
78 raw: RawTeecSession,
79
80 // Just a holder to ensure InnerContext is not dropped and to eliminate the
81 // lifetime constraint, never use it.
82 _ctx: Arc<Mutex<InnerContext>>,
83}
84
85impl Session {
86 /// Initializes a TEE session object with specified context and uuid.
87 pub fn new<A: Param, B: Param, C: Param, D: Param>(
88 context: &mut Context,
89 uuid: Uuid,
90 login: ConnectionMethods,
91 operation: Option<&mut Operation<A, B, C, D>>,
92 ) -> Result<Self> {
93 // SAFETY:
94 // raw_session is a C struct(TEEC_Session), which zero value is valid.
95 let mut raw_session = unsafe { std::mem::zeroed() };
96 // define all parameters for raw::TEEC_OpenSession outside of the unsafe
97 // block to maximize Rust's safety checks and leverage the compiler's
98 // validation.
99 let mut err_origin: u32 = 0;
100 let raw_operation = operation.map_or(ptr::null_mut(), |o| o.as_mut_raw_ptr());
101 let inner_ctx = context.inner_context();
102 let mut guard = match inner_ctx.lock() {
103 Ok(guard) => guard,
104 // A poisoned lock only records that another thread panicked while
105 // holding the context. The underlying `TEEC_Context` is still
106 // usable, so recover it rather than failing this unrelated call.
107 Err(poisoned) => poisoned.into_inner(),
108 };
109 let raw_ctx = &mut guard.0;
110 let raw_uuid = uuid.as_raw_ptr();
111
112 teec_check_with_origin(
113 unsafe {
114 raw::TEEC_OpenSession(
115 raw_ctx,
116 &mut raw_session,
117 raw_uuid,
118 u32::from(login),
119 ptr::null(),
120 raw_operation,
121 &mut err_origin,
122 )
123 },
124 err_origin,
125 )?;
126 Ok(Self {
127 raw: RawTeecSession::new(raw_session),
128 _ctx: context.inner_context(),
129 })
130 }
131
132 /// Invokes a command with an operation with this session.
133 pub fn invoke_command<A: Param, B: Param, C: Param, D: Param>(
134 &mut self,
135 command_id: u32,
136 operation: &mut Operation<A, B, C, D>,
137 ) -> Result<()> {
138 let mut err_origin: u32 = 0;
139 teec_check_with_origin(
140 unsafe {
141 raw::TEEC_InvokeCommand(
142 self.raw.get_mut(),
143 command_id,
144 operation.as_mut_raw_ptr(),
145 &mut err_origin,
146 )
147 },
148 err_origin,
149 )
150 }
151}
152
153impl Drop for Session {
154 fn drop(&mut self) {
155 unsafe {
156 raw::TEEC_CloseSession(self.raw.get_mut());
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::ConnectionMethods;
164 use super::raw;
165
166 /// Guarantee: every `ConnectionMethods` variant converts to the matching
167 /// `TEEC_LOGIN_*` constant and the conversion round-trips through the
168 /// primitive login code, so a wrong discriminant can never be sent to
169 /// `TEEC_OpenSession`.
170 #[test]
171 fn connection_methods_round_trip_to_tee_constants() {
172 let cases = [
173 (ConnectionMethods::LoginPublic, raw::TEEC_LOGIN_PUBLIC),
174 (ConnectionMethods::LoginUser, raw::TEEC_LOGIN_USER),
175 (ConnectionMethods::LoginGroup, raw::TEEC_LOGIN_GROUP),
176 (
177 ConnectionMethods::LoginApplication,
178 raw::TEEC_LOGIN_APPLICATION,
179 ),
180 (
181 ConnectionMethods::LoginUserApplication,
182 raw::TEEC_LOGIN_USER_APPLICATION,
183 ),
184 (
185 ConnectionMethods::LoginGroupApplication,
186 raw::TEEC_LOGIN_GROUP_APPLICATION,
187 ),
188 ];
189
190 for (method, expected) in cases {
191 let code = u32::from(method);
192 assert_eq!(code, expected, "variant {method:?} has wrong TEEC code");
193
194 let round_tripped = ConnectionMethods::from(code);
195 assert_eq!(round_tripped, method);
196 }
197 }
198}