Replace plain TemplateStore with SecureTemplateStore in the auth service. Templates are now encrypted with AES-256-GCM via TPM2 when available, falling back to software encryption with PBKDF2 key derivation (600k iterations) otherwise. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
172 lines
5.6 KiB
Rust
172 lines
5.6 KiB
Rust
//! Linux Hello Daemon
|
|
//!
|
|
//! Main daemon process for face authentication. Handles camera capture,
|
|
//! face detection, anti-spoofing checks, and template matching.
|
|
//!
|
|
//! The daemon provides two communication interfaces:
|
|
//! - IPC (Unix socket): For PAM module authentication
|
|
//! - D-Bus: For desktop applications and system integration
|
|
|
|
mod camera;
|
|
mod detection;
|
|
|
|
use linux_hello_common::{Config, Result, TemplateStore};
|
|
use linux_hello_daemon::auth::AuthService;
|
|
use linux_hello_daemon::dbus_server::{check_system_bus_available, DbusServer};
|
|
use linux_hello_daemon::ipc::IpcServer;
|
|
use linux_hello_daemon::secure_template_store::SecureTemplateStore;
|
|
use tracing::{error, info, warn, Level};
|
|
use tracing_subscriber::FmtSubscriber;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
// Initialize logging
|
|
let _subscriber = FmtSubscriber::builder()
|
|
.with_max_level(Level::INFO)
|
|
.with_target(false)
|
|
.init();
|
|
|
|
info!("Linux Hello Daemon starting...");
|
|
|
|
// Load configuration
|
|
let config = Config::load_or_default();
|
|
info!("Configuration loaded");
|
|
info!(" Camera device: {}", config.camera.device);
|
|
info!(" Detection model: {}", config.detection.model);
|
|
info!(" Anti-spoofing enabled: {}", config.anti_spoofing.enabled);
|
|
|
|
// Initialize camera
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
match camera::enumerate_cameras() {
|
|
Ok(cameras) => {
|
|
info!("Found {} camera(s)", cameras.len());
|
|
for cam in &cameras {
|
|
info!(" - {}", cam);
|
|
}
|
|
}
|
|
Err(e) => {
|
|
warn!("Camera enumeration failed: {}", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(not(target_os = "linux"))]
|
|
{
|
|
info!("Camera functions not available on this platform");
|
|
info!("V4L2 camera support requires Linux");
|
|
}
|
|
|
|
// Initialize face detection
|
|
info!("Face detection module initialized (placeholder)");
|
|
|
|
// Initialize authentication service
|
|
let template_path = std::env::var("LINUX_HELLO_TEMPLATES")
|
|
.map(std::path::PathBuf::from)
|
|
.unwrap_or_else(|_| SecureTemplateStore::default_path());
|
|
|
|
let auth_service = AuthService::with_paths(config.clone(), template_path.clone());
|
|
auth_service.initialize()?;
|
|
info!("Authentication service initialized (Templates: {})", template_path.display());
|
|
|
|
// Start IPC server
|
|
let socket_path = std::env::var("LINUX_HELLO_SOCKET")
|
|
.map(std::path::PathBuf::from)
|
|
.unwrap_or_else(|_| IpcServer::default_socket_path());
|
|
|
|
let mut ipc_server = IpcServer::new(socket_path.clone());
|
|
|
|
// Set authentication handler
|
|
let auth_service_for_auth = auth_service.clone();
|
|
ipc_server.set_auth_handler(move |user| {
|
|
let auth_service = auth_service_for_auth.clone();
|
|
async move { auth_service.authenticate(&user).await }
|
|
});
|
|
|
|
// Set enrollment handler
|
|
let auth_service_for_enroll = auth_service.clone();
|
|
ipc_server.set_enroll_handler(move |user, label, frame_count| {
|
|
let auth_service = auth_service_for_enroll.clone();
|
|
async move { auth_service.enroll(&user, &label, frame_count).await }
|
|
});
|
|
|
|
// Set list handler
|
|
let template_path_for_list = template_path.clone();
|
|
ipc_server.set_list_handler(move |user| {
|
|
let template_path = template_path_for_list.clone();
|
|
async move {
|
|
let store = TemplateStore::new(template_path);
|
|
store.list_templates(&user)
|
|
}
|
|
});
|
|
|
|
// Set remove handler
|
|
let template_path_for_remove = template_path.clone();
|
|
ipc_server.set_remove_handler(move |user, label, all| {
|
|
let template_path = template_path_for_remove.clone();
|
|
async move {
|
|
let store = TemplateStore::new(template_path);
|
|
if all {
|
|
store.remove_all(&user)
|
|
} else if let Some(l) = label {
|
|
store.remove(&user, &l)
|
|
} else {
|
|
Err(linux_hello_common::Error::Config(
|
|
"No label specified".to_string(),
|
|
))
|
|
}
|
|
}
|
|
});
|
|
|
|
// Initialize D-Bus server (optional - will fail gracefully if system bus unavailable)
|
|
let dbus_enabled = check_system_bus_available().await;
|
|
let mut dbus_server = DbusServer::new();
|
|
|
|
if dbus_enabled {
|
|
match dbus_server
|
|
.start(auth_service.clone(), config.clone())
|
|
.await
|
|
{
|
|
Ok(()) => {
|
|
info!("D-Bus server started successfully");
|
|
info!(" Service: org.linuxhello.Daemon");
|
|
info!(" Object path: /org/linuxhello/Manager");
|
|
}
|
|
Err(e) => {
|
|
warn!("Failed to start D-Bus server: {}", e);
|
|
warn!("D-Bus interface will not be available");
|
|
}
|
|
}
|
|
} else {
|
|
info!("System D-Bus not available, skipping D-Bus server");
|
|
}
|
|
|
|
info!("Linux Hello Daemon ready");
|
|
info!("Listening for authentication requests...");
|
|
if dbus_enabled && dbus_server.is_connected() {
|
|
info!(" - IPC: {}", socket_path.display());
|
|
info!(" - D-Bus: org.linuxhello.Daemon");
|
|
} else {
|
|
info!(" - IPC: {}", socket_path.display());
|
|
}
|
|
|
|
// Start IPC server as a task
|
|
let ipc_future = ipc_server.start();
|
|
|
|
// Wait for shutdown signal or server error
|
|
// Both IPC and D-Bus run concurrently using tokio
|
|
tokio::select! {
|
|
_ = tokio::signal::ctrl_c() => {
|
|
info!("Shutdown signal received");
|
|
}
|
|
result = ipc_future => {
|
|
if let Err(e) = result {
|
|
error!("IPC server error: {}", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
info!("Linux Hello Daemon shutting down");
|
|
Ok(())
|
|
}
|