Skip to main content

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}