blob: 10f853dd9274dd568994fd84af6a83647665377d [file] [log] [blame]
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! FIPS compliant random number conditioner. Reads from /dev/hw_random
//! and applies the NIST SP 800-90A CTR DRBG strategy to provide
//! pseudorandom bytes to clients which connect to a socket provided
//! by init.
mod conditioner;
mod cutils_socket;
mod drbg;
use std::{
convert::Infallible,
fs::remove_file,
io::ErrorKind,
os::unix::net::UnixListener,
path::{Path, PathBuf},
};
use anyhow::{ensure, Context, Result};
use log::{error, info, Level};
use nix::sys::signal;
use tokio::{io::AsyncWriteExt, net::UnixListener as TokioUnixListener};
use crate::conditioner::ConditionerBuilder;
//#[derive(Debug, clap::Parser)]
struct Cli {
//#[clap(long, default_value = "/dev/hw_random")]
source: PathBuf,
//#[clap(long)]
socket: Option<PathBuf>,
}
fn configure_logging() -> Result<()> {
ensure!(
logger::init(
logger::Config::default().with_tag_on_device("prng_seeder").with_min_level(Level::Info)
),
"log configuration failed"
);
Ok(())
}
fn get_socket(path: &Path) -> Result<UnixListener> {
if let Err(e) = remove_file(path) {
if e.kind() != ErrorKind::NotFound {
return Err(e).context(format!("Removing old socket: {}", path.display()));
}
} else {
info!("Deleted old {}", path.display());
}
UnixListener::bind(path)
.with_context(|| format!("In get_socket: binding socket to {}", path.display()))
}
fn setup() -> Result<(ConditionerBuilder, UnixListener)> {
configure_logging()?;
let cli = Cli { source: PathBuf::from("/dev/hw_random"), socket: None };
unsafe { signal::signal(signal::Signal::SIGPIPE, signal::SigHandler::SigIgn) }
.context("In setup, setting SIGPIPE to SIG_IGN")?;
let listener = match cli.socket {
Some(path) => get_socket(path.as_path())?,
None => cutils_socket::android_get_control_socket("prng_seeder")
.context("In setup, calling android_get_control_socket")?,
};
let hwrng = std::fs::File::open(&cli.source)
.with_context(|| format!("Unable to open hwrng {}", cli.source.display()))?;
let cb = ConditionerBuilder::new(hwrng)?;
Ok((cb, listener))
}
async fn listen_loop(cb: ConditionerBuilder, listener: UnixListener) -> Result<Infallible> {
let mut conditioner = cb.build();
listener.set_nonblocking(true).context("In listen_loop, on set_nonblocking")?;
let listener = TokioUnixListener::from_std(listener).context("In listen_loop, on from_std")?;
info!("Starting listen loop");
loop {
match listener.accept().await {
Ok((mut stream, _)) => {
let new_bytes = conditioner.request()?;
tokio::spawn(async move {
if let Err(e) = stream.write_all(&new_bytes).await {
error!("Request failed: {}", e);
}
});
conditioner.reseed_if_necessary().await?;
}
Err(e) if e.kind() == ErrorKind::Interrupted => {}
Err(e) => return Err(e).context("accept on socket failed"),
}
}
}
fn run() -> Result<Infallible> {
let (cb, listener) = match setup() {
Ok(t) => t,
Err(e) => {
// If setup fails, just hang forever. That way init doesn't respawn us.
error!("Hanging forever because setup failed: {:?}", e);
// Logs are sometimes mysteriously not being logged, so print too
println!("prng_seeder: Hanging forever because setup failed: {:?}", e);
loop {
std::thread::park();
error!("std::thread::park() finished unexpectedly, re-parking thread");
}
}
};
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("In run, building reactor")?
.block_on(async { listen_loop(cb, listener).await })
}
fn main() {
let e = run();
error!("Launch terminated: {:?}", e);
// Logs are sometimes mysteriously not being logged, so print too
println!("prng_seeder: launch terminated: {:?}", e);
std::process::exit(-1);
}