blob: c6adfd4decb8083731379f0b4ede82cd26d7954c [file] [log] [blame]
Paul Crowley9da969e2022-09-16 23:42:24 +00001// Copyright (C) 2022 The Android Open Source Project
2//
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//! FIPS compliant random number conditioner. Reads from /dev/hw_random
16//! and applies the NIST SP 800-90A CTR DRBG strategy to provide
17//! pseudorandom bytes to clients which connect to a socket provided
18//! by init.
19
20mod conditioner;
Paul Crowley9da969e2022-09-16 23:42:24 +000021mod drbg;
22
23use std::{
24 convert::Infallible,
Paul Crowley021cf552022-09-28 18:37:43 +000025 fs::remove_file,
Paul Crowley9da969e2022-09-16 23:42:24 +000026 io::ErrorKind,
Paul Crowley021cf552022-09-28 18:37:43 +000027 os::unix::net::UnixListener,
Paul Crowley9da969e2022-09-16 23:42:24 +000028 path::{Path, PathBuf},
29};
30
Paul Crowley021cf552022-09-28 18:37:43 +000031use anyhow::{ensure, Context, Result};
Paul Crowley9da969e2022-09-16 23:42:24 +000032use clap::Parser;
Jeff Vander Stoep940820c2024-01-31 10:55:29 +010033use log::{error, info, LevelFilter};
Paul Crowley021cf552022-09-28 18:37:43 +000034use nix::sys::signal;
Paul Crowley9da969e2022-09-16 23:42:24 +000035use tokio::{io::AsyncWriteExt, net::UnixListener as TokioUnixListener};
36
Paul Crowley021cf552022-09-28 18:37:43 +000037use crate::conditioner::ConditionerBuilder;
Paul Crowley9da969e2022-09-16 23:42:24 +000038
Andrew Walbran13335de2022-12-01 15:05:39 +000039#[derive(Debug, Parser)]
Paul Crowley9da969e2022-09-16 23:42:24 +000040struct Cli {
41 #[clap(long, default_value = "/dev/hw_random")]
42 source: PathBuf,
43 #[clap(long)]
44 socket: Option<PathBuf>,
45}
46
Paul Crowley021cf552022-09-28 18:37:43 +000047fn configure_logging() -> Result<()> {
48 ensure!(
49 logger::init(
Jeff Vander Stoep940820c2024-01-31 10:55:29 +010050 logger::Config::default()
51 .with_tag_on_device("prng_seeder")
52 .with_max_level(LevelFilter::Info)
Paul Crowley021cf552022-09-28 18:37:43 +000053 ),
54 "log configuration failed"
55 );
56 Ok(())
Paul Crowley9da969e2022-09-16 23:42:24 +000057}
58
59fn get_socket(path: &Path) -> Result<UnixListener> {
60 if let Err(e) = remove_file(path) {
61 if e.kind() != ErrorKind::NotFound {
Paul Crowley021cf552022-09-28 18:37:43 +000062 return Err(e).context(format!("Removing old socket: {}", path.display()));
Paul Crowley9da969e2022-09-16 23:42:24 +000063 }
64 } else {
Paul Crowley021cf552022-09-28 18:37:43 +000065 info!("Deleted old {}", path.display());
Paul Crowley9da969e2022-09-16 23:42:24 +000066 }
Paul Crowley021cf552022-09-28 18:37:43 +000067 UnixListener::bind(path)
68 .with_context(|| format!("In get_socket: binding socket to {}", path.display()))
Paul Crowley9da969e2022-09-16 23:42:24 +000069}
70
Paul Crowley021cf552022-09-28 18:37:43 +000071fn setup() -> Result<(ConditionerBuilder, UnixListener)> {
Jiyong Park89365ad2025-03-01 09:26:50 +090072 configure_logging()?;
73 let cli = Cli::try_parse()?;
Jiyong Parkc2284f42024-09-11 09:12:55 +090074 // SAFETY: nobody has taken ownership of the inherited FDs yet.
75 unsafe { rustutils::inherited_fd::init_once() }
76 .context("In setup, failed to own inherited FDs")?;
Andrew Walbranc7687332023-07-21 17:26:09 +010077 // SAFETY: Nothing else sets the signal handler, so either it was set here or it is the default.
Paul Crowley021cf552022-09-28 18:37:43 +000078 unsafe { signal::signal(signal::Signal::SIGPIPE, signal::SigHandler::SigIgn) }
79 .context("In setup, setting SIGPIPE to SIG_IGN")?;
80
81 let listener = match cli.socket {
82 Some(path) => get_socket(path.as_path())?,
Jiyong Parkc2284f42024-09-11 09:12:55 +090083 None => rustutils::sockets::android_get_control_socket("prng_seeder")
84 .context("In setup, calling android_get_control_socket")?
85 .into(),
Paul Crowley021cf552022-09-28 18:37:43 +000086 };
87 let hwrng = std::fs::File::open(&cli.source)
88 .with_context(|| format!("Unable to open hwrng {}", cli.source.display()))?;
89 let cb = ConditionerBuilder::new(hwrng)?;
90 Ok((cb, listener))
91}
92
93async fn listen_loop(cb: ConditionerBuilder, listener: UnixListener) -> Result<Infallible> {
94 let mut conditioner = cb.build();
95 listener.set_nonblocking(true).context("In listen_loop, on set_nonblocking")?;
96 let listener = TokioUnixListener::from_std(listener).context("In listen_loop, on from_std")?;
97 info!("Starting listen loop");
Paul Crowley9da969e2022-09-16 23:42:24 +000098 loop {
99 match listener.accept().await {
100 Ok((mut stream, _)) => {
101 let new_bytes = conditioner.request()?;
102 tokio::spawn(async move {
103 if let Err(e) = stream.write_all(&new_bytes).await {
104 error!("Request failed: {}", e);
105 }
106 });
107 conditioner.reseed_if_necessary().await?;
108 }
109 Err(e) if e.kind() == ErrorKind::Interrupted => {}
Paul Crowley021cf552022-09-28 18:37:43 +0000110 Err(e) => return Err(e).context("accept on socket failed"),
Paul Crowley9da969e2022-09-16 23:42:24 +0000111 }
112 }
113}
114
Paul Crowley021cf552022-09-28 18:37:43 +0000115fn run() -> Result<Infallible> {
116 let (cb, listener) = match setup() {
117 Ok(t) => t,
118 Err(e) => {
119 // If setup fails, just hang forever. That way init doesn't respawn us.
120 error!("Hanging forever because setup failed: {:?}", e);
121 // Logs are sometimes mysteriously not being logged, so print too
122 println!("prng_seeder: Hanging forever because setup failed: {:?}", e);
123 loop {
124 std::thread::park();
125 error!("std::thread::park() finished unexpectedly, re-parking thread");
126 }
127 }
Paul Crowley9da969e2022-09-16 23:42:24 +0000128 };
Paul Crowley9da969e2022-09-16 23:42:24 +0000129
130 tokio::runtime::Builder::new_current_thread()
131 .enable_all()
Paul Crowley021cf552022-09-28 18:37:43 +0000132 .build()
133 .context("In run, building reactor")?
134 .block_on(async { listen_loop(cb, listener).await })
Paul Crowley9da969e2022-09-16 23:42:24 +0000135}
136
137fn main() {
Paul Crowley021cf552022-09-28 18:37:43 +0000138 let e = run();
139 error!("Launch terminated: {:?}", e);
140 // Logs are sometimes mysteriously not being logged, so print too
141 println!("prng_seeder: launch terminated: {:?}", e);
Paul Crowley9da969e2022-09-16 23:42:24 +0000142 std::process::exit(-1);
143}
Andrew Walbran13335de2022-12-01 15:05:39 +0000144
145#[cfg(test)]
146mod tests {
147 use super::*;
148 use clap::CommandFactory;
149
150 #[test]
151 fn verify_cli() {
152 Cli::command().debug_assert();
153 }
154}