1/* 2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16use lazy_static::lazy_static; 17use super::cert_chain_utils::PemCollection; 18use super::cert_path_utils::{ 19 add_cert_path_info, remove_cert_path_info, common_format_fabricate_name, 20 DebugCertPathType, ReleaseCertPathType, TrustCertPath, 21}; 22use super::cs_hisysevent::report_parse_profile_err; 23use super::file_utils::{ 24 create_file_path, delete_file_path, file_exists, fmt_store_path, 25 load_bytes_from_file, write_bytes_to_file, change_default_mode_file, change_default_mode_directory 26}; 27use hilog_rust::{error, info, hilog, HiLogLabel, LogType}; 28use openssl::pkcs7::{Pkcs7, Pkcs7Flags}; 29use openssl::stack::Stack; 30use openssl::x509::store::{X509Store, X509StoreBuilder}; 31use openssl::x509::{X509NameRef, X509}; 32use std::error::Error; 33use std::ffi::{c_char, CStr, CString}; 34use std::fs::read_dir; 35use ylong_json::JsonValue; 36 37const ERROR_CODE: i32 = -1; 38const SUCCESS_CODE: i32 = 0; 39const LOG_LABEL: HiLogLabel = HiLogLabel { 40 log_type: LogType::LogCore, 41 domain: 0xd005a06, // security domain 42 tag: "CODE_SIGN", 43}; 44const PROFILE_STORE_EL0_PREFIX: &str = "/data/service/el0/profiles/developer"; 45const PROFILE_STORE_EL1_PREFIX: &str = "/data/service/el1/profiles/release"; 46const PROFILE_STORE_EL1_PUBLIC_PREFIX: &str = "/data/service/el1/public/profiles/release"; 47const DEBUG_PROFILE_STORE_EL0_PREFIX: &str = "/data/service/el0/profiles/debug"; 48const DEBUG_PROFILE_STORE_EL1_PREFIX: &str = "/data/service/el1/profiles/debug"; 49const DEBUG_PROFILE_STORE_EL1_PUBLIC_PREFIX: &str = "/data/service/el1/public/profiles/debug"; 50const PROFILE_STORE_TAIL: &str = "profile.p7b"; 51const PROFILE_TYPE_KEY: &str = "type"; 52const PROFILE_DEVICE_ID_TYPE_KEY: &str = "device-id-type"; 53const PROFILE_DEBUG_INFO_KEY: &str = "debug-info"; 54const PROFILE_DEVICE_IDS_KEY: &str = "device-ids"; 55const PROFILE_BUNDLE_INFO_KEY: &str = "bundle-info"; 56const PROFILE_BUNDLE_INFO_RELEASE_KEY: &str = "distribution-certificate"; 57const PROFILE_BUNDLE_INFO_DEBUG_KEY: &str = "development-certificate"; 58const PROFILE_APP_DISTRIBUTION_TYPE_KEY: &str = "app-distribution-type"; 59const APP_DISTRIBUTION_TYPE_INTERNALTESTING: &str = "internaltesting"; 60const APP_DISTRIBUTION_TYPE_ENTERPRISE: &str = "enterprise"; 61const APP_DISTRIBUTION_TYPE_ENTERPRISE_NORMAL: &str = "enterprise_normal"; 62const APP_DISTRIBUTION_TYPE_ENTERPRISE_MDM: &str = "enterprise_mdm"; 63const DEFAULT_MAX_CERT_PATH_LEN: u32 = 3; 64const PROFILE_RELEASE_TYPE: &str = "release"; 65const PROFILE_DEBUG_TYPE: &str = "debug"; 66 67/// profile error 68pub enum ProfileError { 69 /// add cert path error 70 AddCertPathError, 71} 72/// profile error report to hisysevent 73pub enum HisyseventProfileError { 74 /// release platform code 75 VerifySigner = 1, 76 /// release authed code 77 ParsePkcs7 = 2, 78 /// release developer code 79 AddCertPath = 3, 80} 81 82extern "C" { 83 /// if developer state on return true 84 pub fn IsDeveloperModeOn() -> bool; 85 fn CodeSignGetUdid(udid: *mut u8) -> i32; 86 fn IsRdDevice() -> bool; 87} 88 89#[no_mangle] 90/// the interface to enable key in profile 91pub extern "C" fn EnableKeyInProfileByRust( 92 bundle_name: *const c_char, 93 profile: *const u8, 94 profile_size: u32, 95) -> i32 { 96 match enable_key_in_profile_internal(bundle_name, profile, profile_size) { 97 Ok(_) => SUCCESS_CODE, 98 Err(_) => ERROR_CODE, 99 } 100} 101 102#[no_mangle] 103/// the interface remove key in profile 104pub extern "C" fn RemoveKeyInProfileByRust(bundle_name: *const c_char) -> i32 { 105 match remove_key_in_profile_internal(bundle_name) { 106 Ok(_) => SUCCESS_CODE, 107 Err(_) => ERROR_CODE, 108 } 109} 110 111fn parse_pkcs7_data( 112 pkcs7: &Pkcs7, 113 root_store: &X509Store, 114 flags: Pkcs7Flags, 115 check_udid: bool, 116) -> Result<(String, String, u32), Box<dyn Error>> { 117 let profile = verify_pkcs7_signature(pkcs7, root_store, flags)?; 118 let profile_json = parse_and_validate_profile(profile, check_udid)?; 119 get_cert_details(&profile_json) 120} 121 122fn verify_pkcs7_signature( 123 pkcs7: &Pkcs7, 124 root_store: &X509Store, 125 flags: Pkcs7Flags, 126) -> Result<Vec<u8>, Box<dyn Error>> { 127 let stack_of_certs = Stack::<X509>::new()?; 128 let mut profile = Vec::new(); 129 pkcs7.verify(&stack_of_certs, root_store, None, Some(&mut profile), flags)?; 130 Ok(profile) 131} 132 133/// validate bundle info and debug info 134pub fn validate_bundle_and_distribution_type( 135 profile_json: &JsonValue, 136 check_udid: bool, 137) -> Result<(), Box<dyn Error>> { 138 let bundle_type = profile_json[PROFILE_TYPE_KEY].try_as_string()?.as_str(); 139 match bundle_type { 140 PROFILE_DEBUG_TYPE => { 141 if check_udid && verify_udid(profile_json).is_err() { 142 return Err("Invalid UDID.".into()); 143 } 144 }, 145 PROFILE_RELEASE_TYPE => { 146 let distribution_type = profile_json[PROFILE_APP_DISTRIBUTION_TYPE_KEY].try_as_string()?.as_str(); 147 match distribution_type { 148 APP_DISTRIBUTION_TYPE_INTERNALTESTING => { 149 if check_udid && verify_udid(profile_json).is_err() { 150 return Err("Invalid UDID.".into()); 151 } 152 }, 153 APP_DISTRIBUTION_TYPE_ENTERPRISE | 154 APP_DISTRIBUTION_TYPE_ENTERPRISE_NORMAL | 155 APP_DISTRIBUTION_TYPE_ENTERPRISE_MDM => { 156 }, 157 _ => { 158 return Err("Invalid app distribution type.".into()); 159 } 160 } 161 } 162 _ => { 163 return Err("Invalid bundle type.".into()); 164 }, 165 } 166 Ok(()) 167} 168 169fn parse_and_validate_profile( 170 profile: Vec<u8>, 171 check_udid: bool, 172) -> Result<JsonValue, Box<dyn Error>> { 173 let profile_json = JsonValue::from_text(profile)?; 174 validate_bundle_and_distribution_type(&profile_json, check_udid)?; 175 Ok(profile_json) 176} 177 178fn get_cert_details(profile_json: &JsonValue) -> Result<(String, String, u32), Box<dyn Error>> { 179 let bundle_type = profile_json[PROFILE_TYPE_KEY].try_as_string()?.as_str(); 180 let profile_type = match bundle_type { 181 PROFILE_DEBUG_TYPE => DebugCertPathType::Developer as u32, 182 PROFILE_RELEASE_TYPE => ReleaseCertPathType::Developer as u32, 183 _ => return Err("Invalid bundle type.".into()), 184 }; 185 let signed_cert = match bundle_type { 186 PROFILE_DEBUG_TYPE => profile_json[PROFILE_BUNDLE_INFO_KEY][PROFILE_BUNDLE_INFO_DEBUG_KEY].try_as_string()?, 187 PROFILE_RELEASE_TYPE => profile_json[PROFILE_BUNDLE_INFO_KEY][PROFILE_BUNDLE_INFO_RELEASE_KEY].try_as_string()?, 188 _ => return Err("Invalid bundle type.".into()), 189 }; 190 let signed_pem = X509::from_pem(signed_cert.as_bytes())?; 191 let subject = format_x509_fabricate_name(signed_pem.subject_name()); 192 let issuer = format_x509_fabricate_name(signed_pem.issuer_name()); 193 Ok((subject, issuer, profile_type)) 194} 195 196lazy_static! { 197 /// global udid 198 pub static ref UDID: Result<String, String> = init_udid(); 199} 200 201fn init_udid() -> Result<String, String> { 202 let mut udid: Vec<u8> = vec![0; 128]; 203 let result = unsafe { CodeSignGetUdid(udid.as_mut_ptr()) }; 204 205 if result != 0 { 206 return Err("Failed to get UDID".to_string()); 207 } 208 209 if let Some(first_zero_index) = udid.iter().position(|&x| x == 0) { 210 udid.truncate(first_zero_index); 211 } 212 213 match String::from_utf8(udid) { 214 Ok(s) => Ok(s), 215 Err(_) => Err("UDID is not valid UTF-8".to_string()), 216 } 217} 218 219/// get device udid 220pub fn get_udid() -> Result<String, String> { 221 UDID.clone() 222} 223 224 225fn verify_signers( 226 pkcs7: &Pkcs7, 227 profile_signer: &[(&String, &String)], 228) -> Result<(), Box<dyn Error>> { 229 let stack_of_certs = Stack::<X509>::new()?; 230 let signers_result = pkcs7.signers(&stack_of_certs, Pkcs7Flags::empty())?; 231 for signer in signers_result { 232 let subject_name = format_x509name_to_string(signer.subject_name()); 233 let issuer_name = format_x509name_to_string(signer.issuer_name()); 234 if !profile_signer.contains(&(&subject_name, &issuer_name)) { 235 return Err("Verification failed.".into()); 236 } 237 } 238 Ok(()) 239} 240 241fn format_x509name_to_string(name: &X509NameRef) -> String { 242 let mut parts = Vec::new(); 243 244 for entry in name.entries() { 245 let tag = match entry.object().nid() { 246 openssl::nid::Nid::COMMONNAME => "CN", 247 openssl::nid::Nid::COUNTRYNAME => "C", 248 openssl::nid::Nid::ORGANIZATIONNAME => "O", 249 openssl::nid::Nid::ORGANIZATIONALUNITNAME => "OU", 250 _ => continue, 251 }; 252 let value = entry.data().as_utf8().unwrap(); 253 parts.push(format!("{}={}", tag, value)); 254 } 255 parts.join(", ") 256} 257 258fn format_x509_fabricate_name(name: &X509NameRef) -> String { 259 let mut common_name = String::new(); 260 let mut organization = String::new(); 261 let mut email = String::new(); 262 263 for entry in name.entries() { 264 let entry_nid = entry.object().nid(); 265 if let Ok(value) = entry.data().as_utf8() { 266 match entry_nid { 267 openssl::nid::Nid::COMMONNAME => common_name = value.to_string(), 268 openssl::nid::Nid::ORGANIZATIONNAME => organization = value.to_string(), 269 openssl::nid::Nid::PKCS9_EMAILADDRESS => email = value.to_string(), 270 _ => continue, 271 }; 272 } 273 } 274 let ret = common_format_fabricate_name(&common_name, &organization, &email); 275 ret 276} 277 278fn get_profile_paths(is_debug: bool) -> Vec<String> { 279 let mut paths = Vec::new(); 280 let profile_prefixes = match is_debug { 281 false => vec![PROFILE_STORE_EL0_PREFIX, PROFILE_STORE_EL1_PREFIX, PROFILE_STORE_EL1_PUBLIC_PREFIX], 282 true => vec![DEBUG_PROFILE_STORE_EL0_PREFIX, DEBUG_PROFILE_STORE_EL1_PREFIX, DEBUG_PROFILE_STORE_EL1_PUBLIC_PREFIX], 283 }; 284 for profile_prefix in profile_prefixes { 285 paths.extend(get_paths_from_prefix(profile_prefix)); 286 } 287 paths 288} 289 290fn get_paths_from_prefix(prefix: &str) -> Vec<String> { 291 let mut paths = Vec::new(); 292 if let Ok(entries) = read_dir(prefix) { 293 for entry in entries.filter_map(Result::ok) { 294 let path = entry.path(); 295 let filename = fmt_store_path(&path.to_string_lossy(), PROFILE_STORE_TAIL); 296 if file_exists(&filename) { 297 paths.push(filename); 298 } 299 } 300 } 301 paths 302} 303 304/// add profile cert path data 305pub fn add_profile_cert_path( 306 root_cert: &PemCollection, 307 cert_paths: &TrustCertPath, 308) -> Result<(), ProfileError> { 309 let x509_store = root_cert.to_x509_store().unwrap(); 310 if process_profile(false, &x509_store, cert_paths.get_profile_info().as_slice()).is_err() { 311 return Err(ProfileError::AddCertPathError); 312 } 313 if process_profile(true, &x509_store, cert_paths.get_debug_profile_info().as_slice()).is_err() { 314 return Err(ProfileError::AddCertPathError); 315 } 316 Ok(()) 317} 318 319fn process_profile( 320 is_debug: bool, 321 x509_store: &X509Store, 322 profile_info: &[(&String, &String)], 323) -> Result<(), ProfileError> { 324 let profiles_paths = get_profile_paths(is_debug); 325 for path in profiles_paths { 326 let mut pkcs7_data = Vec::new(); 327 if load_bytes_from_file(&path, &mut pkcs7_data).is_err() { 328 info!(LOG_LABEL, "load profile failed {}!", @public(path)); 329 continue; 330 } 331 info!(LOG_LABEL, "load profile success {}!", @public(path)); 332 let pkcs7 = match Pkcs7::from_der(&pkcs7_data) { 333 Ok(pk7) => pk7, 334 Err(_) => { 335 error!(LOG_LABEL, "load profile to pkcs7 obj failed {}!", @public(path)); 336 continue; 337 } 338 }; 339 if verify_signers(&pkcs7, profile_info).is_err() { 340 error!(LOG_LABEL, "Invalid signer profile file {}", @public(path)); 341 report_parse_profile_err(&path, HisyseventProfileError::VerifySigner as i32); 342 continue; 343 } 344 let check_udid = unsafe { !IsRdDevice() }; 345 let (subject, issuer, profile_type) = 346 match parse_pkcs7_data(&pkcs7, x509_store, Pkcs7Flags::empty(), check_udid) { 347 Ok(tuple) => tuple, 348 Err(e) => { 349 error!(LOG_LABEL, "Error parsing PKCS7 data: {}, profile file {}", 350 @public(e), @public(path)); 351 report_parse_profile_err(&path, HisyseventProfileError::ParsePkcs7 as i32); 352 continue; 353 } 354 }; 355 if add_cert_path_info(subject, issuer, profile_type, DEFAULT_MAX_CERT_PATH_LEN).is_err() { 356 error!( 357 LOG_LABEL, 358 "Failed to add profile cert path info into ioctl for {}", @public(path) 359 ); 360 report_parse_profile_err(&path, HisyseventProfileError::AddCertPath as i32); 361 continue; 362 } 363 } 364 Ok(()) 365} 366 367fn verify_udid(profile_json: &JsonValue) -> Result<(), String> { 368 let device_udid = get_udid()?; 369 info!(LOG_LABEL, "get device udid {}!", device_udid); 370 let device_id_type = &profile_json[PROFILE_DEBUG_INFO_KEY][PROFILE_DEVICE_ID_TYPE_KEY]; 371 372 if let JsonValue::String(id_type) = device_id_type { 373 if id_type != "udid" { 374 return Err("Invalid device ID type".to_string()); 375 } 376 } else { 377 return Err("Device ID type is not a string".to_string()); 378 } 379 match &profile_json[PROFILE_DEBUG_INFO_KEY][PROFILE_DEVICE_IDS_KEY] { 380 JsonValue::Array(arr) => { 381 if arr.iter().any(|item| match item { 382 JsonValue::String(s) => s == &device_udid, 383 _ => false, 384 }) { 385 Ok(()) 386 } else { 387 Err("UDID not found in the list".to_string()) 388 } 389 } 390 _ => Err("Device IDs are not in an array format".to_string()), 391 } 392} 393 394fn validate_and_convert_inputs( 395 bundle_name: *const c_char, 396 profile: *const u8, 397 profile_size: u32, 398) -> Result<(String, Vec<u8>), ()> { 399 let _bundle_name = c_char_to_string(bundle_name); 400 if _bundle_name.is_empty() { 401 error!(LOG_LABEL, "invalid profile bundle name!"); 402 return Err(()); 403 } 404 let profile_data = cbyte_buffer_to_vec(profile, profile_size); 405 Ok((_bundle_name, profile_data)) 406} 407 408fn process_data(profile_data: &[u8]) -> Result<(String, String, u32), ()> { 409 let store = match X509StoreBuilder::new() { 410 Ok(store) => store.build(), 411 Err(_) => { 412 error!(LOG_LABEL, "Failed to build X509 store"); 413 return Err(()); 414 } 415 }; 416 417 let pkcs7 = match Pkcs7::from_der(profile_data) { 418 Ok(pk7) => pk7, 419 Err(_) => { 420 error!(LOG_LABEL, "load profile to pkcs7 obj failed"); 421 return Err(()); 422 } 423 }; 424 425 match parse_pkcs7_data(&pkcs7, &store, Pkcs7Flags::NOVERIFY, false) { 426 Ok(tuple) => Ok(tuple), 427 Err(_) => { 428 error!(LOG_LABEL, "parse pkcs7 data error"); 429 Err(()) 430 } 431 } 432} 433 434fn create_bundle_path(bundle_name: &str, profile_type: u32) -> Result<String, ()> { 435 let bundle_path = match profile_type { 436 value if value == DebugCertPathType::Developer as u32 => { 437 fmt_store_path(DEBUG_PROFILE_STORE_EL1_PUBLIC_PREFIX, bundle_name) 438 } 439 value if value == ReleaseCertPathType::Developer as u32 => { 440 fmt_store_path(PROFILE_STORE_EL1_PUBLIC_PREFIX, bundle_name) 441 } 442 _ => { 443 error!(LOG_LABEL, "invalid profile type"); 444 return Err(()); 445 } 446 }; 447 Ok(bundle_path) 448} 449 450fn enable_key_in_profile_internal( 451 bundle_name: *const c_char, 452 profile: *const u8, 453 profile_size: u32, 454) -> Result<(), ()> { 455 let (_bundle_name, profile_data) = validate_and_convert_inputs(bundle_name, profile, profile_size)?; 456 let (subject, issuer, profile_type) = process_data(&profile_data)?; 457 let bundle_path = create_bundle_path(&_bundle_name, profile_type)?; 458 info!(LOG_LABEL, "create bundle_path path {}!", @public(bundle_path)); 459 if !file_exists(&bundle_path) && create_file_path(&bundle_path).is_err() { 460 error!(LOG_LABEL, "create bundle_path path {} failed!", @public(bundle_path)); 461 return Err(()); 462 } 463 if change_default_mode_directory(&bundle_path).is_err() { 464 error!(LOG_LABEL, "change bundle_path mode error!"); 465 return Err(()); 466 } 467 let filename = fmt_store_path(&bundle_path, PROFILE_STORE_TAIL); 468 if write_bytes_to_file(&filename, &profile_data).is_err() { 469 error!(LOG_LABEL, "dump profile data error!"); 470 return Err(()); 471 } 472 if change_default_mode_file(&filename).is_err() { 473 error!(LOG_LABEL, "change profile mode error!"); 474 return Err(()); 475 } 476 if add_cert_path_info(subject, issuer, profile_type, DEFAULT_MAX_CERT_PATH_LEN).is_err() { 477 error!(LOG_LABEL, "add profile data error!"); 478 return Err(()); 479 } 480 info!(LOG_LABEL, "finish add cert path in ioctl!"); 481 Ok(()) 482} 483 484fn process_remove_bundle( 485 prefix: &str, 486 bundle_name: &str, 487) -> Result<(), ()> { 488 let bundle_path = fmt_store_path(prefix, bundle_name); 489 490 if !file_exists(&bundle_path) { 491 return Err(()); 492 } 493 494 let filename = fmt_store_path(&bundle_path, PROFILE_STORE_TAIL); 495 let mut profile_data = Vec::new(); 496 if load_bytes_from_file(&filename, &mut profile_data).is_err() { 497 error!(LOG_LABEL, "load profile data error!"); 498 return Err(()); 499 } 500 501 let (subject, issuer, profile_type) = process_data(&profile_data)?; 502 if delete_file_path(&bundle_path).is_err() { 503 error!(LOG_LABEL, "remove profile data error!"); 504 return Err(()); 505 } 506 507 info!(LOG_LABEL, "remove bundle_path path {}!", @public(bundle_path)); 508 509 if remove_cert_path_info(subject, issuer, profile_type, DEFAULT_MAX_CERT_PATH_LEN).is_err() { 510 error!(LOG_LABEL, "remove profile data error!"); 511 return Err(()); 512 } 513 514 info!(LOG_LABEL, "finish remove cert path in ioctl!"); 515 Ok(()) 516} 517 518fn remove_key_in_profile_internal(bundle_name: *const c_char) -> Result<(), ()> { 519 let _bundle_name = c_char_to_string(bundle_name); 520 if _bundle_name.is_empty() { 521 error!(LOG_LABEL, "Invalid bundle name"); 522 return Err(()); 523 } 524 525 let profile_prefix = vec![ 526 DEBUG_PROFILE_STORE_EL0_PREFIX, 527 PROFILE_STORE_EL0_PREFIX, 528 DEBUG_PROFILE_STORE_EL1_PREFIX, 529 PROFILE_STORE_EL1_PREFIX, 530 DEBUG_PROFILE_STORE_EL1_PUBLIC_PREFIX, 531 PROFILE_STORE_EL1_PUBLIC_PREFIX, 532 ]; 533 534 let mut rm_succ = false; 535 for prefix in profile_prefix { 536 if process_remove_bundle(prefix, &_bundle_name).is_ok() { 537 rm_succ = true; 538 } 539 } 540 if rm_succ { 541 Ok(()) 542 } else { 543 error!(LOG_LABEL, "Failed to remove bundle profile info, bundleName: {}.", @public(_bundle_name)); 544 Err(()) 545 } 546} 547 548fn c_char_to_string(c_str: *const c_char) -> String { 549 unsafe { 550 if c_str.is_null() { 551 return String::new(); 552 } 553 let c_str = CStr::from_ptr(c_str); 554 c_str.to_string_lossy().to_string() 555 } 556} 557 558fn cbyte_buffer_to_vec(data: *const u8, size: u32) -> Vec<u8> { 559 unsafe { 560 if data.is_null() { 561 return Vec::new(); 562 } 563 let data_slice = std::slice::from_raw_parts(data, size as usize); 564 let mut result = Vec::with_capacity(size as usize); 565 result.extend_from_slice(data_slice); 566 result 567 } 568} 569