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