From 8ac6de8dd35ff211dab49f8ca909e5c76c6ff1d1 Mon Sep 17 00:00:00 2001 From: yeyuning Date: Thu, 1 Aug 2024 15:33:59 +0800 Subject: [PATCH] adhoc Signed-off-by: yeyuning Change-Id: Ia98e5cffe345047e6b57c36f498e7882cd5d0754 --- services/key_enable/BUILD.gn | 2 + services/key_enable/src/lib.rs | 2 +- services/key_enable/src/profile_utils.rs | 111 +++++-- test/unittest/BUILD.gn | 2 + test/unittest/rust_key_enable_test.rs | 359 +++++++++++++++++++++++ 5 files changed, 448 insertions(+), 28 deletions(-) diff --git a/services/key_enable/BUILD.gn b/services/key_enable/BUILD.gn index 7792a3d..31887f5 100644 --- a/services/key_enable/BUILD.gn +++ b/services/key_enable/BUILD.gn @@ -33,6 +33,7 @@ ohos_rust_executable("key_enable") { deps += [ "${rust_openssl_dir}/openssl:lib", "//third_party/rust/crates/cxx:lib", + "//third_party/rust/crates/lazy-static.rs:lib", ] external_deps += [ "c_utils:utils_rust" ] } @@ -61,6 +62,7 @@ ohos_rust_shared_ffi("key_enable_lib") { deps += [ "${rust_openssl_dir}/openssl:lib", "//third_party/rust/crates/cxx:lib", + "//third_party/rust/crates/lazy-static.rs:lib", ] external_deps += [ "c_utils:utils_rust" ] } diff --git a/services/key_enable/src/lib.rs b/services/key_enable/src/lib.rs index 3459613..97c078d 100644 --- a/services/key_enable/src/lib.rs +++ b/services/key_enable/src/lib.rs @@ -14,7 +14,7 @@ */ //!crate key_enable - +extern crate lazy_static; /// module contains cert chain func pub mod cert_chain_utils; /// module contains cert path func diff --git a/services/key_enable/src/profile_utils.rs b/services/key_enable/src/profile_utils.rs index b0d2d62..5a970b8 100644 --- a/services/key_enable/src/profile_utils.rs +++ b/services/key_enable/src/profile_utils.rs @@ -13,6 +13,7 @@ * limitations under the License. */ +use lazy_static::lazy_static; use super::cert_chain_utils::PemCollection; use super::cert_path_utils::{ add_cert_path_info, remove_cert_path_info, common_format_fabricate_name, @@ -52,9 +53,15 @@ const PROFILE_DEVICE_IDS_KEY: &str = "device-ids"; const PROFILE_BUNDLE_INFO_KEY: &str = "bundle-info"; const PROFILE_BUNDLE_INFO_RELEASE_KEY: &str = "distribution-certificate"; const PROFILE_BUNDLE_INFO_DEBUG_KEY: &str = "development-certificate"; +const PROFILE_APP_DISTRIBUTION_TYPE_KEY: &str = "app-distribution-type"; +const APP_DISTRIBUTION_TYPE_INTERNALTESTING: &str = "internaltesting"; +const APP_DISTRIBUTION_TYPE_ENTERPRISE: &str = "enterprise"; +const APP_DISTRIBUTION_TYPE_ENTERPRISE_NORMAL: &str = "enterprise_normal"; +const APP_DISTRIBUTION_TYPE_ENTERPRISE_MDM: &str = "enterprise_mdm"; const DEFAULT_MAX_CERT_PATH_LEN: u32 = 3; const PROFILE_RELEASE_TYPE: &str = "release"; const PROFILE_DEBUG_TYPE: &str = "debug"; + /// profile error pub enum ProfileError { /// add cert path error @@ -105,48 +112,91 @@ fn parse_pkcs7_data( flags: Pkcs7Flags, check_udid: bool, ) -> Result<(String, String, u32), Box> { - let stack_of_certs = Stack::::new()?; + let profile = verify_pkcs7_signature(pkcs7, root_store, flags)?; + let profile_json = parse_and_validate_profile(profile, check_udid)?; + get_cert_details(&profile_json) +} +fn verify_pkcs7_signature( + pkcs7: &Pkcs7, + root_store: &X509Store, + flags: Pkcs7Flags, +) -> Result, Box> { + let stack_of_certs = Stack::::new()?; let mut profile = Vec::new(); - if pkcs7.verify(&stack_of_certs, root_store, None, Some(&mut profile), flags).is_err() { - error!(LOG_LABEL, "pkcs7 verify failed."); - return Err("pkcs7 verify failed.".into()); + pkcs7.verify(&stack_of_certs, root_store, None, Some(&mut profile), flags)?; + Ok(profile) +} + +/// validate bundle info and debug info +pub fn validate_bundle_and_distribution_type( + profile_json: &JsonValue, + check_udid: bool, +) -> Result<(), Box> { + let bundle_type = profile_json[PROFILE_TYPE_KEY].try_as_string()?.as_str(); + match bundle_type { + PROFILE_DEBUG_TYPE => { + if check_udid && verify_udid(profile_json).is_err() { + return Err("Invalid UDID.".into()); + } + }, + PROFILE_RELEASE_TYPE => { + let distribution_type = profile_json[PROFILE_APP_DISTRIBUTION_TYPE_KEY].try_as_string()?.as_str(); + match distribution_type { + APP_DISTRIBUTION_TYPE_INTERNALTESTING => { + if check_udid && verify_udid(profile_json).is_err() { + return Err("Invalid UDID.".into()); + } + }, + APP_DISTRIBUTION_TYPE_ENTERPRISE | + APP_DISTRIBUTION_TYPE_ENTERPRISE_NORMAL | + APP_DISTRIBUTION_TYPE_ENTERPRISE_MDM => { + }, + _ => { + return Err("Invalid app distribution type.".into()); + } + } + } + _ => { + return Err("Invalid bundle type.".into()); + }, } + Ok(()) +} + +fn parse_and_validate_profile( + profile: Vec, + check_udid: bool, +) -> Result> { let profile_json = JsonValue::from_text(profile)?; - let bundle_type = profile_json[PROFILE_TYPE_KEY].try_as_string()?.as_str(); + validate_bundle_and_distribution_type(&profile_json, check_udid)?; + Ok(profile_json) +} - if bundle_type == PROFILE_DEBUG_TYPE && check_udid && verify_udid(&profile_json).is_err() { - error!(LOG_LABEL, "udid verify failed."); - return Err("Invalid udid .".into()); - } +fn get_cert_details(profile_json: &JsonValue) -> Result<(String, String, u32), Box> { + let bundle_type = profile_json[PROFILE_TYPE_KEY].try_as_string()?.as_str(); let profile_type = match bundle_type { PROFILE_DEBUG_TYPE => DebugCertPathType::Developer as u32, PROFILE_RELEASE_TYPE => ReleaseCertPathType::Developer as u32, - _ => { - error!(LOG_LABEL, "pkcs7 verify failed."); - return Err("Invalid bundle type.".into()); - } + _ => return Err("Invalid bundle type.".into()), }; let signed_cert = match bundle_type { - PROFILE_DEBUG_TYPE => { - profile_json[PROFILE_BUNDLE_INFO_KEY][PROFILE_BUNDLE_INFO_DEBUG_KEY].try_as_string()? - } - PROFILE_RELEASE_TYPE => profile_json[PROFILE_BUNDLE_INFO_KEY] - [PROFILE_BUNDLE_INFO_RELEASE_KEY] - .try_as_string()?, - _ => { - error!(LOG_LABEL, "pkcs7 verify failed."); - return Err("Invalid bundle type.".into()); - } + PROFILE_DEBUG_TYPE => profile_json[PROFILE_BUNDLE_INFO_KEY][PROFILE_BUNDLE_INFO_DEBUG_KEY].try_as_string()?, + PROFILE_RELEASE_TYPE => profile_json[PROFILE_BUNDLE_INFO_KEY][PROFILE_BUNDLE_INFO_RELEASE_KEY].try_as_string()?, + _ => return Err("Invalid bundle type.".into()), }; let signed_pem = X509::from_pem(signed_cert.as_bytes())?; let subject = format_x509_fabricate_name(signed_pem.subject_name()); let issuer = format_x509_fabricate_name(signed_pem.issuer_name()); - Ok((subject, issuer, profile_type)) } -fn get_udid() -> Result { +lazy_static! { + /// global udid + pub static ref UDID: Result = init_udid(); +} + +fn init_udid() -> Result { let mut udid: Vec = vec![0; 128]; let result = unsafe { CodeSignGetUdid(udid.as_mut_ptr()) }; @@ -164,6 +214,12 @@ fn get_udid() -> Result { } } +/// get device udid +pub fn get_udid() -> Result { + UDID.clone() +} + + fn verify_signers( pkcs7: &Pkcs7, profile_signer: &[(&String, &String)], @@ -287,8 +343,9 @@ fn process_profile( let (subject, issuer, profile_type) = match parse_pkcs7_data(&pkcs7, x509_store, Pkcs7Flags::empty(), check_udid) { Ok(tuple) => tuple, - Err(_) => { - error!(LOG_LABEL, "Failed to parse profile file {}", @public(path)); + Err(e) => { + error!(LOG_LABEL, "Error parsing PKCS7 data: {}, profile file {}", + @public(e), @public(path)); report_parse_profile_err(&path, HisyseventProfileError::ParsePkcs7 as i32); continue; } diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index 34bd424..994db32 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -208,6 +208,7 @@ ohos_rust_static_library("rust_key_enable_lib") { "${code_signature_root_dir}/services/key_enable/utils:libkey_enable_utils", "${rust_openssl_dir}/openssl:lib", "//third_party/rust/crates/cxx:lib", + "//third_party/rust/crates/lazy-static.rs:lib", ] external_deps = [ "c_utils:utils_rust", @@ -237,6 +238,7 @@ ohos_rust_unittest("rust_key_enable_unittest") { resource_config_file = "resources/ohos_test.xml" crate_root = "./rust_key_enable_test.rs" sources = [ "./rust_key_enable_test.rs" ] + external_deps = [ "ylong_json:lib" ] deps = [ ":rust_key_enable_lib" ] subsystem_name = "security" part_name = "code_signature" diff --git a/test/unittest/rust_key_enable_test.rs b/test/unittest/rust_key_enable_test.rs index 8399195..26ce7a4 100644 --- a/test/unittest/rust_key_enable_test.rs +++ b/test/unittest/rust_key_enable_test.rs @@ -13,8 +13,14 @@ * limitations under the License. */ extern crate key_enable; +extern crate ylong_json; + +use std::thread; +use ylong_json::JsonValue; use key_enable::cert_chain_utils::PemCollection; use key_enable::cert_path_utils::TrustCertPath; +use key_enable::profile_utils::{UDID, get_udid, validate_bundle_and_distribution_type}; + // pem_cert_file const VALID_PEM_CERT: &str = "/data/test/tmp/valid_pem_cert.json"; @@ -99,3 +105,356 @@ fn test_empty_cert_path_json_file() { "Expected cert_paths.app_sources to be empty for an empty JSON file" ); } + +#[test] +fn test_parse_enterprise_profile() { + let profile_str = r#" + { + "version-name": "2.0.0", + "version-code": 2, + "app-distribution-type": "enterprise", + "uuid": "", + "validity": { + "not-before": 1, + "not-after": 2 + }, + "type": "release", + "bundle-info": { + "developer-id": "", + "distribution-certificate": "", + "bundle-name": "com.test.enterprise", + "apl": "normal", + "app-feature": "test_app", + "app-identifier": "123123" + }, + "acls": { + "allowed-acls": [ + "" + ] + }, + "app-privilege-capabilities": [], + "permissions": { + "restricted-permissions": [ + "" + ] + } + } + "#; + let profile_json =JsonValue::from_text(profile_str).unwrap(); + let result = validate_bundle_and_distribution_type(&profile_json, true); + assert!(result.is_ok()); +} + +#[test] +fn test_parse_enterprise_normal_profile() { + let profile_str = r#" + { + "version-name": "2.0.0", + "version-code": 2, + "app-distribution-type": "enterprise_normal", + "uuid": "", + "validity": { + "not-before": 1, + "not-after": 2 + }, + "type": "release", + "bundle-info": { + "developer-id": "", + "distribution-certificate": "", + "bundle-name": "com.test.enterprise_normal", + "apl": "normal", + "app-feature": "test_app", + "app-identifier": "123123" + }, + "acls": { + "allowed-acls": [ + "" + ] + }, + "app-privilege-capabilities": [], + "permissions": { + "restricted-permissions": [ + "" + ] + } + } + "#; + let profile_json =JsonValue::from_text(profile_str).unwrap(); + let result = validate_bundle_and_distribution_type(&profile_json, true); + assert!(result.is_ok()); +} + +#[test] +fn test_parse_enterprise_mdm_profile() { + let profile_str = r#" + { + "version-name": "2.0.0", + "version-code": 2, + "app-distribution-type": "enterprise_mdm", + "uuid": "", + "validity": { + "not-before": 1, + "not-after": 2 + }, + "type": "release", + "bundle-info": { + "developer-id": "", + "distribution-certificate": "", + "bundle-name": "com.test.enterprise_mdm", + "apl": "normal", + "app-feature": "test_app", + "app-identifier": "123123" + }, + "acls": { + "allowed-acls": [ + "" + ] + }, + "app-privilege-capabilities": [], + "permissions": { + "restricted-permissions": [ + "" + ] + } + } + "#; + let profile_json =JsonValue::from_text(profile_str).unwrap(); + let result = validate_bundle_and_distribution_type(&profile_json, true); + assert!(result.is_ok()); +} + +#[test] +fn test_parse_debug_profile() { + let profile_str = r#" + { + "version-name": "2.0.0", + "version-code": 2, + "app-distribution-type": "developer", + "uuid": "", + "validity": { + "not-before": 1, + "not-after": 2 + }, + "type": "debug", + "bundle-info": { + "developer-id": "", + "development-certificate": "", + "bundle-name": "com.test.developer", + "apl": "normal", + "app-feature": "test_app", + "app-identifier": "123123" + }, + "acls": { + "allowed-acls": [ + "" + ] + }, + "app-privilege-capabilities": [], + "permissions": { + "restricted-permissions": [ + "" + ] + }, + "debug-info": { + "device-ids": [], + "device-id-type": "udid" + } + } + "#; + let udid = get_udid().expect("Failed to get UDID"); + let mut profile_json =JsonValue::from_text(profile_str).unwrap(); + profile_json["debug-info"]["device-ids"][0] = JsonValue::String(udid); + let result = validate_bundle_and_distribution_type(&profile_json, true); + assert!(result.is_ok()); +} + +#[test] +fn test_parse_iternaltesting_profile() { + let profile_str = r#" + { + "version-name": "2.0.0", + "version-code": 2, + "app-distribution-type": "internaltesting", + "uuid": "", + "validity": { + "not-before": 1, + "not-after": 2 + }, + "type": "release", + "bundle-info": { + "developer-id": "", + "distribution-certificate": "", + "bundle-name": "com.test.internaltesting", + "apl": "normal", + "app-feature": "test_app", + "app-identifier": "123123" + }, + "acls": { + "allowed-acls": [ + "" + ] + }, + "app-privilege-capabilities": [], + "permissions": { + "restricted-permissions": [ + "" + ] + }, + "debug-info": { + "device-ids": [], + "device-id-type": "udid" + } + } + "#; + let udid = get_udid().expect("Failed to get UDID"); + let mut profile_json =JsonValue::from_text(profile_str).unwrap(); + profile_json["debug-info"]["device-ids"][0] = JsonValue::String(udid); + let result = validate_bundle_and_distribution_type(&profile_json, true); + assert!(result.is_ok()); +} + +#[test] +fn test_parse_invalid_profile() { + let no_type_profile = r#" + { + "version-name": "2.0.0", + "version-code": 2, + "app-distribution-type": "internaltesting", + "uuid": "", + "validity": { + "not-before": 1, + "not-after": 2 + }, + "bundle-info": { + "developer-id": "", + "distribution-certificate": "", + "bundle-name": "com.test.internaltesting", + "apl": "normal", + "app-feature": "test_app", + "app-identifier": "123123" + }, + "acls": { + "allowed-acls": [ + "" + ] + }, + "app-privilege-capabilities": [], + "permissions": { + "restricted-permissions": [ + "" + ] + }, + "debug-info": { + "device-ids": [], + "device-id-type": "udid" + } + } + "#; + let no_distribution_profile = r#" + { + "version-name": "2.0.0", + "version-code": 2, + "uuid": "", + "validity": { + "not-before": 1, + "not-after": 2 + }, + "type": "release", + "bundle-info": { + "developer-id": "", + "distribution-certificate": "", + "bundle-name": "com.test.internaltesting", + "apl": "normal", + "app-feature": "test_app", + "app-identifier": "123123" + }, + "acls": { + "allowed-acls": [ + "" + ] + }, + "app-privilege-capabilities": [], + "permissions": { + "restricted-permissions": [ + "" + ] + }, + "debug-info": { + "device-ids": [], + "device-id-type": "udid" + } + } + "#; + let no_debug_info_profile = r#" + { + "version-name": "2.0.0", + "version-code": 2, + "app-distribution-type": "internaltesting", + "uuid": "", + "validity": { + "not-before": 1, + "not-after": 2 + }, + "type": "release", + "bundle-info": { + "developer-id": "", + "distribution-certificate": "", + "bundle-name": "com.test.internaltesting", + "apl": "normal", + "app-feature": "test_app", + "app-identifier": "123123" + }, + "acls": { + "allowed-acls": [ + "" + ] + }, + "app-privilege-capabilities": [], + "permissions": { + "restricted-permissions": [ + "" + ] + } + } + "#; + let udid = get_udid().expect("Failed to get UDID"); + let mut no_type_profile_json =JsonValue::from_text(no_type_profile).unwrap(); + no_type_profile_json["debug-info"]["device-ids"][0] = JsonValue::String(udid.clone()); + let result = validate_bundle_and_distribution_type(&no_type_profile_json, true); + assert!(result.is_err()); + + let mut no_distribution_profile_json =JsonValue::from_text(no_distribution_profile).unwrap(); + no_distribution_profile_json["debug-info"]["device-ids"][0] = JsonValue::String(udid.clone()); + let result = validate_bundle_and_distribution_type(&no_distribution_profile_json, true); + assert!(result.is_err()); + + let no_debug_info_profile_json =JsonValue::from_text(no_debug_info_profile).unwrap(); + let result = validate_bundle_and_distribution_type(&no_debug_info_profile_json, true); + assert!(result.is_err()); +} + +#[test] +fn test_get_udid_once() { + let udid_from_get = get_udid().expect("Failed to get UDID"); + let udid_from_global = UDID.clone().expect("UDID is None"); + + assert_eq!(udid_from_get, udid_from_global); +} + +#[test] +fn test_get_udid_concurrent() { + let num_threads = 10; + let mut handles = vec![]; + + for _ in 0..num_threads { + let handle = thread::spawn(|| { + let udid = get_udid().expect("Failed to get UDID"); + assert_eq!(udid, UDID.clone().expect("UDID is None")); + }); + handles.push(handle); + } + + for handle in handles { + handle.join().expect("Thread panicked"); + } +} \ No newline at end of file -- Gitee