Skip to content

Commit

Permalink
Port run and KeylimeTpmError to unique modules
Browse files Browse the repository at this point in the history
This change ports the `run` function from `tpm.rs` into its own
file / module `cmd_exec.rs` - primary reason for this is that non
tpm functions also call on the `run` function, so its not
exclusive to tpm operations (for example secure_mount uses run)

For `KeylimeTpmError` - this has been ported into a module
`keylime_error`, so that we can group all errors into a single
module, as it makes sense that we will eventually have more
error types and have them neatly self contained in single module

Resolves: #81
  • Loading branch information
Luke Hinds authored and lukehinds committed Oct 30, 2019
1 parent ff1543e commit d6203e0
Show file tree
Hide file tree
Showing 5 changed files with 326 additions and 281 deletions.
195 changes: 195 additions & 0 deletions src/cmd_exec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
extern crate base64;

use super::*;
use keylime_error;
use std::env;
use std::process::Command;
use std::process::Output;
use std::str;
use std::thread;
use std::time::Duration;
use std::time::SystemTime;

const MAX_TRY: usize = 10;
const RETRY_SLEEP: Duration = Duration::from_millis(50);
const TPM_IO_ERROR: i32 = 5;
const RETRY: usize = 4;
static EMPTYMASK: &'static str = "1";

/*
* Input:
* command: command to be executed
* output_path: file output location
* return:
* execution return output and file output
* KeylimeTpmError
*
* Set up execution envrionment to execute tpm command through shell commands
* and return the execution result in a tuple. Based on the latest update of
* python keylime this function implement the functionality of cmd_exec
* script in the python keylime repo. RaiseOnError, return code and lock are
* dropped due to different error handling in Rust. Returned output string are
* preprocessed to before returning for code efficient.
*/
pub fn run<'a>(
command: String,
output_path: Option<&str>,
) -> Result<(String, String), keylime_error::KeylimeTpmError> {
let mut file_output = String::new();
let mut output: Output;

// tokenize input command
let words: Vec<&str> = command.split(" ").collect();
let mut number_tries = 0;
let args = &words[1..words.len()];
let cmd = &words[0];

// setup environment variable
let mut env_vars: HashMap<String, String> = HashMap::new();
for (key, value) in env::vars() {
env_vars.insert(key.to_string(), value.to_string());
}
env_vars.insert("TPM_SERVER_PORT".to_string(), "9998".to_string());
env_vars.insert("TPM_SERVER_NAME".to_string(), "localhost".to_string());
match env_vars.get_mut("PATH") {
Some(v) => v.push_str(common::TPM_TOOLS_PATH),
None => {
return Err(keylime_error::KeylimeTpmError::new_tpm_rust_error(
"PATH envrionment variable dosen't exist.",
));
}
}

// main loop
'exec: loop {
// Start time stamp
let t0 = SystemTime::now();

output = Command::new(&cmd).args(args).envs(&env_vars).output()?;

// measure execution time
let t_diff = t0.duration_since(t0)?;
info!("Time cost: {}", t_diff.as_secs());

// assume the system is linux
println!("number tries: {:?}", number_tries);

match output.status.code() {
Some(TPM_IO_ERROR) => {
number_tries += 1;
if number_tries >= MAX_TRY {
return Err(keylime_error::KeylimeTpmError::new_tpm_error(
TPM_IO_ERROR,
"TPM appears to be in use by another application.
Keylime is incompatible with other TPM TSS
applications like trousers/tpm-tools. Please
uninstall or disable.",
));
}

info!(
"Failed to call TPM {}/{} times, trying again in {} secs.",
number_tries,
MAX_TRY,
RETRY,
);

thread::sleep(RETRY_SLEEP);
}

_ => break 'exec,
}
}

let return_output = String::from_utf8(output.stdout)?;
match output.status.code() {
None => {
return Err(keylime_error::KeylimeTpmError::new_tpm_rust_error(
"Execution return code is None.",
));
}
Some(0) => info!("Successfully executed TPM command."),
Some(c) => {
return Err(keylime_error::KeylimeTpmError::new_tpm_error(
c,
format!(
"Command: {} returned {}, output {}",
command, c, return_output,
)
.as_str(),
));
}
}

// Retrive data from output path file
if let Some(p) = output_path {
file_output = read_file_output_path(p.to_string())?;
}

Ok((return_output, file_output))
}

