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