kanit

Toy init system
Log | Files | Refs | README | LICENSE

seed.rs (5470B)


      1 use std::cmp::Ordering;
      2 use std::mem::size_of;
      3 use std::os::unix::fs::PermissionsExt;
      4 
      5 use async_fs::File;
      6 use async_trait::async_trait;
      7 use blocking::unblock;
      8 use futures_lite::{AsyncReadExt, AsyncWriteExt};
      9 use log::info;
     10 use nix::errno::Errno;
     11 use nix::fcntl::{OFlag, open};
     12 use nix::request_code_write;
     13 use nix::sys::stat::Mode;
     14 use nix::sys::time::{TimeSpec, TimeValLike};
     15 use nix::time::{ClockId, clock_gettime};
     16 use nix::unistd::close;
     17 
     18 use kanit_common::constants;
     19 use kanit_common::error::{Context, ErrorKind, Result};
     20 use kanit_unit::{Dependencies, Unit};
     21 
     22 use crate::oneshot::{Clock, LocalMount};
     23 use crate::unit_name;
     24 
     25 pub struct Seed;
     26 
     27 const RND_IOC_MAGIC: u8 = b'R';
     28 const RND_IOC_TYPE_MODE: u8 = 0x03;
     29 const MAX_SEED_LEN: usize = 512;
     30 
     31 #[repr(C)]
     32 struct RandPoolInfo {
     33     entropy_count: libc::c_int,
     34     buf_size: libc::c_int,
     35     buf: [u8; MAX_SEED_LEN], // defined as u32 but it doesn't *really* matter
     36 }
     37 
     38 impl Seed {
     39     fn untrusted_seed() -> u64 {
     40         let seconds = clock_gettime(ClockId::CLOCK_REALTIME) // TODO; unblock?
     41             .unwrap_or_else(|_| {
     42                 clock_gettime(ClockId::CLOCK_BOOTTIME).unwrap_or(TimeSpec::new(0, 0))
     43             })
     44             .num_seconds();
     45 
     46         u64::from_be_bytes(seconds.to_be_bytes())
     47     }
     48 
     49     async fn trusted_seed() -> Option<u64> {
     50         if let Ok(mut data) = async_fs::read(constants::KAN_SEED).await {
     51             data.resize(8, 0);
     52 
     53             // since the vector was resized, this shouldn't fail
     54             Some(u64::from_be_bytes(data.as_slice().try_into().unwrap()))
     55         } else {
     56             None
     57         }
     58     }
     59 
     60     async fn write_bytes(bytes: &[u8], trusted: bool) -> Result<()> {
     61         let buf: [u8; MAX_SEED_LEN] = match bytes.len().cmp(&MAX_SEED_LEN) {
     62             Ordering::Greater => (&bytes[0..MAX_SEED_LEN]).try_into().unwrap(),
     63             Ordering::Less => {
     64                 let mut buf = [0u8; MAX_SEED_LEN];
     65 
     66                 let view = &mut buf[0..bytes.len()];
     67 
     68                 view.copy_from_slice(bytes);
     69 
     70                 buf
     71             }
     72             Ordering::Equal => bytes.try_into().unwrap(),
     73         };
     74 
     75         let entropy_count = if trusted { bytes.len() * 8 } else { 0 };
     76 
     77         let info = RandPoolInfo {
     78             entropy_count: entropy_count as libc::c_int,
     79             buf_size: bytes.len() as libc::c_int,
     80             buf, // unwrap: we ensure the size of `bytes`
     81         };
     82 
     83         unblock(move || {
     84             let rand_fd = open("/dev/urandom", OFlag::O_RDONLY, Mode::empty())
     85                 .context_kind("failed to open urandom", ErrorKind::Recoverable)?;
     86 
     87             unsafe {
     88                 Errno::result(libc::ioctl(
     89                     rand_fd,
     90                     request_code_write!(
     91                         RND_IOC_MAGIC,
     92                         RND_IOC_TYPE_MODE,
     93                         size_of::<libc::c_int>() * 2
     94                     ),
     95                     &info as *const RandPoolInfo,
     96                 ))
     97             }
     98             .context_kind("failed to add entropy", ErrorKind::Recoverable)?;
     99 
    100             close(rand_fd).context_kind("failed to close urandom", ErrorKind::Recoverable)?;
    101 
    102             Ok(())
    103         })
    104         .await
    105     }
    106 }
    107 
    108 #[async_trait]
    109 impl Unit for Seed {
    110     unit_name!("seed");
    111 
    112     fn dependencies(&self) -> Dependencies {
    113         Dependencies::new()
    114             .need(LocalMount.name())
    115             .after(Clock.name())
    116             .clone()
    117     }
    118 
    119     async fn start(&mut self) -> Result<()> {
    120         info!("seeding random number generator");
    121 
    122         let t_seed = Self::trusted_seed().await;
    123 
    124         let seed = t_seed.unwrap_or_else(Self::untrusted_seed);
    125 
    126         let mut rand = fastrand::Rng::with_seed(seed);
    127 
    128         let bytes = async_fs::read_to_string("/proc/sys/kernel/random/poolsize")
    129             .await
    130             .unwrap_or_else(|_| "2048".to_string())
    131             .parse::<usize>()
    132             .unwrap_or(2048)
    133             / 8;
    134 
    135         // preload some bytes
    136         let mut buf = Vec::with_capacity(bytes);
    137 
    138         rand.fill(buf.as_mut_slice());
    139 
    140         Self::write_bytes(buf.as_slice(), t_seed.is_some()).await?;
    141 
    142         // load previous seed
    143         if let Ok(mut previous_seed) = async_fs::read(constants::KAN_SEED).await {
    144             let len = previous_seed.len();
    145 
    146             previous_seed.resize(bytes, 0);
    147 
    148             if bytes > len {
    149                 rand.fill(&mut previous_seed[len..]);
    150             }
    151 
    152             Self::write_bytes(previous_seed.as_slice(), true).await?;
    153         }
    154 
    155         Ok(())
    156     }
    157 
    158     async fn stop(&mut self) -> Result<()> {
    159         info!("saving random seed");
    160 
    161         let mut f = File::open("/dev/urandom")
    162             .await
    163             .context_kind("failed to open urandom", ErrorKind::Recoverable)?;
    164 
    165         let mut buf = [0; 16];
    166 
    167         f.read_exact(&mut buf)
    168             .await
    169             .context_kind("failed to save random seed", ErrorKind::Recoverable)?;
    170 
    171         let mut file = File::create(constants::KAN_SEED)
    172             .await
    173             .context_kind("failed to open seed file", ErrorKind::Recoverable)?;
    174 
    175         let metadata = file
    176             .metadata()
    177             .await
    178             .context_kind("failed to get metadata", ErrorKind::Recoverable)?;
    179 
    180         metadata
    181             .permissions()
    182             .set_mode((Mode::S_IWUSR | Mode::S_IRUSR).bits());
    183 
    184         file.write(&buf)
    185             .await
    186             .context_kind("failed to write seed", ErrorKind::Recoverable)?;
    187 
    188         Ok(())
    189     }
    190 }