-
Notifications
You must be signed in to change notification settings - Fork 59
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
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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 returningKeylimeTpmError
which although works programatically, still reads wrong. If ok, I will refactor this in the tpm2 patch I am fixing up.There was a problem hiding this comment.
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.