• 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 !unsafe { IsDeveloperModeOn() } && &cert_path.mode == "Dev" {
164                 continue;
165             }
166             if !cert_path.subject.is_empty()
167             && !cert_path.issuer_ca.is_empty()
168             && cert_path.add_subject_cert_path().is_err() {
169                     error!(
170                         LOG_LABEL,
171                         "add signing cert path into ioctl error {} : {} : {}",
172                         cert_path.subject,
173                         cert_path.issuer_ca,
174                         cert_path.cert_path_type
175                     );
176                     continue;
177             }
178         }
179         Ok(())
180     }
181 
parse_cert_profile<F>( cert_profile: &JsonValue, path_type_resolver: F, ) -> Result<CertPath, ()> where F: Fn(&str, &str) -> Result<u32, ()>182     fn parse_cert_profile<F>(
183         cert_profile: &JsonValue,
184         path_type_resolver: F,
185     ) -> Result<CertPath, ()>
186     where
187         F: Fn(&str, &str) -> Result<u32, ()> {
188         let cert_mode = match cert_profile[MODE_KEY].try_as_string() {
189             Ok(v) => v,
190             Err(e) => {
191                 error!(LOG_LABEL, "Error JSON MODE_KEY from file {:?}", e);
192                 return Err(());
193             }
194         };
195 
196         let cert_type = match cert_profile[TYPE_KEY].try_as_string() {
197             Ok(v) => v,
198             Err(e) => {
199                 error!(LOG_LABEL, "Error JSON TYPE_KEY from file {:?}", e);
200                 return Err(());
201             }
202         };
203 
204         let path_type = match path_type_resolver(cert_mode, cert_type) {
205             Ok(v) => v,
206             Err(e) => {
207                 error!(LOG_LABEL, "Error JSON Path Type from file {:?}", e);
208                 return Err(());
209             }
210         };
211 
212         let signing_cert = match cert_profile[SUBJECT_KEY].try_as_string() {
213             Ok(v) => v,
214             Err(e) => {
215                 error!(LOG_LABEL, "Error JSON SUBJECT_KEY from file {:?}", e);
216                 return Err(());
217             }
218         };
219 
220         let issuer = match cert_profile[ISSUER_KEY].try_as_string() {
221             Ok(v) => v,
222             Err(e) => {
223                 error!(LOG_LABEL, "Error JSON ISSUER_KEY from file {:?}", e);
224                 return Err(());
225             }
226         };
227 
228         let path_len = match cert_profile[MAX_CERT_PATH].try_as_number().and_then(|n| n.try_as_i64()) {
229             Ok(v) => v,
230             Err(e) => {
231                 error!(LOG_LABEL, "Error JSON MAX_CERT_PATH from file {:?}", e);
232                 return Err(());
233             }
234         };
235 
236         Ok(CertPath {
237             mode: cert_mode.to_string(),
238             subject: signing_cert.to_string(),
239             issuer_ca: issuer.to_string(),
240             cert_path_type: path_type,
241             max_certs_path: path_len as u32,
242         })
243     }
244 
issuer_resolver(cert_mode: &str, _cert_type: &str) -> Result<u32, ()>245     fn issuer_resolver(cert_mode: &str, _cert_type: &str) -> Result<u32, ()> {
246         match cert_mode {
247             "developer" => Ok(ProfileCertPathType::Developer as u32),
248             "debug" => Ok(ProfileCertPathType::Debug as u32),
249             _ => Err(()),
250         }
251     }
252 
path_resolver(cert_mode: &str, cert_type: &str) -> Result<u32, ()>253     fn path_resolver(cert_mode: &str, cert_type: &str) -> Result<u32, ()> {
254         match cert_mode {
255             "Release" => ReleaseCertPathType::from_str(cert_type),
256             "Dev" => DebugCertPathType::from_str(cert_type),
257             _ => Err(()),
258         }
259     }
260 
261     /// load cert path from json file
load_cert_path_from_json_file(&mut self, file_path: &str)262     pub fn load_cert_path_from_json_file(&mut self, file_path: &str) {
263         let value = match JsonValue::from_file(file_path) {
264             Ok(v) => v,
265             Err(e) => {
266                 error!(
267                     LOG_LABEL,
268                     "Error loading JSON from file {}: {:?}", file_path, e
269                 );
270                 return;
271             }
272         };
273 
274         let certs_profile_issuer = match value[TRUST_PROFILE_PATH_KEY].try_as_array() {
275             Ok(array) => array,
276             Err(_) => {
277                 error!(
278                     LOG_LABEL,
279                     "Cannot get preset key TRUST_PROFILE_PATH_KEY from file "
280                 );
281                 return;
282             }
283         };
284 
285         let cert_path_array = match value[TRUST_CERT_PATH_KEY].try_as_array() {
286             Ok(array) => array,
287             Err(_) => {
288                 error!(
289                     LOG_LABEL,
290                     "Cannot get preset key TRUST_CERT_PATH_KEY from file "
291                 );
292                 return;
293             }
294         };
295 
296         for cert_profile in certs_profile_issuer.iter() {
297             match Self::parse_cert_profile(cert_profile, Self::issuer_resolver) {
298                 Ok(app_source) => self.profile_signers.push(app_source),
299                 Err(e) => {
300                     error!(LOG_LABEL, "Error parsing cert profile issuer : {:?}", e);
301                     continue;
302                 }
303             }
304         }
305 
306         for cert_path in cert_path_array.iter() {
307             match Self::parse_cert_profile(cert_path, Self::path_resolver) {
308                 Ok(app_source) => self.app_sources.push(app_source),
309                 Err(e) => {
310                     error!(LOG_LABEL, "Error parsing cert path: {:?}", e);
311                     continue;
312                 }
313             }
314         }
315     }
316 }
317 
318 impl CertPath {
319     /// add single app cert path
add_subject_cert_path(&self) -> Result<(), CertPathError>320     pub fn add_subject_cert_path(&self) -> Result<(), CertPathError> {
321         let subject = fabricate_name(&self.subject);
322         let issuer = fabricate_name(&self.issuer_ca);
323         add_cert_path_info(
324             subject,
325             issuer,
326             self.cert_path_type,
327             EMPTY_APP_ID.to_string(),
328             self.max_certs_path,
329         )?;
330         Ok(())
331     }
332 }
333 
334 #[repr(C)]
335 /// cert path info reflect to C
336 pub struct CertPathInfo {
337     /// signing_length
338     pub signing_length: u32,
339     /// issuer_length
340     pub issuer_length: u32,
341     /// signing
342     pub signing: u64,
343     /// issuer
344     pub issuer: u64,
345     /// path length
346     pub path_len: u32,
347     /// path type
348     pub path_type: u32,
349     /// app id
350     pub app_id: u64,
351     /// app id length
352     pub app_id_length: u32,
353     __reserved: [u8; 20],
354 }
355 
fabricate_name(subject: &str) -> String356 fn fabricate_name(subject: &str) -> String {
357     if subject == "ALL" {
358         return "ALL".to_string();
359     }
360     let mut common_name = String::new();
361     let mut organization = String::new();
362     let mut email = String::new();
363     let parts: Vec<&str> = subject.split(',').collect();
364     for part in parts {
365         let inner: Vec<&str> = part.split('=').collect();
366         if inner.len() < 2 {
367             continue;
368         }
369         let inner_trimmed: Vec<&str> = inner.iter().map(|s| s.trim()).collect();
370         if inner_trimmed[0] == "CN" {
371             common_name = inner_trimmed[1].into();
372         } else if inner_trimmed[0] == "O" {
373             organization = inner_trimmed[1].into();
374         } else if inner_trimmed[0] == "E" {
375             email = inner_trimmed[1].into();
376         }
377     }
378     let ret = common_format_fabricate_name(&common_name, &organization, &email);
379     ret
380 }
381 /// common rule to fabricate name
common_format_fabricate_name(common_name: &str, organization: &str, email: &str) -> String382 pub fn common_format_fabricate_name(common_name: &str, organization: &str, email: &str) -> String {
383     let mut ret = String::new();
384     if !common_name.is_empty() && !organization.is_empty() {
385         if common_name.len() >= organization.len() && common_name.starts_with(organization) {
386             return common_name.to_string();
387         }
388         if common_name.len() >= COMMON_NAME_CHAR_LIMIT && organization.len() >= COMMON_NAME_CHAR_LIMIT {
389             let common_name_prefix = &common_name.as_bytes()[..COMMON_NAME_CHAR_LIMIT];
390             let organization_prefix = &organization.as_bytes()[..COMMON_NAME_CHAR_LIMIT];
391             if common_name_prefix == organization_prefix {
392                 ret = common_name.to_string();
393                 return ret;
394             }
395         }
396         ret = format!("{}: {}", organization, common_name);
397     } else if !common_name.is_empty() {
398         ret = common_name.to_string();
399     } else if !organization.is_empty() {
400         ret = organization.to_string();
401     } else if !email.is_empty() {
402         ret = email.to_string();
403     }
404     ret
405 }
406 
convert_cert_type(cert_path_type: u32) -> u32407 fn convert_cert_type(cert_path_type: u32) -> u32 {
408     if cert_path_type == ReleaseCertPathType::Restricted as u32 {
409         if env!("support_openharmony_ca") == "on" || unsafe { IsRdDevice() } {
410             return ReleaseCertPathType::Authed as u32;
411         } else {
412             return 0;   // return invalid type
413         }
414     } else if cert_path_type == DebugCertPathType::Restricted as u32 {
415         if env!("support_openharmony_ca") == "on" || unsafe { IsRdDevice() } {
416             return DebugCertPathType::Authed as u32;
417         } else {
418             return 0;   // return invalid type
419         }
420     }
421     cert_path_type
422 }
423 
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) -> i32424 fn cert_path_operation<F>(
425     subject: String,
426     issuer: String,
427     cert_path_type: u32,
428     app_id: String,
429     path_length: u32,
430     operation: F,
431     op_name: &str,
432 ) -> Result<(), CertPathError>
433 where
434     F: Fn(&CertPathInfo) -> i32 {
435     if subject.is_empty() || issuer.is_empty() {
436         return Err(CertPathError::CertPathOperationError);
437     }
438 
439     if !app_id.is_empty() {
440         info!(LOG_LABEL, "sending APP_ID '{}' through ioctl", @public(app_id));
441     }
442     let subject_cstring = CString::new(subject).expect("convert to subject_cstring error!");
443     let issuer_cstring = CString::new(issuer).expect("convert to issuer_cstring error!");
444     let converted_cert_type = convert_cert_type(cert_path_type);
445     let app_id_cstring = CString::new(app_id).expect("convert to app_id_cstring error!");
446 
447     // invalid cert type, skip adding
448     if converted_cert_type == 0u32 {
449         return Ok(());
450     }
451 
452     let cert_path_info = CertPathInfo {
453         signing_length: subject_cstring.as_bytes().len() as u32,
454         issuer_length: issuer_cstring.as_bytes().len() as u32,
455         signing: subject_cstring.as_ptr() as u64,
456         issuer: issuer_cstring.as_ptr() as u64,
457         path_len: path_length,
458         path_type: converted_cert_type,
459         app_id: app_id_cstring.as_ptr() as u64,
460         app_id_length: app_id_cstring.as_bytes().len() as u32,
461         __reserved: [0; 20],
462     };
463     let ret = operation(&cert_path_info);
464     info!(LOG_LABEL, "ioctl return:{}", @public(ret));
465     if ret < 0 {
466         cs_hisysevent::report_add_key_err(op_name, ret);
467         return Err(CertPathError::CertPathOperationError);
468     }
469     Ok(())
470 }
471 /// 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>472 pub fn add_cert_path_info(
473     subject: String,
474     issuer: String,
475     cert_path_type: u32,
476     app_id: String,
477     path_length: u32,
478 ) -> Result<(), CertPathError> {
479     cert_path_operation(
480         subject,
481         issuer,
482         cert_path_type,
483         app_id,
484         path_length,
485         |info| unsafe { AddCertPath(info) },
486         "add cert_path",
487     )?;
488     Ok(())
489 }
490 /// 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>491 pub fn remove_cert_path_info(
492     subject: String,
493     issuer: String,
494     cert_path_type: u32,
495     app_id: String,
496     path_length: u32,
497 ) -> Result<(), CertPathError> {
498     cert_path_operation(
499         subject,
500         issuer,
501         cert_path_type,
502         app_id,
503         path_length,
504         |info| unsafe { RemoveCertPath(info) },
505         "remove cert_path",
506     )?;
507     Ok(())
508 }
509