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
16use super::cert_chain_utils::PemCollection;
17use super::cert_path_utils::TrustCertPath;
18use super::cert_utils::{get_cert_path, get_trusted_certs};
19use super::cs_hisysevent;
20use super::profile_utils::add_profile_cert_path;
21use hilog_rust::{error, hilog, info, HiLogLabel, LogType};
22use openssl::error::ErrorStack;
23use std::ffi::{c_char, CString};
24use std::fs::File;
25use std::io::{BufRead, BufReader};
26use std::option::Option;
27use std::ptr;
28use std::thread;
29use std::time::{Duration, Instant};
30use std::path::Path;
31
32const LOG_LABEL: HiLogLabel = HiLogLabel {
33    log_type: LogType::LogCore,
34    domain: 0xd005a06, // security domain
35    tag: "CODE_SIGN",
36};
37
38const CERT_DATA_MAX_SIZE: usize = 8192;
39const PROC_KEY_FILE_PATH: &str = "/proc/keys";
40const KEYRING_TYPE: &str = "keyring";
41const FSVERITY_KEYRING_NAME: &str = ".fs-verity";
42const LOCAL_KEY_NAME: &str = "local_key";
43const CODE_SIGN_KEY_NAME_PREFIX: &str = "fs_verity_key";
44const PROFILE_STORE_EL1: &str = "/data/service/el1/public/profiles";
45const PROFILE_SEARCH_SLEEP_TIME: u64 = 200;
46const PROFILE_SEARCH_SLEEP_OUT_TIME: u64 = 600;
47const SUCCESS: i32 = 0;
48
49type KeySerial = i32;
50
51extern "C" {
52    fn InitLocalCertificate(cert_data: *mut u8, cert_size: *mut usize) -> i32;
53    fn AddKey(
54        type_name: *const u8,
55        description: *const u8,
56        payload: *const u8,
57        plen: usize,
58        ring_id: KeySerial,
59    ) -> KeySerial;
60    fn KeyctlRestrictKeyring(
61        ring_id: KeySerial,
62        type_name: *const u8,
63        restriction: *const u8,
64    ) -> KeySerial;
65    fn CheckUserUnlock() -> bool;
66}
67
68fn print_openssl_error_stack(error_stack: ErrorStack) {
69    for error in error_stack.errors() {
70        error!(LOG_LABEL, "{}", @public(error.to_string()));
71    }
72}
73
74fn get_local_key() -> Option<Vec<u8>> {
75    let mut cert_size = CERT_DATA_MAX_SIZE;
76    let mut cert_data = Vec::with_capacity(cert_size);
77    let pcert = cert_data.as_mut_ptr();
78
79    unsafe {
80        let ret = InitLocalCertificate(pcert, &mut cert_size);
81        if ret == 0 {
82            cert_data.set_len(cert_size);
83            Some(cert_data)
84        } else {
85            None
86        }
87    }
88}
89
90/// parse key info
91/// [Serial][Flags][Usage][Expiry][Permissions][UID][GID][TypeName][Description]: [Summary]
92///   [0]     [1]    [2]    [3]       [4]       [5]  [6]     [7]        [8]         [9]
93/// 3985ad4c I------  1    perm     082f0000     0    0    keyring  .fs-verity:   empty
94fn parse_key_info(line: String) -> Option<KeySerial> {
95    let attrs: Vec<&str> = line.split_whitespace().collect();
96    if attrs.len() != 10 {
97        return None;
98    }
99    if attrs[7] == KEYRING_TYPE && attrs[8].strip_suffix(':') == Some(FSVERITY_KEYRING_NAME) {
100        match KeySerial::from_str_radix(attrs[0], 16) {
101            Ok(x) => Some(x),
102            Err(error) => {
103                error!(LOG_LABEL, "Convert KeySerial failed: {}", error);
104                None
105            }
106        }
107    } else {
108        None
109    }
110}
111
112fn enable_key(key_id: KeySerial, key_name: &str, cert_data: &Vec<u8>) -> i32 {
113    let type_name = CString::new("asymmetric").expect("type name is invalid");
114    let keyname = CString::new(key_name).expect("keyname is invalid");
115    unsafe {
116        let ret: i32 = AddKey(
117            type_name.as_ptr(),
118            keyname.as_ptr(),
119            cert_data.as_ptr(),
120            cert_data.len(),
121            key_id,
122        );
123        ret
124    }
125}
126
127fn enable_key_list(key_id: KeySerial, certs: Vec<Vec<u8>>, key_name_prefix: &str) -> i32 {
128    let prefix = String::from(key_name_prefix);
129    for (i, cert_data) in certs.iter().enumerate() {
130        let key_name = prefix.clone() + &i.to_string();
131        let ret = enable_key(key_id, key_name.as_str(), cert_data);
132        if ret < 0 {
133            return ret;
134        }
135    }
136    SUCCESS
137}
138
139/// parse proc_key_file to get keyring id
140fn get_keyring_id() -> Result<KeySerial, ()> {
141    let file = File::open(PROC_KEY_FILE_PATH).expect("Open /proc/keys failed");
142    let lines = BufReader::new(file).lines();
143    for line in lines.flatten() {
144        if line.contains(KEYRING_TYPE) && line.contains(FSVERITY_KEYRING_NAME) {
145            if let Some(keyid) = parse_key_info(line) {
146                return Ok(keyid);
147            }
148        }
149    }
150    error!(LOG_LABEL, "Get .fs-verity keyring id failed.");
151    Err(())
152}
153
154// enable all trusted keys
155fn enable_trusted_keys(key_id: KeySerial, root_cert: &PemCollection) {
156    let certs = match root_cert.to_der() {
157        Ok(der) => der,
158        Err(e) => {
159            print_openssl_error_stack(e);
160            Vec::new()
161        }
162    };
163    if certs.is_empty() {
164        error!(LOG_LABEL, "empty trusted certs!");
165    }
166    let ret = enable_key_list(key_id, certs, CODE_SIGN_KEY_NAME_PREFIX);
167    if ret < 0 {
168        cs_hisysevent::report_add_key_err("code_sign_keys", ret);
169    }
170}
171
172fn check_and_add_cert_path(root_cert: &PemCollection, cert_paths: &TrustCertPath) -> bool {
173    if Path::new(PROFILE_STORE_EL1).exists() {
174        if add_profile_cert_path(root_cert, cert_paths).is_err() {
175            error!(LOG_LABEL, "Add cert path from local profile err.");
176        }
177        info!(LOG_LABEL, "Finished cert path adding.");
178        true
179    } else {
180        false
181    }
182}
183
184// start cert path ops thread add trusted cert & developer cert
185fn add_profile_cert_path_thread(
186    root_cert: PemCollection,
187    cert_paths: TrustCertPath,
188) -> std::thread::JoinHandle<()> {
189    thread::spawn(move || {
190        // enable developer certs
191        info!(LOG_LABEL, "Starting enable developer cert.");
192        let start_time = Instant::now();
193        loop {
194            if check_and_add_cert_path(&root_cert, &cert_paths) {
195                break;
196            } else if start_time.elapsed() >= Duration::from_secs(PROFILE_SEARCH_SLEEP_OUT_TIME) {
197                error!(LOG_LABEL, "Timeout while waiting for PROFILE_STORE_EL1.");
198                break;
199            } else {
200                thread::sleep(Duration::from_millis(PROFILE_SEARCH_SLEEP_TIME));
201            }
202        }
203    })
204}
205
206// enable local key from local code sign SA
207fn enable_local_key(key_id: KeySerial) {
208    if let Some(cert_data) = get_local_key() {
209        let ret = enable_key(key_id, LOCAL_KEY_NAME, &cert_data);
210        if ret < 0 {
211            cs_hisysevent::report_add_key_err("local_key", ret);
212            error!(LOG_LABEL, "Enable local key failed");
213        }
214    }
215}
216
217// restrict fs-verity keyring, don't allow to add more keys
218fn restrict_keys(key_id: KeySerial) {
219    unsafe {
220        if KeyctlRestrictKeyring(key_id, ptr::null(), ptr::null()) < 0 {
221            error!(LOG_LABEL, "Restrict keyring err");
222        }
223    }
224}
225
226fn enable_keys_after_user_unlock(key_id: KeySerial) {
227    if !unsafe { CheckUserUnlock() } {
228        restrict_keys(key_id);
229        return;
230    }
231
232    // enable local code sign key
233    enable_local_key(key_id);
234    restrict_keys(key_id);
235}
236
237/// enable trusted and local keys, and then restrict keyring
238pub fn enable_all_keys() {
239    let key_id = match get_keyring_id() {
240        Ok(id) => id,
241        Err(_) => {
242            error!(LOG_LABEL, "Failed to get keyring ID.");
243            return;
244        },
245    };
246    let root_cert = get_trusted_certs();
247    // enable device keys and authed source
248    enable_trusted_keys(key_id, &root_cert);
249
250    let cert_paths = get_cert_path();
251    // enable trusted cert in prebuilt config
252    if cert_paths.add_cert_paths().is_err() {
253        error!(LOG_LABEL, "Add trusted cert path err.");
254    }
255
256    let cert_thread = add_profile_cert_path_thread(root_cert, cert_paths);
257    enable_keys_after_user_unlock(key_id);
258
259    if let Err(e) = cert_thread.join() {
260        error!(LOG_LABEL, "add cert path thread panicked: {:?}", e);
261    }
262    info!(LOG_LABEL, "Fnished enable all keys.");
263}
264