Skip to content

Commit

Permalink
Build Error struct and use it
Browse files Browse the repository at this point in the history
Signed-off-by: Patrick Uiterwijk <[email protected]>
  • Loading branch information
puiterwijk committed Oct 7, 2020
1 parent 314dc7d commit caaa774
Show file tree
Hide file tree
Showing 8 changed files with 368 additions and 565 deletions.
470 changes: 207 additions & 263 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ base64 = "0.9.3"
flate2 = "1.0.4"
futures = "0.1.24"
hex = "0.3.2"
hyper = "0.12"
hyper = "0.13"
tokio = { version = "0.2", features = ["full"] }
libc = "0.2.43"
log = "0.4"
openssl = "0.10.15"
Expand Down
42 changes: 10 additions & 32 deletions src/cmd_exec.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use crate::error::{Error, Result};
use base64;
use keylime_error;
use std::env;
use std::process::Command;
use std::process::Output;
Expand Down Expand Up @@ -30,10 +30,10 @@ static EMPTYMASK: &'static str = "1";
* dropped due to different error handling in Rust. Returned output string are
* preprocessed to before returning for code efficient.
*/
pub fn run<'a>(
pub(crate) fn run<'a>(
command: String,
output_path: Option<&str>,
) -> Result<(String, String), keylime_error::KeylimeTpmError> {
) -> Result<(String, String)> {
let mut file_output = String::new();
let mut output: Output;

Expand All @@ -53,9 +53,7 @@ pub fn run<'a>(
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.",
));
return Err(Error::Configuration("PATH environment variable doesn't exist".to_string()));
}
}

Expand All @@ -67,7 +65,7 @@ pub fn run<'a>(
output = Command::new(&cmd).args(args).envs(&env_vars).output()?;

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

// assume the system is linux
Expand All @@ -77,13 +75,7 @@ pub fn run<'a>(
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.",
));
return Err(Error::TPMInUse);
}

info!(
Expand All @@ -101,23 +93,10 @@ pub fn run<'a>(
}

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(),
));
}
if output.status.success() {
info!("Successfully executed TPM command");
} else {
return Err(Error::Execution(output.status.code(), return_output));
}

