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