Skip to content

Commit

Permalink
Call revocation actions
Browse files Browse the repository at this point in the history
Signed-off-by: Lily Sturmann <[email protected]>
  • Loading branch information
lkatalin committed Feb 23, 2021
1 parent dccc791 commit 57c39a5
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 9 deletions.
16 changes: 11 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ rust-ini = "0.12.1"
rustc-serialize = "0.3.24"
serde = "1.0.80"
serde_derive = "1.0.80"
serde_json = "1.0"
serde_json = { version = "1.0", features = ["raw_value"] }
tempfile = "3.0.4"
tokio = {version = "0.2", features = ["full"]}
tokio-io = "0.1"
Expand Down
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub(crate) enum Error {
OpenSSL(openssl::error::ErrorStack),
#[error("ZMQ error: {0}")]
ZMQ(zmq::Error),
#[error("{0}")]
Other(String),
}

impl From<tss_esapi::Error> for Error {
Expand Down
171 changes: 169 additions & 2 deletions src/revocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,151 @@ use crate::error;
use crate::error::{Error, Result};
use crate::secure_mount;

use std::io::Write;
use std::path::Path;
use std::process::{Child, Command, Output, Stdio};

use serde_json::Value;

/// Runs a script with a json value as argument (used for revocation actions)
pub(crate) fn run_action(
dir: &Path,
script: &str,
json: Value,
) -> Result<Output> {
let raw_json = serde_json::value::to_raw_value(&json)?;

let mut child = match Command::new(format!("{}{}", "./", script))
.current_dir(dir)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
{
Ok(child) => child,
Err(e) => {
let msg = format!(
"ERROR: failed to run revocation action: {}, received: {}",
script, e
);
error!("{}", msg);
return Err(Error::Other(msg));
}
};

let msg = format!(
"ERROR: failed writing to stdin on revocation action: {:?}",
script
);
if let Err(e) = child
.stdin
.as_mut()
.expect(&msg)
.write_all(raw_json.get().as_bytes())
{
error!("{}", msg);
return Err(Error::Other(msg));
}

let output = match child.wait_with_output() {
Ok(output) => output,
Err(e) => {
let msg = format!("ERROR: failed to wait on child process while running revocation action: {:?}", script);
error!("{}", msg);
return Err(Error::Other(msg));
}
};

if !output.status.success() {
let code = match output.status.code() {
Some(code) => code.to_string(),
None => {
"no code; process likely terminated by signal".to_string()
}
};
let stdout = String::from_utf8(output.stdout)?;
let stderr = String::from_utf8(output.stderr)?;

let mut msg = format!(
"ERROR: revocation action {} returned with {}\n",
script, code
);

if !stdout.is_empty() {
msg = format!(
"{}{}",
msg,
format!(
"ERROR: revocation action {} stdout: {:?}",
script, stdout
)
);
}

if !stderr.is_empty() {
msg = format!(
"{}{}",
msg,
format!(
"ERROR: revocation action {} stderr: {:?}",
script, stderr
)
);
}

error!("{}", msg);
return Err(Error::Other(msg));
}

info!(
"{}",
format!("INFO: revocation action {:?} successful", script)
);
Ok(output)
}

/// Runs revocation actions received from tenant post-attestation
pub(crate) fn run_revocation_actions(
json: Value,
) -> Result<Vec<Result<Output>>> {
#[cfg(not(test))]
pretty_env_logger::init();

#[cfg(test)]
let mount = concat!(env!("CARGO_MANIFEST_DIR"), "/tests");

#[cfg(not(test))]
let mount = secure_mount::mount()?;
let unzipped = format!("{}/unzipped", mount);
let action_file = format!("{}/unzipped/action_list", mount);

let mut outputs = Vec::new();

if Path::new(&action_file).exists() {
let action_data = std::fs::read_to_string(action_file)
.expect("unable to read action_list");

let action_list = action_data
.split('\n')
.filter(|&script| !script.is_empty())
.map(|script| script.trim())
.collect::<Vec<&str>>();

if !action_list.is_empty() {
for action in action_list {
let output =
run_action(&Path::new(&unzipped), action, json.clone());
outputs.push(output);
}
} else {
warn!("WARNING: no actions found in revocation action list");
}
} else {
warn!("WARNING: no action_list found in secure directory");
}
Ok(outputs)
}

/// Handles revocation messages via 0mq
/// See:
/// - URL: https://github.com/keylime/keylime/blob/master/keylime/revocation_notifier.py
Expand Down Expand Up @@ -118,8 +259,7 @@ pub(crate) async fn run_revocation_service() -> Result<()> {
"Revocation signature validated for revocation: {}",
msg_payload
);
// TODO: Implement callback
//callback(msg_payload)
let _ = run_revocation_actions(msg_payload)?;
}
_ => {
error!("Invalid revocation message siganture {}", body);
Expand All @@ -128,3 +268,30 @@ pub(crate) async fn run_revocation_service() -> Result<()> {
}
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn revocation_scripts() {
let json_file =
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/unzipped/test.json");
let json_str = std::fs::read_to_string(json_file).unwrap(); //#[allow_ci]
let json = serde_json::from_str(&json_str).unwrap(); //#[allow_ci]

let outputs = run_revocation_actions(json);
assert!(outputs.is_ok());
let outputs = outputs.unwrap(); //#[allow_ci]
assert!(outputs.len() == 2);

for output in outputs {
assert!(output.is_ok());
let output = output.unwrap(); //#[allow_ci]
assert_eq!(
String::from_utf8(output.stdout).unwrap(), //#[allow_ci]
"there\n"
);
}
}
}
3 changes: 2 additions & 1 deletion tests/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ RUN dnf install -y \
swtpm swtpm-tools \
rust clippy cargo \
llvm llvm-devel clang pkg-config \
dbus-daemon czmq-devel
dbus-daemon czmq-devel \
python3
2 changes: 2 additions & 0 deletions tests/unzipped/action_list
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rev_script1.py
rev_script2.py
11 changes: 11 additions & 0 deletions tests/unzipped/rev_script1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/python3

import sys
import json

input_data = sys.stdin.read()
input_json = json.loads(input_data)

value = input_json.get('hello')

print(value)
11 changes: 11 additions & 0 deletions tests/unzipped/rev_script2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/python3

import sys
import json

input_data = sys.stdin.read()
input_json = json.loads(input_data)

value = input_json.get('hello')

print(value)
3 changes: 3 additions & 0 deletions tests/unzipped/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"hello": "there"
}

0 comments on commit 57c39a5

Please sign in to comment.