1use std::{
36 fs,
37 os::unix::fs::MetadataExt,
38 sync::{Arc, LazyLock},
39};
40
41use dashmap::DashMap;
42use log::{debug, warn};
43use teec_protocol::{CaAuthInfo, path_to_uuid};
44
45#[derive(Debug, Clone, Hash, PartialEq, Eq)]
47struct CacheKey {
48 pid: i32,
49 ca_file_id: CaFileId,
50}
51
52#[derive(Debug, Clone, Hash, PartialEq, Eq)]
54struct CaFileId {
55 inode: u64, mtime: i64, mtime_nsec: i64, dev: u64, }
60
61fn get_ca_file_id(path: &str) -> Option<CaFileId> {
63 match std::fs::metadata(path) {
64 Ok(metadata) => Some(CaFileId {
65 inode: metadata.ino(),
66 mtime: metadata.mtime(),
67 mtime_nsec: metadata.mtime_nsec(),
68 dev: metadata.dev(),
69 }),
70 Err(_) => None,
71 }
72}
73
74static CA_AUTH_CACHE: LazyLock<DashMap<CacheKey, Arc<CaAuthInfo>>> = LazyLock::new(DashMap::new);
77
78pub fn get_or_verify_ca() -> CaAuthInfo {
93 let pid = unsafe { libc::getpid() };
96
97 let ca_path = match std::fs::read_link("/proc/self/exe") {
99 Ok(path) => path.to_string_lossy().to_string(),
100 Err(_) => "<unknown>".to_string(),
101 };
102
103 let ca_uuid = path_to_uuid(&ca_path);
105
106 let ca_file_id = match get_ca_file_id(&ca_path) {
108 Some(id) => id,
109 None => {
110 warn!("无法获取CA文件元数据: {}", ca_path);
112 return perform_ca_auth_internal(&ca_uuid);
113 }
114 };
115
116 let key = CacheKey {
118 pid,
119 ca_file_id: ca_file_id.clone(),
120 };
121
122 if let Some(result) = CA_AUTH_CACHE.get(&key) {
124 debug!("CA认证缓存命中: pid={}, inode={}", pid, ca_file_id.inode);
125 return (**result.value()).clone();
127 }
128
129 debug!("CA认证缓存未命中,执行验签: pid={}, path={}", pid, ca_path);
131 let result = perform_ca_auth_internal(&ca_uuid);
132
133 CA_AUTH_CACHE.insert(key, Arc::new(result.clone()));
135
136 result
137}
138
139pub fn clear_cache() {
141 CA_AUTH_CACHE.clear();
142}
143
144fn perform_ca_auth_internal(ca_uuid: &str) -> CaAuthInfo {
146 let caller_path = match fs::read_link("/proc/self/exe") {
147 Ok(path) => path,
148 Err(e) => {
149 warn!("无法获取调用者可执行文件路径: {}", e);
150 return CaAuthInfo {
151 ca_uuid: ca_uuid.to_string(),
152 verified: false,
153 };
154 }
155 };
156
157 let path_str = caller_path.to_string_lossy().to_string();
158
159 debug!("开始验证 CA ELF 签名: {}", path_str);
160
161 let elf_data = match fs::read(&caller_path) {
162 Ok(data) => data,
163 Err(e) => {
164 warn!("无法读取ELF文件: {}", e);
165 return CaAuthInfo {
166 ca_uuid: ca_uuid.to_string(),
167 verified: false,
168 };
169 }
170 };
171
172 match tasign::verify_elf_signature(&elf_data, None) {
173 Ok(_) => {
174 debug!("CA ELF 签名验证成功");
175 CaAuthInfo {
176 ca_uuid: ca_uuid.to_string(),
177 verified: true,
178 }
179 }
180 Err(e) => {
181 warn!("签名验证失败: {}", e);
182 CaAuthInfo {
183 ca_uuid: ca_uuid.to_string(),
184 verified: false,
185 }
186 }
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
197 fn test_ca_file_id_uniqueness() {
198 let id1 = CaFileId {
200 inode: 12345,
201 mtime: 1000,
202 mtime_nsec: 0,
203 dev: 1,
204 };
205
206 let id2 = CaFileId {
207 inode: 12345,
208 mtime: 1000,
209 mtime_nsec: 0,
210 dev: 1,
211 };
212
213 let id3 = CaFileId {
214 inode: 12345,
215 mtime: 2000, mtime_nsec: 0,
217 dev: 1,
218 };
219
220 assert_eq!(id1, id2);
221 assert_ne!(id1, id3);
222 }
223
224 #[test]
225 fn test_get_ca_file_id_for_current_exe() {
226 let exe_path = "/proc/self/exe";
228 if let Ok(real_path) = fs::read_link(exe_path) {
229 let path_str = real_path.to_string_lossy();
230 let file_id = get_ca_file_id(&path_str);
231 assert!(file_id.is_some());
232
233 let id = file_id.unwrap();
234 assert!(id.inode > 0);
235 assert!(id.dev > 0);
236 }
237 }
238
239 #[test]
240 fn test_clear_cache() {
241 let test_key = CacheKey {
245 pid: 99999, ca_file_id: CaFileId {
247 inode: 12345,
248 mtime: 1000,
249 mtime_nsec: 0,
250 dev: 67890,
251 },
252 };
253
254 let test_info = Arc::new(CaAuthInfo {
255 ca_uuid: "test-uuid".to_string(),
256 verified: true,
257 });
258
259 CA_AUTH_CACHE.insert(test_key.clone(), test_info);
260
261 assert!(CA_AUTH_CACHE.contains_key(&test_key));
263 assert_eq!(CA_AUTH_CACHE.len(), 1);
264
265 clear_cache();
267
268 assert!(!CA_AUTH_CACHE.contains_key(&test_key));
270 assert_eq!(CA_AUTH_CACHE.len(), 0);
271 }
272}