• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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