/*
* Input: file name
* Return: the content of the file int Result<>
*
* run method helper method
* read in the file and return the content of the file into a Result enum
*/
fn read_file_output_path(output_path: String) -> std::io::Result<String> {
let mut file = File::open(output_path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}

#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
use std::fs;

#[test]
fn test_read_file_output_path() {
assert_eq!(
read_file_output_path("test-data/test_input.txt".to_string())
.unwrap(),
"Hello World!\n"
);
}

#[test]
fn test_run_command() {
match command_exist("getrandom") {
true => {
let command = "getrandom -size 8 -out foo.out".to_string();
cmd_exec::run(command, None);
let p = Path::new("foo.out");
assert_eq!(p.exists(), true);
match fs::remove_file("foo.out") {
Ok(_) => {}
Err(_) => {}
}
}
false => assert!(true),
}
}
/*
* Input: command name
* Output: checkout command result
*
* Look for the command in path, if command is there return true, if
* command is not exist return false.
*/
fn command_exist(command: &str) -> bool {
if let Ok(path) = env::var("PATH") {
for pp in path.split(":") {
let command_path = format!("{}/{}", pp, command);
if fs::metadata(command_path).is_ok() {
return true;
}
}
}
false
}
}
92 changes: 92 additions & 0 deletions src/keylime_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use super::*;
use std::error::Error;
use std::fmt;

/*
* Custom Error type for tpm execution error. It contains both error from the
* TPM command execution result or error cause by rust function. Potential
* rust error are map to this error by implemented From<> trait.
*/
#[derive(Debug)]
pub enum KeylimeTpmError {
TpmRustError { details: String },
TpmError { code: i32, details: String },
}

impl KeylimeTpmError {
pub fn new_tpm_error(err_code: i32, err_msg: &str) -> KeylimeTpmError {
KeylimeTpmError::TpmError {
code: err_code,
details: err_msg.to_string(),
}
}

pub fn new_tpm_rust_error(err_msg: &str) -> KeylimeTpmError {
KeylimeTpmError::TpmRustError {
details: err_msg.to_string(),
}
}
}

impl Error for KeylimeTpmError {
fn description(&self) -> &str {
match &self {
KeylimeTpmError::TpmError {
ref details,
ref code,
} => details,
KeylimeTpmError::TpmRustError { ref details } => details,
}
}
}

impl fmt::Display for KeylimeTpmError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
KeylimeTpmError::TpmError {
ref code,
ref details,
} => write!(
f,
"Execute TPM command failed with Error Code: [{}] and
Error Message [{}].",
code, details,
),
KeylimeTpmError::TpmRustError { ref details } => write!(
f,
"Error occur in TPM rust interface with message [{}].",
details,
),
}
}
}

impl From<std::io::Error> for KeylimeTpmError {
fn from(e: std::io::Error) -> KeylimeTpmError {
KeylimeTpmError::new_tpm_rust_error(e.description())
}
}

impl From<std::time::SystemTimeError> for KeylimeTpmError {
fn from(e: std::time::SystemTimeError) -> KeylimeTpmError {
KeylimeTpmError::new_tpm_rust_error(e.description())
}
}

impl From<std::string::FromUtf8Error> for KeylimeTpmError {
fn from(e: std::string::FromUtf8Error) -> KeylimeTpmError {
KeylimeTpmError::new_tpm_rust_error(e.description())
}
}

impl From<serde_json::error::Error> for KeylimeTpmError {
fn from(e: serde_json::error::Error) -> KeylimeTpmError {
KeylimeTpmError::new_tpm_rust_error(e.description())
}
}

impl From<std::num::ParseIntError> for KeylimeTpmError {
fn from(e: std::num::ParseIntError) -> KeylimeTpmError {
KeylimeTpmError::new_tpm_rust_error(e.description())
}
}
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ extern crate rustc_serialize;
extern crate serde;
extern crate tempfile;

mod cmd_exec;
mod common;
mod crypto;
mod keylime_error;
mod secure_mount;
mod tpm;

Expand Down
2 changes: 1 addition & 1 deletion src/secure_mount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ fn mount() -> Result<String, Box<String>> {
})?;

// mount tmpfs with secure directory
tpm::run(
cmd_exec::run(
format!(
"mount -t tmpfs -o size={},mode=0700 tmpfs {}",
secure_size, s,
Expand Down
Loading

0 comments on commit d6203e0

Please sign in to comment.