use std::{
fs,
os::unix::fs::MetadataExt,
sync::{Arc, LazyLock},
};
use dashmap::DashMap;
use log::{debug, warn};
use teec_protocol::{CaAuthInfo, path_to_uuid};
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct CacheKey {
pid: i32,
ca_file_id: CaFileId,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct CaFileId {
inode: u64, mtime: i64, mtime_nsec: i64, dev: u64, }
fn get_ca_file_id(path: &str) -> Option<CaFileId> {
match std::fs::metadata(path) {
Ok(metadata) => Some(CaFileId {
inode: metadata.ino(),
mtime: metadata.mtime(),
mtime_nsec: metadata.mtime_nsec(),
dev: metadata.dev(),
}),
Err(_) => None,
}
}
static CA_AUTH_CACHE: LazyLock<DashMap<CacheKey, Arc<CaAuthInfo>>> = LazyLock::new(DashMap::new);
pub fn get_or_verify_ca() -> CaAuthInfo {
let pid = unsafe { libc::getpid() };
let ca_path = match std::fs::read_link("/proc/self/exe") {
Ok(path) => path.to_string_lossy().to_string(),
Err(_) => "<unknown>".to_string(),
};
let ca_uuid = path_to_uuid(&ca_path);
let ca_file_id = match get_ca_file_id(&ca_path) {
Some(id) => id,
None => {
warn!("无法获取CA文件元数据: {}", ca_path);
return perform_ca_auth_internal(&ca_uuid);
}
};
let key = CacheKey {
pid,
ca_file_id: ca_file_id.clone(),
};
if let Some(result) = CA_AUTH_CACHE.get(&key) {
debug!("CA认证缓存命中: pid={}, inode={}", pid, ca_file_id.inode);
return (**result.value()).clone();
}
debug!("CA认证缓存未命中,执行验签: pid={}, path={}", pid, ca_path);
let result = perform_ca_auth_internal(&ca_uuid);
CA_AUTH_CACHE.insert(key, Arc::new(result.clone()));
result
}
pub fn clear_cache() {
CA_AUTH_CACHE.clear();
}
fn perform_ca_auth_internal(ca_uuid: &str) -> CaAuthInfo {
let caller_path = match fs::read_link("/proc/self/exe") {
Ok(path) => path,
Err(e) => {
warn!("无法获取调用者可执行文件路径: {}", e);
return CaAuthInfo {
ca_uuid: ca_uuid.to_string(),
verified: false,
};
}
};
let path_str = caller_path.to_string_lossy().to_string();
debug!("开始验证 CA ELF 签名: {}", path_str);
let elf_data = match fs::read(&caller_path) {
Ok(data) => data,
Err(e) => {
warn!("无法读取ELF文件: {}", e);
return CaAuthInfo {
ca_uuid: ca_uuid.to_string(),
verified: false,
};
}
};
match tasign::verify_elf_signature(&elf_data, None) {
Ok(_) => {
debug!("CA ELF 签名验证成功");
CaAuthInfo {
ca_uuid: ca_uuid.to_string(),
verified: true,
}
}
Err(e) => {
warn!("签名验证失败: {}", e);
CaAuthInfo {
ca_uuid: ca_uuid.to_string(),
verified: false,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ca_file_id_uniqueness() {
let id1 = CaFileId {
inode: 12345,
mtime: 1000,
mtime_nsec: 0,
dev: 1,
};
let id2 = CaFileId {
inode: 12345,
mtime: 1000,
mtime_nsec: 0,
dev: 1,
};
let id3 = CaFileId {
inode: 12345,
mtime: 2000, mtime_nsec: 0,
dev: 1,
};
assert_eq!(id1, id2);
assert_ne!(id1, id3);
}
#[test]
fn test_get_ca_file_id_for_current_exe() {
let exe_path = "/proc/self/exe";
if let Ok(real_path) = fs::read_link(exe_path) {
let path_str = real_path.to_string_lossy();
let file_id = get_ca_file_id(&path_str);
assert!(file_id.is_some());
let id = file_id.unwrap();
assert!(id.inode > 0);
assert!(id.dev > 0);
}
}
#[test]
fn test_clear_cache() {
let test_key = CacheKey {
pid: 99999, ca_file_id: CaFileId {
inode: 12345,
mtime: 1000,
mtime_nsec: 0,
dev: 67890,
},
};
let test_info = Arc::new(CaAuthInfo {
ca_uuid: "test-uuid".to_string(),
verified: true,
});
CA_AUTH_CACHE.insert(test_key.clone(), test_info);
assert!(CA_AUTH_CACHE.contains_key(&test_key));
assert_eq!(CA_AUTH_CACHE.len(), 1);
clear_cache();
assert!(!CA_AUTH_CACHE.contains_key(&test_key));
assert_eq!(CA_AUTH_CACHE.len(), 0);
}
}