1 /*
2  * Copyright (c) 2023 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 use super::cs_hisysevent;
16 use super::profile_utils::IsDeveloperModeOn;
17 use hilog_rust::{error, hilog, info, HiLogLabel, LogType};
18 use std::ffi::{c_char, CString};
19 use ylong_json::JsonValue;
20 
21 extern "C" {
IsRdDevice() -> bool22     fn IsRdDevice() -> bool;
23 }
24 
25 const LOG_LABEL: HiLogLabel = HiLogLabel {
26     log_type: LogType::LogCore,
27     domain: 0xd005a06,
28     tag: "CODE_SIGN",
29 };
30 const TRUST_PROFILE_PATH_KEY: &str = "trust-profile-path";
31 const TRUST_CERT_PATH_KEY: &str = "trust-cert-path";
32 const MODE_KEY: &str = "mode";
33 const TYPE_KEY: &str = "type";
34 const SUBJECT_KEY: &str = "subject";
35 const ISSUER_KEY: &str = "issuer";
36 const MAX_CERT_PATH: &str = "max-certs-path";
37 const COMMON_NAME_CHAR_LIMIT: usize = 7;
38 /// profile cert path error
39 pub enum CertPathError {
40     /// cert path add remove error
41     CertPathOperationError,
42 }
43 /// release cert path type
44 pub enum ReleaseCertPathType {
45     /// release platform code
46     Platform = 0x1,
47     /// release authed code
48     Authed = 0x2,
49     /// release developer code
50     Developer = 0x3,
51     /// release block code
52     Block = 0x4,
53     /// restrict code
54     Restricted = 0xff,
55 }
56 
57 impl ReleaseCertPathType {
from_str(s: &str) -> Result<u32, ()>58     fn from_str(s: &str) -> Result<u32, ()> {
59         match s {
60             "Platform" => Ok(ReleaseCertPathType::Platform as u32),
61             "Authed" => Ok(ReleaseCertPathType::Authed as u32),
62             "Developer" => Ok(ReleaseCertPathType::Developer as u32),
63             "Block" => Ok(ReleaseCertPathType::Block as u32),
64             "Restricted" => Ok(ReleaseCertPathType::Restricted as u32),
65             _ => Err(()),
66         }
67     }
68 }
69 /// debug cert path type
70 pub enum DebugCertPathType {
71     /// debug platform code
72     Platform = 0x101,
73     /// debug authed code
74     Authed = 0x102,
75     /// debug developer code
76     Developer = 0x103,
77     /// debug block code
78     Block = 0x104,
79     /// debug debug code
80     Debug = 0x105,
81     /// restrict code
82     Restricted = 0x1ff,
83 }
84 
85 impl DebugCertPathType {
from_str(s: &str) -> Result<u32, ()>86     fn from_str(s: &str) -> Result<u32, ()> {
87         match s {
88             "Platform" => Ok(DebugCertPathType::Platform as u32),
89             "Authed" => Ok(DebugCertPathType::Authed as u32),
90             "Developer" => Ok(DebugCertPathType::Developer as u32),
91             "Block" => Ok(DebugCertPathType::Block as u32),
92             "Debug" => Ok(DebugCertPathType::Debug as u32),
93             "Restricted" => Ok(DebugCertPathType::Restricted as u32),
94             _ => Err(()),
95         }
96     }
97 }
98 /// profile cert path type
99 pub enum ProfileCertPathType {
100     /// profile developer code
101     Developer = 0x01,
102     /// profile debug code
103     Debug = 0x02,
104 }
105 
106 extern "C" {
AddCertPath(info: *const CertPathInfo) -> i32107     fn AddCertPath(info: *const CertPathInfo) -> i32;
RemoveCertPath(info: *const CertPathInfo) -> i32108     fn RemoveCertPath(info: *const CertPathInfo) -> i32;
109 }
110 /// structure of trust-app-source from json file
111 pub struct TrustCertPath {
112     /// vec to contains valid profile_signers
113     pub profile_signers: Vec<CertPath>,
114     /// vec to contains app source data
115     pub app_sources: Vec<CertPath>,
116 }
117 /// inner data of trust-app-source
118 pub struct CertPath {
119     ///mode
120     pub mode: String,
121     /// subject
122     pub subject: String,
123     /// issuer
124     pub issuer_ca: String,
125     /// max certs path
126     pub max_certs_path: u32,
127     /// cert path type
128     pub cert_path_type: u32,
129 }
130 impl Default for TrustCertPath {
default() -> Self131     fn default() -> Self {
132         Self::new()
133     }
134 }
135 impl TrustCertPath {
136     /// init object
new() -> Self137     pub fn new() -> Self {
138         TrustCertPath {
139             profile_signers: Vec::new(),
140             app_sources: Vec::new(),
141         }
142     }
143     /// get source.profile_signing_cert from json array to check developer profiles
get_profile_info(&self) -> Vec<(&String, &String)>144     pub fn get_profile_info(&self) -> Vec<(&String, &String)> {
145         self.profile_signers
146             .iter()
147             .filter(|source| source.cert_path_type == ProfileCertPathType::Developer as u32)
148             .map(|source| (&source.subject, &source.issuer_ca))
149             .collect()
150     }
151     /// get source.profile_signing_cert from json array to check debug profiles
get_debug_profile_info(&self) -> Vec<(&String, &String)>152     pub fn get_debug_profile_info(&self) -> Vec<(&String, &String)> {
153         self.profile_signers
154             .iter()
155             .filter(|source| source.cert_path_type == ProfileCertPathType::Debug as u32)
156             .map(|source| (&source.subject, &source.issuer_ca))
157             .collect()
158     }
159     /// add signing cert paths to kernel
add_cert_paths(&self) -> Result<(), CertPathError>160     pub fn add_cert_paths(&self) -> Result<(), CertPathError> {
161         for cert_path in &self.app_sources {
162             if !unsafe { IsDeveloperModeOn() } && &cert_path.mode == "Dev" {
163                 continue;
164             }
165             if !cert_path.subject.is_empty()
166             && !cert_path.issuer_ca.is_empty()
167             && cert_path.add_subject_cert_path().is_err() {
168                     error!(
169                         LOG_LABEL,
170                         "add signing cert path into ioctl error {} : {} : {}",
171                         cert_path.subject,
172                         cert_path.issuer_ca,
173                         cert_path.cert_path_type
174                     );
175                     continue;
176             }
177         }
178         Ok(())
179     }
180 
parse_cert_profile<F>( cert_profile: &JsonValue, path_type_resolver: F, ) -> Result<CertPath, ()> where F: Fn(&str, &str) -> Result<u32, ()>181     fn parse_cert_profile<F>(
182         cert_profile: &JsonValue,
183         path_type_resolver: F,
184     ) -> Result<CertPath, ()>
185     where
186         F: Fn(&str, &str) -> Result<u32, ()> {
187         let cert_mode = match cert_profile[MODE_KEY].try_as_string() {
188             Ok(v) => v,
189             Err(e) => {
190                 error!(LOG_LABEL, "Error JSON MODE_KEY from file {:?}", e);
191                 return Err(());
192             }
193         };
194 
195         let cert_type = match cert_profile[TYPE_KEY].try_as_string() {
196             Ok(v) => v,
197             Err(e) => {
198                 error!(LOG_LABEL, "Error JSON TYPE_KEY from file {:?}", e);
199                 return Err(());
200             }
201         };
202 
203         let path_type = match path_type_resolver(cert_mode, cert_type) {
204             Ok(v) => v,
205             Err(e) => {
206                 error!(LOG_LABEL, "Error JSON Path Type from file {:?}", e);
207                 return Err(());
208             }
209         };
210 
211         let signing_cert = match cert_profile[SUBJECT_KEY].try_as_string() {
212             Ok(v) => v,
213             Err(e) => {
214                 error!(LOG_LABEL, "Error JSON SUBJECT_KEY from file {:?}", e);
215                 return Err(());
216             }
217         };
218 
219         let issuer = match cert_profile[ISSUER_KEY].try_as_string() {
220             Ok(v) => v,
221             Err(e) => {
222                 error!(LOG_LABEL, "Error JSON ISSUER_KEY from file {:?}", e);
223                 return Err(());
224             }
225         };
226 
227         let path_len = match cert_profile[MAX_CERT_PATH].try_as_number().and_then(|n| n.try_as_i64()) {
228             Ok(v) => v,
229             Err(e) => {
230                 error!(LOG_LABEL, "Error JSON MAX_CERT_PATH from file {:?}", e);
231                 return Err(());
232             }
233         };
234 
235         Ok(CertPath {
236             mode: cert_mode.to_string(),
237             subject: signing_cert.to_string(),
238             issuer_ca: issuer.to_string(),
239             cert_path_type: path_type,
240             max_certs_path: path_len as u32,
241         })
242     }
243 
issuer_resolver(cert_mode: &str, _cert_type: &str) -> Result<u32, ()>244     fn issuer_resolver(cert_mode: &str, _cert_type: &str) -> Result<u32, ()> {
245         match cert_mode {
246             "developer" => Ok(ProfileCertPathType::Developer as u32),
247             "debug" => Ok(ProfileCertPathType::Debug as u32),
248             _ => Err(()),
249         }
250     }
251 
path_resolver(cert_mode: &str, cert_type: &str) -> Result<u32, ()>252     fn path_resolver(cert_mode: &str, cert_type: &str) -> Result<u32, ()> {
253         match cert_mode {
254             "Release" => ReleaseCertPathType::from_str(cert_type),
255             "Dev" => DebugCertPathType::from_str(cert_type),
256             _ => Err(()),
257         }
258     }
259 
260     /// load cert path from json file
load_cert_path_from_json_file(&mut self, file_path: &str)261     pub fn load_cert_path_from_json_file(&mut self, file_path: &str) {
262         let value = match JsonValue::from_file(file_path) {
263             Ok(v) => v,
264             Err(e) => {
265                 error!(
266                     LOG_LABEL,
267                     "Error loading JSON from file {}: {:?}", file_path, e
268                 );
269                 return;
270             }
271         };
272 
273         let certs_profile_issuer = match value[TRUST_PROFILE_PATH_KEY].try_as_array() {
274             Ok(array) => array,
275             Err(_) => {
276                 error!(
277                     LOG_LABEL,
278                     "Cannot get preset key TRUST_PROFILE_PATH_KEY from file "
279                 );
280                 return;
281             }
282         };
283 
284         let cert_path_array = match value[TRUST_CERT_PATH_KEY].try_as_array() {
285             Ok(array) => array,
286             Err(_) => {
287                 error!(
288                     LOG_LABEL,
289                     "Cannot get preset key TRUST_CERT_PATH_KEY from file "
290                 );
291                 return;
292             }
293         };
294 
295         for cert_profile in certs_profile_issuer.iter() {
296             match Self::parse_cert_profile(cert_profile, Self::issuer_resolver) {
297                 Ok(app_source) => self.profile_signers.push(app_source),
298                 Err(e) => {
299                     error!(LOG_LABEL, "Error parsing cert profile issuer : {:?}", e);
300                     continue;
301                 }
302             }
303         }
304 
305         for cert_path in cert_path_array.iter() {
306             match Self::parse_cert_profile(cert_path, Self::path_resolver) {
307                 Ok(app_source) => self.app_sources.push(app_source),
308                 Err(e) => {
309                     error!(LOG_LABEL, "Error parsing cert path: {:?}", e);
310                     continue;
311                 }
312             }
313         }
314     }
315 }
316 
317 impl CertPath {
318     /// add single app cert path
add_subject_cert_path(&self) -> Result<(), CertPathError>319     pub fn add_subject_cert_path(&self) -> Result<(), CertPathError> {
320         let subject = fabricate_name(&self.subject);
321         let issuer = fabricate_name(&self.issuer_ca);
322         add_cert_path_info(
323             subject,
324             issuer,
325             self.cert_path_type,
326             self.max_certs_path,
327         )?;
328         Ok(())
329     }
330 }
331 
332 #[repr(C)]
333 /// cert path info reflect to C
334 pub struct CertPathInfo {
335     /// signing_length
336     pub signing_length: u32,
337     /// issuer_length
338     pub issuer_length: u32,
339     /// signing
340     pub signing: u64,
341     /// issuer
342     pub issuer: u64,
343     /// path length
344     pub path_len: u32,
345     /// path type
346     pub path_type: u32,
347     __reserved: [u8; 32],
348 }
349 
fabricate_name(subject: &str) -> String350 fn fabricate_name(subject: &str) -> String {
351     if subject == "ALL" {
352         return "ALL".to_string();
353     }
354     let mut common_name = String::new();
355     let mut organization = String::new();
356     let mut email = String::new();
357     let parts: Vec<&str> = subject.split(',').collect();
358     for part in parts {
359         let inner: Vec<&str> = part.split('=').collect();
360         if inner.len() < 2 {
361             continue;
362         }
363         let inner_trimmed: Vec<&str> = inner.iter().map(|s| s.trim()).collect();
364         if inner_trimmed[0] == "CN" {
365             common_name = inner_trimmed[1].into();
366         } else if inner_trimmed[0] == "O" {
367             organization = inner_trimmed[1].into();
368         } else if inner_trimmed[0] == "E" {
369             email = inner_trimmed[1].into();
370         }
371     }
372     let ret = common_format_fabricate_name(&common_name, &organization, &email);
373     ret
374 }
375 /// common rule to fabricate name
common_format_fabricate_name(common_name: &str, organization: &str, email: &str) -> String376 pub fn common_format_fabricate_name(common_name: &str, organization: &str, email: &str) -> String {
377     let mut ret = String::new();
378     if !common_name.is_empty() && !organization.is_empty() {
379         if common_name.len() >= organization.len() && common_name.starts_with(organization) {
380             return common_name.to_string();
381         }
382         if common_name.len() >= COMMON_NAME_CHAR_LIMIT && organization.len() >= COMMON_NAME_CHAR_LIMIT {
383             let common_name_prefix = &common_name.as_bytes()[..COMMON_NAME_CHAR_LIMIT];
384             let organization_prefix = &organization.as_bytes()[..COMMON_NAME_CHAR_LIMIT];
385             if common_name_prefix == organization_prefix {
386                 ret = common_name.to_string();
387                 return ret;
388             }
389         }
390         ret = format!("{}: {}", organization, common_name);
391     } else if !common_name.is_empty() {
392         ret = common_name.to_string();
393     } else if !organization.is_empty() {
394         ret = organization.to_string();
395     } else if !email.is_empty() {
396         ret = email.to_string();
397     }
398     ret
399 }
400 
convert_cert_type(cert_path_type: u32) -> u32401 fn convert_cert_type(cert_path_type: u32) -> u32 {
402     if cert_path_type == ReleaseCertPathType::Restricted as u32 {
403         if env!("support_openharmony_ca") == "on" || unsafe { IsRdDevice() } {
404             return ReleaseCertPathType::Authed as u32;
405         } else {
406             return 0;   // return invalid type
407         }
408     } else if cert_path_type == DebugCertPathType::Restricted as u32 {
409         if env!("support_openharmony_ca") == "on" || unsafe { IsRdDevice() } {
410             return DebugCertPathType::Authed as u32;
411         } else {
412             return 0;   // return invalid type
413         }
414     }
415     cert_path_type
416 }
417 
cert_path_operation<F>( subject: String, issuer: String, cert_path_type: u32, path_length: u32, operation: F, op_name: &str, ) -> Result<(), CertPathError> where F: Fn(&CertPathInfo) -> i32418 fn cert_path_operation<F>(
419     subject: String,
420     issuer: String,
421     cert_path_type: u32,
422     path_length: u32,
423     operation: F,
424     op_name: &str,
425 ) -> Result<(), CertPathError>
426 where
427     F: Fn(&CertPathInfo) -> i32 {
428     if subject.is_empty() || issuer.is_empty() {
429         return Err(CertPathError::CertPathOperationError);
430     }
431 
432     let subject_cstring = CString::new(subject).expect("convert to subject_cstring error!");
433     let issuer_cstring = CString::new(issuer).expect("convert to cstring error!");
434     let converted_cert_type = convert_cert_type(cert_path_type);
435 
436     // invalid cert type, skip adding
437     if converted_cert_type == 0u32 {
438         return Ok(());
439     }
440 
441     let cert_path_info = CertPathInfo {
442         signing_length: subject_cstring.as_bytes().len() as u32,
443         issuer_length: issuer_cstring.as_bytes().len() as u32,
444         signing: subject_cstring.as_ptr() as u64,
445         issuer: issuer_cstring.as_ptr() as u64,
446         path_len: path_length,
447         path_type: converted_cert_type,
448         __reserved: [0; 32],
449     };
450     let ret = operation(&cert_path_info);
451     info!(LOG_LABEL, "ioctl return:{}", @public(ret));
452     if ret < 0 {
453         cs_hisysevent::report_add_key_err(op_name, ret);
454         return Err(CertPathError::CertPathOperationError);
455     }
456     Ok(())
457 }
458 /// add cert path info in kernel
add_cert_path_info( subject: String, issuer: String, cert_path_type: u32, path_length: u32, ) -> Result<(), CertPathError>459 pub fn add_cert_path_info(
460     subject: String,
461     issuer: String,
462     cert_path_type: u32,
463     path_length: u32,
464 ) -> Result<(), CertPathError> {
465     cert_path_operation(
466         subject,
467         issuer,
468         cert_path_type,
469         path_length,
470         |info| unsafe { AddCertPath(info) },
471         "add cert_path",
472     )?;
473     Ok(())
474 }
475 /// remove cert path info in kernel
remove_cert_path_info( subject: String, issuer: String, cert_path_type: u32, path_length: u32, ) -> Result<(), CertPathError>476 pub fn remove_cert_path_info(
477     subject: String,
478     issuer: String,
479     cert_path_type: u32,
480     path_length: u32,
481 ) -> Result<(), CertPathError> {
482     cert_path_operation(
483         subject,
484         issuer,
485         cert_path_type,
486         path_length,
487         |info| unsafe { RemoveCertPath(info) },
488         "remove cert_path",
489     )?;
490     Ok(())
491 }
492