-
Notifications
You must be signed in to change notification settings - Fork 39
/
mod.rs
283 lines (253 loc) · 10.2 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
//! Management module for the Veracruz execution engine.
//!
//! ## Authors
//!
//! The Veracruz Development Team.
//!
//! ## Licensing and copyright notice
//!
//! See the `LICENSE_MIT.markdown` file in the Veracruz root directory for
//! information on licensing and copyright.
use anyhow::{anyhow, Result};
use execution_engine::{execute, fs::FileSystem};
use lazy_static::lazy_static;
use log::info;
use policy_utils::{
pipeline::Expr, policy::Policy, principal::Principal, CANONICAL_STDIN_FILE_PATH,
};
use std::{
collections::HashMap,
path::PathBuf,
string::String,
sync::{
atomic::{AtomicBool, AtomicU32, Ordering},
Mutex,
},
vec::Vec,
};
use veracruz_utils::sha256::sha256;
use wasi_types::{ErrNo, Rights};
pub mod error;
pub mod execution_engine_manager;
pub mod session_manager;
pub use error::RuntimeManagerError;
////////////////////////////////////////////////////////////////////////////////
// Various bits of persistent state.
////////////////////////////////////////////////////////////////////////////////
lazy_static! {
static ref MY_SESSION_MANAGER: Mutex<Option<::session_manager::SessionContext>> =
Mutex::new(None);
static ref SESSION_COUNTER: AtomicU32 = AtomicU32::new(1);
static ref SESSIONS: Mutex<HashMap<u32, ::session_manager::Session>> =
Mutex::new(HashMap::new());
static ref PROTOCOL_STATE: Mutex<Option<ProtocolState>> = Mutex::new(None);
static ref DEBUG_FLAG: AtomicBool = AtomicBool::new(false);
}
////////////////////////////////////////////////////////////////////////////////
// Error and response codes and messages.
////////////////////////////////////////////////////////////////////////////////
/// `None` means that the incoming buffer is not full,
/// so we cannot parse a complete protobuf message.
/// We need to wait longer for this to arrive.
type ProvisioningResponse = Option<Vec<u8>>;
/// Result type of provisioning functions.
pub type ProvisioningResult = Result<ProvisioningResponse>;
/// The configuration details for the ongoing provisioning of secrets into the
/// Veracruz platform, containing information that must be persisted across the
/// different rounds of the provisioning process and the fixed global policy.
pub(crate) struct ProtocolState {
/// The fixed, global policy parameterising the computation. This should
/// not change...
global_policy: Policy,
/// A hex-encoding of the raw JSON global policy.
global_policy_hash: String,
/// The list of clients (their IDs) that can request shutdown of the
/// Veracruz platform.
expected_shutdown_sources: Vec<u64>,
/// The ref to the VFS, this is a FS handler with super user capability.
vfs: FileSystem,
/// Digest table. Certain files must match the digest before writing to
/// the filesystem.
digest_table: HashMap<PathBuf, Vec<u8>>,
}
impl ProtocolState {
/// Constructs a new `ProtocolState` from a global policy. The selected
/// execution strategy is extracted from the global policy and a suitable
/// Veracruz execution strategy is selected based on that.
pub fn new(global_policy: Policy, global_policy_hash: String) -> Result<Self> {
let expected_shutdown_sources = global_policy.expected_shutdown_list();
let mut rights_table = global_policy.get_rights_table();
// Grant the super user read access to any file under the root. This is
// used internally to read the program on behalf of the executing party
let mut su_read_rights = HashMap::new();
su_read_rights.insert(PathBuf::from("/"), Rights::all());
rights_table.insert(Principal::InternalSuperUser, su_read_rights);
let digest_table = global_policy.get_file_hash_table()?;
let native_modules = global_policy.native_modules();
let vfs = FileSystem::new(rights_table, native_modules.to_vec())?;
Ok(ProtocolState {
global_policy,
global_policy_hash,
expected_shutdown_sources,
vfs,
digest_table,
})
}
/// Returns the global policy's hash, a hex-encoded string.
#[inline]
pub(crate) fn get_policy_hash(&self) -> &str {
&self.global_policy_hash
}
////////////////////////////////////////////////////////////////////////////
// The ExecutionEngine facade.
////////////////////////////////////////////////////////////////////////////
/// Check if a client has capability to write to a file, and then overwrite it with new `data`.
pub(crate) fn write_file(
&mut self,
client_id: &Principal,
file_name: &str,
data: Vec<u8>,
) -> Result<()> {
// Check the digest, if necessary
if let Some(digest) = self.digest_table.get(&PathBuf::from(file_name)) {
let incoming_digest = sha256(&data);
if incoming_digest.len() != digest.len() {
return Err(anyhow!(RuntimeManagerError::FileSystemError(ErrNo::Access)));
}
for (lhs, rhs) in digest.iter().zip(incoming_digest.iter()) {
if lhs != rhs {
return Err(anyhow!(RuntimeManagerError::FileSystemError(ErrNo::Access)));
}
}
}
if file_name == CANONICAL_STDIN_FILE_PATH {
self.vfs.spawn(client_id)?.write_stdin(&data)?;
} else {
self.vfs
.spawn(client_id)?
.write_file_by_absolute_path(file_name, data, false)?;
}
Ok(())
}
/// Check if a client has capability to write to a file, and then overwrite it with new `data`.
pub(crate) fn append_file(
&mut self,
client_id: &Principal,
file_name: &str,
data: Vec<u8>,
) -> Result<()> {
// If a file must match a digest, e.g. a program,
// it is not permitted to append the file.
if self.digest_table.contains_key(&PathBuf::from(file_name)) {
return Err(anyhow!(RuntimeManagerError::FileSystemError(ErrNo::Access)));
}
self.vfs.spawn(client_id)?.write_file_by_absolute_path(
file_name, data, // set the append flag to true
true,
)?;
Ok(())
}
/// Check if a client has capability to read from a file, if so, return the content in bytes.
pub(crate) fn read_file(
&self,
client_id: &Principal,
file_name: &str,
) -> Result<Option<Vec<u8>>> {
let mut vfs = self.vfs.spawn(client_id)?;
let rst = match file_name {
"stderr" => vfs.read_stderr()?,
"stdout" => vfs.read_stdout()?,
_otherwise => vfs.read_file_by_absolute_path(file_name)?,
};
if rst.len() == 0 {
return Ok(None);
}
Ok(Some(rst))
}
pub(crate) fn read_pipeline_script(&self, pipeline_id: usize) -> Result<Box<Expr>> {
info!("try tp read pipeline_id {}.", pipeline_id);
let expr = self
.global_policy
.get_pipeline(pipeline_id)?
.get_parsed_pipeline()
.map(|e| e.clone())?;
info!("result {:?}", expr);
Ok(expr)
}
/// Requests shutdown on behalf of a client, as identified by their client
/// ID.
/// TODO: Do something better (https://github.com/veracruz-project/veracruz/issues/393)
pub(crate) fn request_and_check_shutdown(&mut self, client_id: u64) -> Result<bool> {
Ok(self.expected_shutdown_sources.contains(&client_id))
}
/// Execute the program `file_name` on behalf of the client (participant)
/// identified by `principal`. The client must have the right to execute the
/// program.
pub(crate) fn execute(
&mut self,
caller_principal: &Principal,
execution_principal: &Principal,
environment_variables: Vec<(String, String)>,
pipeline: Box<Expr>,
) -> ProvisioningResult {
info!(
"Execute program, caller: {:?} and execution: {:?}",
caller_principal, execution_principal
);
let execution_strategy = self.global_policy.execution_strategy();
let options = execution_engine::Options {
enable_clock: *self.global_policy.enable_clock(),
environment_variables,
..Default::default()
};
let return_code = execute(
&execution_strategy,
self.vfs.spawn(caller_principal)?,
self.vfs.spawn(execution_principal)?,
pipeline,
&options,
)?;
let response = Self::response_error_code_returned(return_code);
Ok(Some(response))
}
/// Internal function converts error code to response message.
#[inline]
fn response_error_code_returned(error_code: u32) -> std::vec::Vec<u8> {
transport_protocol::serialize_result(
transport_protocol::ResponseStatus::SUCCESS as i32,
Some(error_code.to_le_bytes().to_vec()),
)
.unwrap_or_else(|err| panic!("{:?}", err))
}
}
////////////////////////////////////////////////////////////////////////////////
// Debug printing outside of the enclave.
////////////////////////////////////////////////////////////////////////////////
/// Prints a debug message, `message`, via our debug OCALL print mechanism, if
/// the debug configuration flat is set for the enclave. Has no effect,
/// otherwise.
pub fn debug_message(message: String) {
if DEBUG_FLAG.load(Ordering::SeqCst) {
print_message(message, 0);
}
}
/// Prints an error message, `message`, with a fixed error code, `error_code`,
/// via our debug OCALL print mechanism, if the debug configuration flat is set
/// for the enclave. Has no effect, otherwise.
pub fn error_message(message: String, error_code: u32) {
print_message(message, error_code);
}
/// Base function for printing messages outside of the enclave. Note that this
/// should only print something to *stdout* on the host's machine if the debug
/// configuration flag is set in the Veracruz global policy.
fn print_message(#[allow(unused)] message: String, #[allow(unused)] code: u32) {
#[cfg(feature = "linux")]
if code == 0 {
eprintln!("Enclave debug message \"{}\"", message);
} else {
eprintln!(
"Enclave returns error code {} and message \"{}\"",
code, message
);
}
}