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