diff --git a/deps/sefs b/deps/sefs index a843c678..2a7101f0 160000 --- a/deps/sefs +++ b/deps/sefs @@ -1 +1 @@ -Subproject commit a843c6783ae14842225bb6777616dc707a657583 +Subproject commit 2a7101f074439c5c70a1ad5d4d171227817eaf19 diff --git a/src/libos/src/config.rs b/src/libos/src/config.rs index 297fdfae..f3e90f6c 100644 --- a/src/libos/src/config.rs +++ b/src/libos/src/config.rs @@ -1,6 +1,7 @@ use super::*; use serde::{Deserialize, Serialize}; use std::sgxfs::{SgxFile}; +use std::path::{Path, PathBuf}; const LIBOS_CONFIG_PATH : &str = "Occlum.json.protected"; @@ -103,16 +104,24 @@ pub struct ConfigProcess { #[derive(Debug)] pub struct ConfigMount { - pub type_: String, - pub target: String, - pub source: Option, + pub type_: ConfigMountFsType, + pub target: PathBuf, + pub source: Option, pub options: ConfigMountOptions, } +#[derive(Debug, PartialEq)] +#[allow(non_camel_case_types)] +pub enum ConfigMountFsType { + TYPE_SEFS, + TYPE_HOSTFS, + TYPE_RAMFS, +} + #[derive(Debug)] pub struct ConfigMountOptions { pub integrity_only: bool, - pub mac: Option, + pub mac: Option, } @@ -155,21 +164,22 @@ impl ConfigMount { fn from_input(input: &InputConfigMount) -> Result { const ALL_FS_TYPES : [&str; 3] = [ "sefs", "hostfs", "ramfs" ]; - let type_ = { - let type_ = input.type_.to_string(); - if !ALL_FS_TYPES.contains(&type_.as_str()) { + let type_ = match input.type_.as_str() { + "sefs" => ConfigMountFsType::TYPE_SEFS, + "hostfs" => ConfigMountFsType::TYPE_HOSTFS, + "ramfs" => ConfigMountFsType::TYPE_RAMFS, + _ => { return errno!(EINVAL, "Unsupported file system type"); } - type_ }; let target = { - let target = input.target.clone(); + let target = PathBuf::from(&input.target); if !target.starts_with("/") { return errno!(EINVAL, "Target must be an absolute path"); } target }; - let source = input.source.clone(); + let source = input.source.as_ref().map(|s| PathBuf::from(s)); let options = ConfigMountOptions::from_input(&input.options)?; Ok(ConfigMount { type_, target, source, options, }) } @@ -184,7 +194,7 @@ impl ConfigMountOptions { if input.mac.is_none() { return errno!(EINVAL, "MAC is expected"); } - (true, input.mac.clone()) + (true, Some(parse_mac(&input.mac.as_ref().unwrap())?)) }; Ok(ConfigMountOptions { integrity_only, mac }) } diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index 8c242eb9..64be87d7 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -1,50 +1,5 @@ -use rcore_fs::vfs::{FileSystem, FileType, FsError, INode}; -use rcore_fs_mountfs::{MountFS, MNode}; -use rcore_fs_ramfs::RamFS; -use rcore_fs_sefs::SEFS; -use std::fmt; - -use super::hostfs::HostFS; -use super::sgx_impl::SgxStorage; use super::*; - -lazy_static! { - /// The root of file system - pub static ref ROOT_INODE: Arc = { - // Mount SEFS at / - let device = Box::new(SgxStorage::new("sefs")); - let sefs = SEFS::open(device, &time::OcclumTimeProvider) - .expect("failed to open SEFS"); - let rootfs = MountFS::new(sefs); - let root = rootfs.root_inode(); - - fn mount_default_fs(fs: Arc, root: &MNode, mount_at: &str) -> Result<(), Error> { - let mount_dir = match root.find(false, mount_at) { - Ok(existing_dir) => { - if existing_dir.metadata()?.type_ != FileType::Dir { - return errno!(EIO, "not a directory"); - } - existing_dir - } - Err(_) => { - root.create(mount_at, FileType::Dir, 0o777)? - } - }; - mount_dir.mount(fs); - Ok(()) - } - - let hostfs = HostFS::new("."); - mount_default_fs(hostfs, &root, "host") - .expect("failed to mount HostFS at /host"); - - let ramfs = RamFS::new(); - mount_default_fs(ramfs, &root, "tmp") - .expect("failed to mount RamFS at /tmp"); - - root - }; -} +use std::fmt; pub struct INodeFile { inode: Arc, diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index e1ebe1e7..f56aac1b 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -10,7 +10,8 @@ pub use self::access::{do_access, do_faccessat, AccessFlags, AccessModes, AT_FDC pub use self::file::{File, FileRef, SgxFile, StdinFile, StdoutFile}; pub use self::file_table::{FileDesc, FileTable}; use self::inode_file::OpenOptions; -pub use self::inode_file::{INodeExt, INodeFile, ROOT_INODE}; +pub use self::inode_file::{INodeExt, INodeFile}; +pub use self::root_inode::{ROOT_INODE}; pub use self::io_multiplexing::*; use self::dev_null::DevNull; use self::dev_zero::DevZero; @@ -26,6 +27,7 @@ mod file; mod file_table; mod hostfs; mod inode_file; +mod root_inode; mod io_multiplexing; mod dev_null; mod dev_zero; diff --git a/src/libos/src/fs/root_inode.rs b/src/libos/src/fs/root_inode.rs new file mode 100644 index 00000000..2454694b --- /dev/null +++ b/src/libos/src/fs/root_inode.rs @@ -0,0 +1,131 @@ +use super::*; +use super::hostfs::HostFS; +use super::sgx_impl::SgxStorage; +use config::{ConfigMount, ConfigMountFsType}; +use std::path::{Path, PathBuf}; + +use rcore_fs::vfs::{FileSystem, FileType, FsError, INode}; +use rcore_fs_sefs::SEFS; +use rcore_fs_sefs::dev::*; +use rcore_fs_mountfs::{MountFS, MNode}; +use rcore_fs_ramfs::RamFS; + +lazy_static! { + /// The root of file system + pub static ref ROOT_INODE: Arc = { + let mount_config = &config::LIBOS_CONFIG.mount; + + let rootfs = open_or_create_root_fs_according_to(mount_config) + .expect("failed to create or open SEFS for /"); + let root = rootfs.root_inode(); + + mount_nonroot_fs_according_to(mount_config, &root) + .expect("failed to create or open other FS"); + + root + }; +} + +fn open_or_create_root_fs_according_to(mount_config: &Vec) + -> Result, Error> +{ + let root_sefs_source = { + let root_mount_config = mount_config + .iter() + .find(|m| m.target == Path::new("/")) + .ok_or_else(|| Error::new(Errno::ENOENT, "The mount point at / is not specified"))?; + + if root_mount_config.type_ != ConfigMountFsType::TYPE_SEFS { + return errno!(EINVAL, "The mount point at / must be SEFS"); + } + if root_mount_config.options.integrity_only { + return errno!(EINVAL, "The root SEFS at / must be encrypted (i.e., integrity-only is not enough)"); + } + if root_mount_config.source.is_none() { + return errno!(EINVAL, "The root SEFS must be given a source path (on host)"); + } + root_mount_config.source.as_ref().unwrap() + }; + + let root_sefs = { + SEFS::open(Box::new(SgxStorage::new(root_sefs_source, false)), + &time::OcclumTimeProvider) + } + .or_else(|_| { + SEFS::create(Box::new(SgxStorage::new(root_sefs_source, false)), + &time::OcclumTimeProvider) + })?; + let root_mountable_sefs = MountFS::new(root_sefs); + Ok(root_mountable_sefs) +} + +fn mount_nonroot_fs_according_to(mount_config: &Vec, root: &MNode) + -> Result<(), Error> +{ + for mc in mount_config { + if mc.target == Path::new("/") { + continue; + } + + if !mc.target.is_absolute() { + return errno!(EINVAL, "The target path must be absolute"); + } + if mc.target.parent().unwrap() != Path::new("/") { + return errno!(EINVAL, "The target mount point must be under /"); + } + let target_dirname = mc.target.file_name().unwrap().to_str().unwrap(); + + use self::ConfigMountFsType::*; + match mc.type_ { + TYPE_SEFS => { + if !mc.options.integrity_only { + return errno!(EINVAL, "Must be integrity-only SEFS") + } + if mc.source.is_none() { + return errno!(EINVAL, "Source is expected for integrity-only SEFS is supported") + } + let source_path = mc.source.as_ref().unwrap(); + + let device = { + let mut device = Box::new(SgxStorage::new(source_path, true)); + device.set_root_mac(mc.options.mac.unwrap()); + device + }; + let sefs = SEFS::open(device, &time::OcclumTimeProvider)?; + mount_fs_at(sefs, &root, target_dirname)?; + }, + TYPE_HOSTFS => { + if mc.source.is_none() { + return errno!(EINVAL, "Source is expected for HostFS") + } + let source_path = mc.source.as_ref().unwrap(); + + let hostfs = HostFS::new(source_path); + mount_fs_at(hostfs, &root, target_dirname)?; + }, + TYPE_RAMFS => { + let ramfs = RamFS::new(); + mount_fs_at(ramfs, &root, target_dirname)?; + }, + } + } + Ok(()) +} + +fn mount_fs_at(fs: Arc, parent_inode: &MNode, dirname: &str) + -> Result<(), Error> +{ + let mount_dir = match parent_inode.find(false, dirname) { + Ok(existing_dir) => { + if existing_dir.metadata()?.type_ != FileType::Dir { + return errno!(EIO, "not a directory"); + } + existing_dir + } + Err(_) => { + parent_inode.create(dirname, FileType::Dir, 0o777)? + } + }; + mount_dir.mount(fs); + Ok(()) +} diff --git a/src/libos/src/fs/sgx_impl.rs b/src/libos/src/fs/sgx_impl.rs index bc3620d5..220e9087 100644 --- a/src/libos/src/fs/sgx_impl.rs +++ b/src/libos/src/fs/sgx_impl.rs @@ -1,3 +1,4 @@ +use super::{sgx_aes_gcm_128bit_tag_t}; use rcore_fs::dev::TimeProvider; use rcore_fs::vfs::Timespec; use rcore_fs_sefs::dev::*; @@ -11,39 +12,52 @@ use std::time::{SystemTime, UNIX_EPOCH}; pub struct SgxStorage { path: PathBuf, + integrity_only: bool, file_cache: Mutex>, + root_mac: Option, } impl SgxStorage { - pub fn new(path: impl AsRef) -> Self { + pub fn new(path: impl AsRef, integrity_only: bool) -> Self { // assert!(path.as_ref().is_dir()); SgxStorage { path: path.as_ref().to_path_buf(), + integrity_only: integrity_only, file_cache: Mutex::new(BTreeMap::new()), + root_mac: None, } } /// Get file by `file_id`. /// It lookups cache first, if miss, then call `open_fn` to open one, /// and add it to cache before return. #[cfg(feature = "sgx_file_cache")] - fn get(&self, file_id: usize, open_fn: impl FnOnce(&Self) -> LockedFile) -> LockedFile { + fn get(&self, file_id: usize, open_fn: impl FnOnce(&Self) -> DevResult) -> DevResult { // query cache let mut caches = self.file_cache.lock().unwrap(); if let Some(locked_file) = caches.get(&file_id) { // hit, return - return locked_file.clone(); + return Ok(locked_file.clone()); } // miss, open one - let locked_file = open_fn(self); + let locked_file = open_fn(self)?; // add to cache caches.insert(file_id, locked_file.clone()); - locked_file + Ok(locked_file) } /// Get file by `file_id` without cache. #[cfg(not(feature = "sgx_file_cache"))] - fn get(&self, file_id: usize, open_fn: impl FnOnce(&Self) -> LockedFile) -> LockedFile { + fn get(&self, file_id: usize, open_fn: impl FnOnce(&Self) -> DevResult) -> LockedFile { open_fn(self) } + + /// Set the expected root MAC of the SGX storage. + /// + /// By giving this root MAC, we can be sure that the root file (file_id = 0) opened + /// by the storage has a MAC that is equal to the given root MAC. + pub fn set_root_mac(&mut self, mac: sgx_aes_gcm_128bit_tag_t) -> DevResult<()> { + self.root_mac = Some(mac); + Ok(()) + } } impl Storage for SgxStorage { @@ -51,15 +65,36 @@ impl Storage for SgxStorage { let locked_file = self.get(file_id, |this| { let mut path = this.path.to_path_buf(); path.push(format!("{}", file_id)); - // TODO: key - let key = [0u8; 16]; - let file = OpenOptions::new() - .read(true) - .update(true) - .open_ex(path, &key) - .expect("failed to open SgxFile"); - LockedFile(Arc::new(Mutex::new(file))) - }); + let options = { + let mut options = OpenOptions::new(); + options.read(true).update(true); + options + }; + let file = { + let open_res = if !self.integrity_only { + options.open(path) + } else { + options.open_integrity_only(path) + }; + if open_res.is_err() { + return Err(DeviceError); + } + open_res.unwrap() + }; + + // Check the MAC of the root file against the given root MAC of the storage + if file_id == 0 && self.root_mac.is_some() { + let root_file_mac = file.get_mac() + .expect("Failed to get mac"); + if root_file_mac != self.root_mac.unwrap() { + println!("Expected MAC = {:#?}, actual MAC = {:?}", + self.root_mac.unwrap(), root_file_mac); + return Err(DeviceError); + } + } + + Ok(LockedFile(Arc::new(Mutex::new(file)))) + })?; Ok(Box::new(locked_file)) } @@ -67,15 +102,24 @@ impl Storage for SgxStorage { let locked_file = self.get(file_id, |this| { let mut path = this.path.to_path_buf(); path.push(format!("{}", file_id)); - // TODO: key - let key = [0u8; 16]; - let file = OpenOptions::new() - .write(true) - .update(true) - .open_ex(path, &key) - .expect("failed to create SgxFile"); - LockedFile(Arc::new(Mutex::new(file))) - }); + let options = { + let mut options = OpenOptions::new(); + options.write(true).update(true); + options + }; + let file = { + let open_res = if !self.integrity_only { + options.open(path) + } else { + options.open_integrity_only(path) + }; + if open_res.is_err() { + return Err(DeviceError); + } + open_res.unwrap() + }; + Ok(LockedFile(Arc::new(Mutex::new(file)))) + })?; Ok(Box::new(locked_file)) } diff --git a/test/Makefile b/test/Makefile index c5ca6645..dccb00c7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -15,7 +15,7 @@ BUILD_TARGETS := $(TEST_DEPS) $(TESTS) $(BENCHES) TEST_TARGETS := $(TESTS:%=test-%) BENCH_TARGETS := $(BENCHES:%=bench-%) CLEAN_TARGETS := $(BUILD_TARGETS:%=clean-%) -.PHONY: all build test clean sefs $(BUILD_TARGETS) $(TEST_TARGETS) $(BENCH_TARGETS) $(CLEAN_TARGETS) +.PHONY: all build test clean sefs root-sefs bin-sefs lib-sefs $(BUILD_TARGETS) $(TEST_TARGETS) $(BENCH_TARGETS) $(CLEAN_TARGETS) # Use echo program instead of built-in echo command in shell. This ensures # that echo can recognize escaped sequences (with -e argument) regardless of @@ -29,6 +29,8 @@ NO_COLOR := \033[0m FS_PATH := fs SEFS_PATH := sefs +BIN_SEFS_ROOT_FILE := $(SEFS_PATH)/bin/0 +LIB_SEFS_ROOT_FILE := $(SEFS_PATH)/lib/0 ############################################################################# # Build targets @@ -43,33 +45,60 @@ $(BUILD_TARGETS): %: @$(MAKE) --no-print-directory -C $@ @$(ECHO) "$(GREEN)DONE$(NO_COLOR)" -sefs: - @$(RM) -rf $(SEFS_PATH) +sefs: root-sefs bin-sefs lib-sefs + +root-sefs: + @mkdir -p $(SEFS_PATH)/root/ + @echo "SEFS => $@" + +bin-sefs: + @mkdir -p $(FS_PATH)/bin/ + @for test in $(TESTS) ; do \ + cp "$$test/$$test" $(FS_PATH)/bin/ ; \ + done + @rm -rf $(SEFS_PATH)/bin + @mkdir -p $(SEFS_PATH) + @cd $(PROJECT_DIR)/deps/sefs/sefs-fuse/bin/ && \ + ./app \ + --integrity-only \ + $(CUR_DIR)/$(SEFS_PATH)/bin \ + $(CUR_DIR)/$(FS_PATH)/bin \ + zip + @echo "SEFS => $@" + +lib-sefs: @mkdir -p $(FS_PATH)/lib/ @cp /lib/ld-musl-x86_64.so.1 $(FS_PATH)/lib/ @cp /usr/local/occlum/lib/libc++.so.1 $(FS_PATH)/lib/ @cp /usr/local/occlum/lib/libc++abi.so.1 $(FS_PATH)/lib/ @cp /usr/local/occlum/lib/libunwind.so.1 $(FS_PATH)/lib/ + @rm -rf $(SEFS_PATH)/lib + @mkdir -p $(SEFS_PATH) @cd $(PROJECT_DIR)/deps/sefs/sefs-fuse/bin/ && \ ./app \ - $(CUR_DIR)/$(SEFS_PATH) \ - $(CUR_DIR)/$(FS_PATH) \ + --integrity-only \ + $(CUR_DIR)/$(SEFS_PATH)/lib \ + $(CUR_DIR)/$(FS_PATH)/lib \ zip @echo "SEFS => $@" libocclum.signed.so: Occlum.json Enclave_config.xml Enclave_private.pem @$(PROJECT_DIR)/tools/bin/build-enclave Occlum.json Enclave_config.xml Enclave_private.pem +Occlum.json: Occlum.json.sh $(BIN_SEFS_ROOT_FILE) $(LIB_SEFS_ROOT_FILE) + @./Occlum.json.sh \ + `$(PROJECT_DIR)/tools/bin/protect-integrity show-mac $(BIN_SEFS_ROOT_FILE)` \ + `$(PROJECT_DIR)/tools/bin/protect-integrity show-mac $(LIB_SEFS_ROOT_FILE)` \ + > $@ + @echo "GEN => $@" + ############################################################################# # Test targets ############################################################################# test: build $(TEST_TARGETS) -pal: $(PROJECT_DIR)/src/pal/pal - @cp $< pal - -$(TEST_TARGETS): test-%: % pal libocclum.signed.so +$(TEST_TARGETS): test-%: % pal @$(ECHO) "$(CYAN)RUN TEST => $<$(NO_COLOR)" @$(MAKE) --no-print-directory -C $< test ; \ if [ $$? -eq 0 ] ; then \ @@ -78,6 +107,13 @@ $(TEST_TARGETS): test-%: % pal libocclum.signed.so $(ECHO) "$(RED)FAILED$(NO_COLOR)" ; \ fi ; +pal: $(PROJECT_DIR)/src/pal/pal + @cp $< pal + +$(PROJECT_DIR)/src/pal/pal: + @cd $(PROJECT_DIR)/src/pal && make + + ############################################################################# # Benchmark targets ############################################################################# @@ -98,7 +134,7 @@ $(BENCH_TARGETS): bench-%: % pal libocclum.signed.so ############################################################################# clean: $(CLEAN_TARGETS) - @$(RM) -f pal libocclum.signed.so Occlum.json.protected + @$(RM) -f pal libocclum.signed.so Occlum.json.protected Occlum.json @$(RM) -rf $(FS_PATH) $(SEFS_PATH) $(CLEAN_TARGETS): clean-%: diff --git a/test/Occlum.json b/test/Occlum.json.sh old mode 100644 new mode 100755 similarity index 61% rename from test/Occlum.json rename to test/Occlum.json.sh index a19c8434..6e5fbfc4 --- a/test/Occlum.json +++ b/test/Occlum.json.sh @@ -1,3 +1,8 @@ +#!/bin/bash +bin_sefs_mac=$1 +lib_sefs_mac=$2 + +cat < $@" +all: $(BIN_NAME) # Compile C/C++ test program # @@ -61,7 +54,7 @@ $(CXX_OBJS): %.o: %.cc ############################################################################# test: $(BIN_ENC_NAME) - @cd $(CUR_DIR)/.. && RUST_BACKTRACE=1 ./pal $(BIN_FS_PATH) $(BIN_ARGS) + @cd $(CUR_DIR)/.. && RUST_BACKTRACE=1 ./pal /bin/$(BIN_NAME) $(BIN_ARGS) ############################################################################# # Misc diff --git a/test/unix_socket/main.c b/test/unix_socket/main.c index a78cc086..717dbec5 100644 --- a/test/unix_socket/main.c +++ b/test/unix_socket/main.c @@ -83,7 +83,7 @@ int main(int argc, const char* argv[]) { posix_spawn_file_actions_addclose(&file_actions, socket_rd_fd); const char* msg = "Echo!\n"; - const char* child_prog = "hello_world"; + const char* child_prog = "/bin/hello_world"; const char* child_argv[3] = { child_prog, msg, NULL }; int child_pid; if (posix_spawn(&child_pid, child_prog, &file_actions,