From 435ae8b68d629fa961a542b3d0671cf99675e4fd Mon Sep 17 00:00:00 2001 From: "Zheng, Qi" Date: Thu, 2 Mar 2023 16:55:55 +0800 Subject: [PATCH] Add init with grpc_ratls kms client support --- etc/template/init_grpc_ratls.json | 27 ++++ tools/Makefile | 2 + tools/init_grpc_ratls/Cargo.lock | 119 +++++++++++++++ tools/init_grpc_ratls/Cargo.toml | 12 ++ tools/init_grpc_ratls/Makefile | 16 +++ tools/init_grpc_ratls/build.rs | 7 + tools/init_grpc_ratls/src/main.rs | 231 ++++++++++++++++++++++++++++++ 7 files changed, 414 insertions(+) create mode 100644 etc/template/init_grpc_ratls.json create mode 100644 tools/init_grpc_ratls/Cargo.lock create mode 100644 tools/init_grpc_ratls/Cargo.toml create mode 100644 tools/init_grpc_ratls/Makefile create mode 100644 tools/init_grpc_ratls/build.rs create mode 100644 tools/init_grpc_ratls/src/main.rs diff --git a/etc/template/init_grpc_ratls.json b/etc/template/init_grpc_ratls.json new file mode 100644 index 00000000..d1ce6094 --- /dev/null +++ b/etc/template/init_grpc_ratls.json @@ -0,0 +1,27 @@ +{ + "kms_server": "localhost:50051", + "kms_keys": [ + { + "key": "demo_key", + "path": "/etc/demo_key" + } + ], + "ra_config": { + "verify_mr_enclave" : "off", + "verify_mr_signer" : "off", + "verify_isv_prod_id" : "off", + "verify_isv_svn" : "off", + "verify_config_svn" : "off", + "verify_enclave_debuggable" : "off", + "sgx_mrs": [ + { + "mr_enclave" : "", + "mr_signer" : "", + "isv_prod_id" : 0, + "isv_svn" : 0, + "config_svn" : 0, + "debuggable" : true + } + ] + } +} diff --git a/tools/Makefile b/tools/Makefile index 5c0257df..a6234fec 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -12,6 +12,7 @@ all: @$(MAKE) --no-print-directory -C gen_internal_conf @$(MAKE) --no-print-directory -C copy_bom @$(MAKE) --no-print-directory -C init + @$(MAKE) --no-print-directory -C init_grpc_ratls format: @$(MAKE) --no-print-directory -C protect-integrity format @@ -24,3 +25,4 @@ clean: @$(MAKE) --no-print-directory -C gen_internal_conf clean @$(MAKE) --no-print-directory -C copy_bom clean @$(MAKE) --no-print-directory -C init clean + @$(MAKE) --no-print-directory -C init_grpc_ratls clean diff --git a/tools/init_grpc_ratls/Cargo.lock b/tools/init_grpc_ratls/Cargo.lock new file mode 100644 index 00000000..af9157c6 --- /dev/null +++ b/tools/init_grpc_ratls/Cargo.lock @@ -0,0 +1,119 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "base64" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +dependencies = [ + "byteorder", + "safemem", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "init_grpc_ratls" +version = "0.0.2" +dependencies = [ + "base64", + "libc", + "serde", + "serde_json", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "libc" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "serde" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" diff --git a/tools/init_grpc_ratls/Cargo.toml b/tools/init_grpc_ratls/Cargo.toml new file mode 100644 index 00000000..9bff8188 --- /dev/null +++ b/tools/init_grpc_ratls/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "init_grpc_ratls" +version = "0.0.2" +build = "build.rs" +authors = ["Zheng Qi huaiqing.zq@antgroup.com"] +edition = "2021" + +[dependencies] +libc = "0.2.84" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +base64 = "0.9" diff --git a/tools/init_grpc_ratls/Makefile b/tools/init_grpc_ratls/Makefile new file mode 100644 index 00000000..24a781a8 --- /dev/null +++ b/tools/init_grpc_ratls/Makefile @@ -0,0 +1,16 @@ +include ../../src/sgxenv.mk + +SRC_FILES := $(shell find . -type f -name '*.rs') Cargo.toml +RUST_TARGET_DIR := $(BUILD_DIR)/internal/tools/init_grpc_ratls/cargo-target +RUST_OUT_DIR := $(BUILD_DIR)/bin +TARGET_BINARY := $(RUST_OUT_DIR)/init_grpc_ratls + +.PHONY: all clean + +all: $(SRC_FILES) + @RUSTC_BOOTSTRAP=1 occlum-cargo build --release --target-dir=$(RUST_TARGET_DIR) -Z unstable-options --out-dir=$(RUST_OUT_DIR) + @echo "CARGO (release) => init_grpc_ratls" + +clean: + @occlum-cargo clean --target-dir=$(RUST_TARGET_DIR) + @-$(RM) -f $(TARGET_BINARY) diff --git a/tools/init_grpc_ratls/build.rs b/tools/init_grpc_ratls/build.rs new file mode 100644 index 00000000..cc3578d7 --- /dev/null +++ b/tools/init_grpc_ratls/build.rs @@ -0,0 +1,7 @@ +fn main() { + println!("cargo:rustc-link-search=native=/opt/occlum/toolchains/dcap_lib/musl"); + println!("cargo:rustc-link-search=native=/opt/occlum/toolchains/grpc_ratls/musl"); + println!("cargo:rustc-link-lib=dylib=grpc_ratls_client"); + println!("cargo:rustc-link-lib=dylib=hw_grpc_proto"); + println!("cargo:rustc-link-lib=dylib=occlum_dcap"); +} \ No newline at end of file diff --git a/tools/init_grpc_ratls/src/main.rs b/tools/init_grpc_ratls/src/main.rs new file mode 100644 index 00000000..c2ca407a --- /dev/null +++ b/tools/init_grpc_ratls/src/main.rs @@ -0,0 +1,231 @@ +extern crate libc; +extern crate serde; +extern crate serde_json; + +use libc::syscall; +use serde::{Deserialize, Serialize}; + +use std::error::Error; +use std::fs; +use std::fs::File; +use std::io::{ErrorKind, Read}; +use std::str; + +use std::ffi::CString; +use std::os::raw::{c_int, c_char}; + +#[link(name = "grpc_ratls_client")] +extern "C" { + fn grpc_ratls_get_secret( + server_addr: *const c_char, // grpc server address+port, such as "localhost:50051" + config_json: *const c_char, // ratls handshake config json file + name: *const c_char, // secret name to be requested + secret_file: *const c_char // secret file to be saved + ) -> c_int; +} + +#[derive(Deserialize, Serialize, Debug)] +#[warn(dead_code)] +struct MRsValue { + pub mr_enclave: String, + pub mr_signer: String, + pub isv_prod_id: u32, + pub isv_svn: u32, + pub config_svn: u32, + pub debuggable: bool, +} + +#[derive(Deserialize, Serialize, Debug)] +#[warn(dead_code)] +struct RAConfig { + verify_mr_enclave: String, + verify_mr_signer: String, + verify_isv_prod_id: String, + verify_isv_svn: String, + verify_config_svn: String, + verify_enclave_debuggable: String, + sgx_mrs: Vec, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[warn(dead_code)] +struct KmsKeys { + key: String, + path: String, +} + +#[derive(Deserialize, Serialize, Debug)] +#[warn(dead_code)] +struct InitRAConfig { + kms_server: String, + kms_keys: Vec, + ra_config: RAConfig +} + +fn load_ra_config(ra_conf_path: &str) -> Result> { + let mut ra_conf_file = File::open(ra_conf_path)?; + let ra_conf = { + let mut ra_conf = String::new(); + ra_conf_file.read_to_string(&mut ra_conf)?; + ra_conf + }; + let config: InitRAConfig = serde_json::from_str(&ra_conf)?; + Ok(config) +} + + +fn main() -> Result<(), Box> { + // Load the configuration from initfs + const IMAGE_CONFIG_FILE: &str = "/etc/image_config.json"; + const INIT_RA_CONF: &str = "/etc/init_ra_conf.json"; + let image_config = load_config(IMAGE_CONFIG_FILE)?; + + // Get the MAC of Occlum.json.protected file + let occlum_json_mac = { + let mut mac: sgx_aes_gcm_128bit_tag_t = Default::default(); + parse_str_to_bytes(&image_config.occlum_json_mac, &mut mac)?; + mac + }; + let occlum_json_mac_ptr = &occlum_json_mac as *const sgx_aes_gcm_128bit_tag_t; + + // Do parse to get Init RA information + let init_ra_conf = load_ra_config(INIT_RA_CONF)?; + // Extract RA config part + let ra_conf_string = serde_json::to_string_pretty(&init_ra_conf.ra_config).unwrap(); + fs::write("ra_config.json", ra_conf_string.clone().into_bytes())?; + let config_json = CString::new("ra_config.json").unwrap(); + let server_addr = CString::new(init_ra_conf.kms_server).unwrap(); + + // Get the key of FS image if needed + let key = match &image_config.image_type[..] { + "encrypted" => { + // Get the image encrypted key through RA + let secret = CString::new("image_key").unwrap(); + let filename = CString::new("/etc/image_key").unwrap(); + + let ret = unsafe { + grpc_ratls_get_secret( + server_addr.as_ptr(), + config_json.as_ptr(), + secret.as_ptr(), + filename.as_ptr()) + }; + + if ret != 0 { + println!("grpc_ratls_get_secret failed return {}", ret); + return Err(Box::new(std::io::Error::last_os_error())); + } + + const IMAGE_KEY_FILE: &str = "/etc/image_key"; + let key_str = load_key(IMAGE_KEY_FILE)?; + // Remove key file which is not needed any more + fs::remove_file(IMAGE_KEY_FILE)?; + let mut key: sgx_key_128bit_t = Default::default(); + parse_str_to_bytes(&key_str, &mut key)?; + Some(key) + } + "integrity-only" => None, + _ => unreachable!(), + }; + let key_ptr = key + .as_ref() + .map(|key| key as *const sgx_key_128bit_t) + .unwrap_or(std::ptr::null()); + + // Do one time key acquire to force all necessary libraries got loaded to memory. + // Thus after mount rootfs, the API could run successfully. + // The key got this time will be dropped. + unsafe { + let kms_key = init_ra_conf.kms_keys[0].clone(); + let key = CString::new(kms_key.key).unwrap(); + let file = CString::new(kms_key.path).unwrap(); + + grpc_ratls_get_secret( + server_addr.as_ptr(), + config_json.as_ptr(), + key.as_ptr(), + file.as_ptr()); + // Remove key file which is not needed any more + fs::remove_file(file.into_string().unwrap())?; + }; + + // Mount the image + const SYS_MOUNT_FS: i64 = 363; + let ret = unsafe { syscall(SYS_MOUNT_FS, key_ptr, occlum_json_mac_ptr) }; + if ret < 0 { + return Err(Box::new(std::io::Error::last_os_error())); + } + + // Rewrite ra_config to rootfs + fs::write("ra_config.json", ra_conf_string.into_bytes())?; + let config_json = CString::new("ra_config.json").unwrap(); + + // Get keys and save to path + for keys in init_ra_conf.kms_keys { + let key = CString::new(keys.key).unwrap(); + let file = CString::new(keys.path).unwrap(); + + let ret = unsafe { + grpc_ratls_get_secret( + server_addr.as_ptr(), + config_json.as_ptr(), + key.as_ptr(), + file.as_ptr()) + }; + + if ret != 0 { + println!("Failed to get key {:?}, return {}", key, ret); + // return Err(Box::new(std::io::Error::last_os_error())); + } + } + + Ok(()) +} + +#[allow(non_camel_case_types)] +type sgx_key_128bit_t = [u8; 16]; +#[allow(non_camel_case_types)] +type sgx_aes_gcm_128bit_tag_t = [u8; 16]; + +#[derive(Deserialize, Debug)] +#[serde(deny_unknown_fields)] +struct ImageConfig { + occlum_json_mac: String, + image_type: String, +} + +fn load_config(config_path: &str) -> Result> { + let mut config_file = File::open(config_path)?; + let config_json = { + let mut config_json = String::new(); + config_file.read_to_string(&mut config_json)?; + config_json + }; + let config: ImageConfig = serde_json::from_str(&config_json)?; + Ok(config) +} + +fn load_key(key_path: &str) -> Result> { + let mut key_file = File::open(key_path)?; + let mut key = String::new(); + key_file.read_to_string(&mut key)?; + Ok(key.trim_end_matches(|c| c == '\r' || c == '\n').to_string()) +} + +fn parse_str_to_bytes(arg_str: &str, bytes: &mut [u8]) -> Result<(), Box> { + let bytes_str_vec = { + let bytes_str_vec: Vec<&str> = arg_str.split('-').collect(); + if bytes_str_vec.len() != bytes.len() { + return Err(Box::new(std::io::Error::new( + ErrorKind::InvalidData, + "The length or format of Key/MAC string is invalid", + ))); + } + bytes_str_vec + }; + + for (byte_i, byte_str) in bytes_str_vec.iter().enumerate() { + bytes[byte_i] = u8::from_str_radix(byte_str, 16)?; + } + Ok(()) +}