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