blob: 3f698f6179f5dfbc6f25f20cd376e4ae92621be9 [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;
21mod cutils_socket;
22mod drbg;
23
24use std::{
25 convert::Infallible,
Paul Crowley021cf552022-09-28 18:37:43 +000026 fs::remove_file,
Paul Crowley9da969e2022-09-16 23:42:24 +000027 io::ErrorKind,
Paul Crowley021cf552022-09-28 18:37:43 +000028 os::unix::net::UnixListener,
Paul Crowley9da969e2022-09-16 23:42:24 +000029 path::{Path, PathBuf},
30};
31
Paul Crowley021cf552022-09-28 18:37:43 +000032use anyhow::{ensure, Context, Result};
Paul Crowley9da969e2022-09-16 23:42:24 +000033use clap::Parser;
Paul Crowley021cf552022-09-28 18:37:43 +000034use log::{error, info, Level};
35use nix::sys::signal;
Paul Crowley9da969e2022-09-16 23:42:24 +000036use tokio::{io::AsyncWriteExt, net::UnixListener as TokioUnixListener};
37
Paul Crowley021cf552022-09-28 18:37:43 +000038use crate::conditioner::ConditionerBuilder;
Paul Crowley9da969e2022-09-16 23:42:24 +000039
40#[derive(Debug, clap::Parser)]
41struct Cli {
42 #[clap(long, default_value = "/dev/hw_random")]
43 source: PathBuf,
44 #[clap(long)]
45 socket: Option<PathBuf>,
46}
47
Paul Crowley021cf552022-09-28 18:37:43 +000048fn configure_logging() -> Result<()> {
49 ensure!(
50 logger::init(
51 logger::Config::default().with_tag_on_device("prng_seeder").with_min_level(Level::Info)
52 ),
53 "log configuration failed"
54 );
55 Ok(())
Paul Crowley9da969e2022-09-16 23:42:24 +000056}
57
58fn get_socket(path: &Path) -> Result<UnixListener> {
59 if let Err(e) = remove_file(path) {
60 if e.kind() != ErrorKind::NotFound {
Paul Crowley021cf552022-09-28 18:37:43 +000061 return Err(e).context(format!("Removing old socket: {}", path.display()));
Paul Crowley9da969e2022-09-16 23:42:24 +000062 }
63 } else {
Paul Crowley021cf552022-09-28 18:37:43 +000064 info!("Deleted old {}", path.display());
Paul Crowley9da969e2022-09-16 23:42:24 +000065 }
Paul Crowley021cf552022-09-28 18:37:43 +000066 UnixListener::bind(path)
67 .with_context(|| format!("In get_socket: binding socket to {}", path.display()))
Paul Crowley9da969e2022-09-16 23:42:24 +000068}
69
Paul Crowley021cf552022-09-28 18:37:43 +000070fn setup() -> Result<(ConditionerBuilder, UnixListener)> {
71 configure_logging()?;
72 let cli = Cli::try_parse()?;
73 unsafe { signal::signal(signal::Signal::SIGPIPE, signal::SigHandler::SigIgn) }
74 .context("In setup, setting SIGPIPE to SIG_IGN")?;
75
76 let listener = match cli.socket {
77 Some(path) => get_socket(path.as_path())?,
78 None => cutils_socket::android_get_control_socket("prng_seeder")
79 .context("In setup, calling android_get_control_socket")?,
80 };
81 let hwrng = std::fs::File::open(&cli.source)
82 .with_context(|| format!("Unable to open hwrng {}", cli.source.display()))?;
83 let cb = ConditionerBuilder::new(hwrng)?;
84 Ok((cb, listener))
85}
86
87async fn listen_loop(cb: ConditionerBuilder, listener: UnixListener) -> Result<Infallible> {
88 let mut conditioner = cb.build();
89 listener.set_nonblocking(true).context("In listen_loop, on set_nonblocking")?;
90 let listener = TokioUnixListener::from_std(listener).context("In listen_loop, on from_std")?;
91 info!("Starting listen loop");
Paul Crowley9da969e2022-09-16 23:42:24 +000092 loop {
93 match listener.accept().await {
94 Ok((mut stream, _)) => {
95 let new_bytes = conditioner.request()?;
96 tokio::spawn(async move {
97 if let Err(e) = stream.write_all(&new_bytes).await {
98 error!("Request failed: {}", e);
99 }
100 });
101 conditioner.reseed_if_necessary().await?;
102 }
103 Err(e) if e.kind() == ErrorKind::Interrupted => {}
Paul Crowley021cf552022-09-28 18:37:43 +0000104 Err(e) => return Err(e).context("accept on socket failed"),
Paul Crowley9da969e2022-09-16 23:42:24 +0000105 }
106 }
107}
108
Paul Crowley021cf552022-09-28 18:37:43 +0000109fn run() -> Result<Infallible> {
110 let (cb, listener) = match setup() {
111 Ok(t) => t,
112 Err(e) => {
113 // If setup fails, just hang forever. That way init doesn't respawn us.
114 error!("Hanging forever because setup failed: {:?}", e);
115 // Logs are sometimes mysteriously not being logged, so print too
116 println!("prng_seeder: Hanging forever because setup failed: {:?}", e);
117 loop {
118 std::thread::park();
119 error!("std::thread::park() finished unexpectedly, re-parking thread");
120 }
121 }
Paul Crowley9da969e2022-09-16 23:42:24 +0000122 };
Paul Crowley9da969e2022-09-16 23:42:24 +0000123
124 tokio::runtime::Builder::new_current_thread()
125 .enable_all()
Paul Crowley021cf552022-09-28 18:37:43 +0000126 .build()
127 .context("In run, building reactor")?
128 .block_on(async { listen_loop(cb, listener).await })
Paul Crowley9da969e2022-09-16 23:42:24 +0000129}
130
131fn main() {
Paul Crowley021cf552022-09-28 18:37:43 +0000132 let e = run();
133 error!("Launch terminated: {:?}", e);
134 // Logs are sometimes mysteriously not being logged, so print too
135 println!("prng_seeder: launch terminated: {:?}", e);
Paul Crowley9da969e2022-09-16 23:42:24 +0000136 std::process::exit(-1);
137}