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