Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port run and KeylimeTpmError to unique modules #82

Merged
merged 2 commits into from
Oct 30, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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> {
Copy link
Member Author

@lukehinds lukehinds Oct 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps nit picking, but just noticed run is returning KeylimeTpmError which although works programatically, still reads wrong. If ok, I will refactor this in the tpm2 patch I am fixing up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems fine as long as we don't lose track of it.

let mut file_output = String::new();
let mut output: Output;

// tokenize input command
let words: Vec<&str> = command.split(" ").collect();
frozencemetery marked this conversation as resolved.
Show resolved Hide resolved
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.",
frozencemetery marked this conversation as resolved.
Show resolved Hide resolved
));
}

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