xref: /aosp_15_r20/system/security/prng_seeder/src/main.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
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 drbg;
22 
23 use std::{
24     convert::Infallible,
25     fs::remove_file,
26     io::ErrorKind,
27     os::unix::net::UnixListener,
28     path::{Path, PathBuf},
29 };
30 
31 use anyhow::{ensure, Context, Result};
32 use clap::Parser;
33 use log::{error, info, LevelFilter};
34 use nix::sys::signal;
35 use tokio::{io::AsyncWriteExt, net::UnixListener as TokioUnixListener};
36 
37 use crate::conditioner::ConditionerBuilder;
38 
39 #[derive(Debug, 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()
51                 .with_tag_on_device("prng_seeder")
52                 .with_max_level(LevelFilter::Info)
53         ),
54         "log configuration failed"
55     );
56     Ok(())
57 }
58 
get_socket(path: &Path) -> Result<UnixListener>59 fn get_socket(path: &Path) -> Result<UnixListener> {
60     if let Err(e) = remove_file(path) {
61         if e.kind() != ErrorKind::NotFound {
62             return Err(e).context(format!("Removing old socket: {}", path.display()));
63         }
64     } else {
65         info!("Deleted old {}", path.display());
66     }
67     UnixListener::bind(path)
68         .with_context(|| format!("In get_socket: binding socket to {}", path.display()))
69 }
70 
setup() -> Result<(ConditionerBuilder, UnixListener)>71 fn setup() -> Result<(ConditionerBuilder, UnixListener)> {
72     // SAFETY: nobody has taken ownership of the inherited FDs yet.
73     unsafe { rustutils::inherited_fd::init_once() }
74         .context("In setup, failed to own inherited FDs")?;
75     configure_logging()?;
76     let cli = Cli::try_parse()?;
77     // SAFETY: Nothing else sets the signal handler, so either it was set here or it is the default.
78     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())?,
83         None => rustutils::sockets::android_get_control_socket("prng_seeder")
84             .context("In setup, calling android_get_control_socket")?
85             .into(),
86     };
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 
listen_loop(cb: ConditionerBuilder, listener: UnixListener) -> Result<Infallible>93 async 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");
98     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 => {}
110             Err(e) => return Err(e).context("accept on socket failed"),
111         }
112     }
113 }
114 
run() -> Result<Infallible>115 fn 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         }
128     };
129 
130     tokio::runtime::Builder::new_current_thread()
131         .enable_all()
132         .build()
133         .context("In run, building reactor")?
134         .block_on(async { listen_loop(cb, listener).await })
135 }
136 
main()137 fn main() {
138     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);
142     std::process::exit(-1);
143 }
144 
145 #[cfg(test)]
146 mod tests {
147     use super::*;
148     use clap::CommandFactory;
149 
150     #[test]
verify_cli()151     fn verify_cli() {
152         Cli::command().debug_assert();
153     }
154 }
155