From 6da1c0643e61b808dc9b32cda352563cdddc2e38 Mon Sep 17 00:00:00 2001 From: Leo Jia Date: Thu, 15 Nov 2018 17:55:04 -0500 Subject: [PATCH] Add support for tmpfs secure key storage [rharwood@redhat.com: rewrote commit message] --- src/common.rs | 38 +++++++++++++ src/main.rs | 1 + src/secure_mount.rs | 129 ++++++++++++++++++++++++++++++++++++++++++++ src/tpm.rs | 5 +- 4 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 src/secure_mount.rs diff --git a/src/common.rs b/src/common.rs index d92cad12..4cbc9fd9 100644 --- a/src/common.rs +++ b/src/common.rs @@ -20,6 +20,15 @@ pub static IMA_ML_STUB: &'static str = pub static IMA_ML: &'static str = "/sys/kernel/security/ima/ascii_runtime_measurements"; pub static KEY: &'static str = "secret"; +pub static WORK_DIR: &'static str = "/tmp"; + +/* + * Temporaray location for configuration parameters + */ + +// cloud node +pub static SECURE_SIZE: &'static str = "1m"; +pub static MOUNT_SECURE: bool = true; /* * Input: Response status code @@ -99,6 +108,35 @@ pub fn get_restful_parameters(urlstring: &str) -> HashMap<&str, &str> { parameters } +/* + * Input: path directory to be changed owner to root + * Return: Result contains execution result + * - directory name for successful execution + * - -1 code for failure execution. + * + * If privilege requirement is met, change the owner of the path to root + * This function is unsafely using libc. Result is returned indicating + * execution result. + */ +pub fn chownroot(path: String) -> Result { + unsafe { + // check privilege + if libc::geteuid() != 0 { + error!("Privilege level unable to change ownership to root for file: {}", path); + return Err(-1); + } + + // 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); + } + + info!("Changed file {} owner to root.", path); + Ok(path) + } +} + // Unit Testing #[cfg(test)] mod tests { diff --git a/src/main.rs b/src/main.rs index e92b571c..9a0ce0c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ extern crate tempfile; mod common; mod crypto; +mod secure_mount; mod tpm; use futures::future; diff --git a/src/secure_mount.rs b/src/secure_mount.rs new file mode 100644 index 00000000..fd1d8ff5 --- /dev/null +++ b/src/secure_mount.rs @@ -0,0 +1,129 @@ +use super::*; + +use std::fs; +use std::os::unix::fs::PermissionsExt; +use std::process::Command; + +/* + * Input: secure mount directory + * Return: boolean + * - true if directory is mounted + * - false if not mounted + * + * Check the mount status of the secure mount directory. Same + * implementation as the original python version. + */ +fn check_mount(secure_dir: &str) -> bool { + let output = Command::new("mount") + .output() + .expect("Failed to execute mount command"); + + let mount_result = String::from_utf8(output.stdout).unwrap(); + let lines: Vec<&str> = mount_result.split("\n").collect(); + + // Check mount list for secure directory + for line in lines { + let tokens: Vec<&str> = line.split('/').collect(); + + if tokens.len() < 3 { + continue; + } + + if tokens[2] == secure_dir { + if &tokens[0] != &"tmpfs" { + error!("secure storage location {} already mounted on wrong file system type: {}. Unmount to continue.", secure_dir, tokens[0]); + debug!( + "secure storage location {} aleady mounted on tmpfs", + secure_dir + ); + } else { + return true; + } + } + } + debug!("secure storage location {} not mounted.", secure_dir); + false +} + +/* + * Return: Result contains secure mount directory or error code + * + * Mounted the work directory as tmpfs, which is owned by root. Same + * implementation as the original python version, but the chown/geteuid + * functions are unsafe function in Rust to use. + */ +fn mount() -> Result { + // use /tmpfs-dev directory if MOUNT_SECURE flag is setm, which doesn't + // mount to the system. This is for developement envrionment. + if !common::MOUNT_SECURE { + let secure_dir = format!("{}{}", common::WORK_DIR, "/tmpfs-dev"); + let secure_dir_path = Path::new(secure_dir.as_str()); + if !secure_dir_path.exists() { + match fs::create_dir(secure_dir_path) { + Ok(()) => { + return Ok(secure_dir_path.to_str().unwrap().to_string()) + } + Err(e) => { + error!("Failed to create directory, error {}", e); + return Err(-1); + } + } + } + } + + let secure_dir = format!("{}{}", common::WORK_DIR, "/secure"); + + // if the directory is not mount to file system, mount the directory to + // file system + if !check_mount(&secure_dir) { + let secure_dir_clone = secure_dir.clone(); + let secure_dir_path = Path::new(secure_dir_clone.as_str()); + + // create directory if the directory is not exist + if !secure_dir_path.exists() { + match fs::create_dir(secure_dir_path) { + Ok(()) => { + let metadata = fs::metadata(secure_dir_path).unwrap(); + let mut perm = metadata.permissions(); + + // This function support unix only + perm.set_mode(448); + } + + Err(e) => { + error!("Failed to create directory, error {}", e); + return Err(-1); + } + } + } + + info!( + "Mounting secure storage location {} on tmpfs.", + secure_dir_path.to_str().unwrap() + ); + + // change the secure path directory owner to root + match common::chownroot(secure_dir_path.to_str().unwrap().to_string()) + { + Ok(path) => info!("Changed path {} owner to root.", path), + Err(e) => { + error!("Failed to path owner with error code {}.", e); + return Err(-1); + } + } + + // mount tmpfs with secure directory + tpm::run( + format!( + "mount -t tmpfs -o size={},mode=0700 tmpfs {}", + common::SECURE_SIZE, + secure_dir_path.to_str().unwrap() + ), + tpm::EXIT_SUCCESS, + true, + false, + String::new(), + ); + } + Ok(secure_dir) +} diff --git a/src/tpm.rs b/src/tpm.rs index 51345e12..cdc702ed 100644 --- a/src/tpm.rs +++ b/src/tpm.rs @@ -22,7 +22,7 @@ const MAX_TRY: usize = 10; const RETRY_SLEEP: Duration = Duration::from_millis(50); const TPM_IO_ERROR: i32 = 5; const RETRY: usize = 4; -const EXIT_SUCCESS: i32 = 0; +pub const EXIT_SUCCESS: i32 = 0; static EMPTYMASK: &'static str = "1"; @@ -319,7 +319,6 @@ pub fn check_mask(ima_mask: String, ima_pcr: usize) -> bool { * Check the quote string, if it is deep quote string, return true, otherwise, * return false. Same as the original python version. */ -#[allow(dead_code)] pub fn is_deep_quote(quote: String) -> bool { let first_char = "e[0..1]; match first_char { @@ -356,7 +355,7 @@ Following are function from tpm_exec.py program * result in a tuple. Implement as original python version. Haven't * implemented tpm stubbing and metric. */ -fn run<'a>( +pub fn run<'a>( command: String, except_code: i32, raise_on_error: bool,