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