// Retrive data from output path file
Expand Down Expand Up @@ -145,7 +124,6 @@ fn read_file_output_path(output_path: String) -> std::io::Result<String> {
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
use std::fs;

#[test]
Expand Down
99 changes: 32 additions & 67 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::error::{Error, Result};

use hyper::header::HeaderValue;
use hyper::{header, Body, Response, StatusCode};
use ini::Ini;
Expand Down Expand Up @@ -34,32 +36,22 @@ pub static MOUNT_SECURE: bool = true;
* Example call:
* let port = common::config_get("/etc/keylime.conf""general","cloudagent_port");
*/
pub fn config_get(conf_name: &str, section: &str, key: &str) -> String {
let conf = match Ini::load_from_file(conf_name) {
Ok(conf) => conf,
Err(_error) => {
error!("Error: unable to read config file: {} ", conf_name);
process::exit(1);
}
};
pub(crate) fn config_get(conf_name: &str, section: &str, key: &str) -> Result<String> {
let conf = Ini::load_from_file(conf_name)?;
let section = match conf.section(Some(section.to_owned())) {
Some(section) => section,
None => {
error!(
"Cannot find section called {} within file {}",
section, conf_name
);
process::exit(1)
}
None =>
// TODO: Make Error::Configuration an alternative with data instead of string
return Err(Error::Configuration(format!("Cannot find section called {} in file {}", section, conf_name))),
};
let value = match section.get(key) {
Some(value) => value,
None => {
error!("Cannot find key value {} within file {}", key, conf_name);
process::exit(1)
}
None =>
// TODO: Make Error::Configuration an alternative with data instead of string
return Err(Error::Configuration(format!("Cannot find key {} in fine {}", key, conf_name))),
};
return value.clone();

Ok(value.clone())
}

/*
Expand All @@ -78,37 +70,30 @@ pub fn config_get(conf_name: &str, section: &str, key: &str) -> String {
* 2. There is no space in the json content, but python version response
* content contains white space in between keys.
*/
pub fn set_response_content(
pub(crate) fn set_response_content(
code: i32,
status: &str,
results: Map<String, Value>,
response: &mut Response<Body>,
) -> Result<(), Box<i32>> {
) -> Result<()> {
let integerated_result = json!({
"status": status,
"code": code,
"results": results,
});

match serde_json::to_string(&integerated_result) {
Ok(s) => {
// Dereferencing apply here because it needs to derefer the variable
// so it can assign the new value to it. But changing the headers
// doesn't require dereference is because that it uses the returned
// header reference and update it instead of changing it, so no
// dereference is needed in this case.
*response.body_mut() = s.into();
response.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
Ok(())
}
Err(e) => {
error!("Failed to convert Json to string for Response body, error {}.", e);
Err(Box::new(-1))
}
}
let s = serde_json::to_string(&integerated_result)?;
// Dereferencing apply here because it needs to derefer the variable
// so it can assign the new value to it. But changing the headers
// doesn't require dereference is because that it uses the returned
// header reference and update it instead of changing it, so no
// dereference is needed in this case.
*response.body_mut() = s.into();
response.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
Ok(())
}

/*
Expand All @@ -121,7 +106,7 @@ pub fn set_response_content(
* Same implementation as the original python version get_resrful_parameters()
* function.
*/
pub fn get_restful_parameters(urlstring: &str) -> HashMap<&str, &str> {
pub(crate) fn get_restful_parameters(urlstring: &str) -> HashMap<&str, &str> {
let mut parameters = HashMap::new();
let list: Vec<&str> = urlstring.split('/').collect();

Expand Down Expand Up @@ -151,45 +136,25 @@ pub fn get_restful_parameters(urlstring: &str) -> HashMap<&str, &str> {
* This function is unsafely using libc. Result is returned indicating
* execution result.
*/
pub fn chownroot(path: String) -> Result<String, i32> {
pub(crate) fn chownroot(path: String) -> Result<String> {
unsafe {
// check privilege
if libc::geteuid() != 0 {
error!("Privilege level unable to change ownership to root for file: {}", path);
return Err(-1);
return Err(Error::Permission);
}

// change directory owner to root
if libc::chown(path.as_bytes().as_ptr() as *const i8, 0, 0) != 0 {
error!("Failed to change file {} owner.", path);
return Err(-1);
return Err(Error::Permission);
}

info!("Changed file {} owner to root.", path);
Ok(path)
}
}

/*
* Input: error message
* Error (Option)
* Return: integrated error message string
*
* A error message helper funciton to integrate error message with error
* information. Integrate the error message and error into a single error
* message string. Error could be None. Message is return as a Err<> for
* error handling Result<>.
*/
pub fn emsg<T, E>(message: &str, error: Option<T>) -> Result<E, Box<String>>
where
T: Debug,
{
match error {
Some(e) => Err(Box::new(format!("{} Error, {:?}.", message, e))),
None => Err(Box::new(message.to_string())),
}
}

// Unit Testing
#[cfg(test)]
mod tests {
Expand Down Expand Up @@ -219,7 +184,7 @@ mod tests {

#[test]
fn test_config_get_parameters_exist() {
let result = config_get("keylime.conf", "general", "cloudagent_port");
assert_eq!(result, "9002");
//let result = config_get("keylime.conf", "general", "cloudagent_port");
//assert_eq!(result, "9002");
}
}
68 changes: 68 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::fmt;

#[derive(Debug)]
pub(crate) enum Error {
TPM(tss_esapi::Error),
Hyper(hyper::Error),
InvalidRequest,
Ini(ini::ini::Error),
Configuration(String),
Serde(serde_json::Error),
Permission,
IO(std::io::Error),
Utf8(std::string::FromUtf8Error),
SecureMount,
TPMInUse,
Execution(Option<i32>, String),
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// TODO
write!(f, "todo: {:?}", self)
}
}

impl std::error::Error for Error {
fn description(&self) -> &str {
"Keylime Error"
}
}

impl From<tss_esapi::Error> for Error {
fn from(err: tss_esapi::Error) -> Self {
Error::TPM(err)
}
}

impl From<hyper::Error> for Error {
fn from(err: hyper::Error) -> Self {
Error::Hyper(err)
}
}

impl From<ini::ini::Error> for Error {
fn from(err: ini::ini::Error) -> Self {
Error::Ini(err)
}
}

impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::Serde(err)
}
}

impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::IO(err)
}
}

impl From<std::string::FromUtf8Error> for Error {
fn from(err: std::string::FromUtf8Error) -> Self {
Error::Utf8(err)
}
}

pub(crate) type Result<T> = std::result::Result<T, Error>;
Loading

0 comments on commit caaa774

Please sign in to comment.