]> git.proxmox.com Git - cargo.git/blob - vendor/rand_os/src/solaris.rs
New upstream version 0.33.0
[cargo.git] / vendor / rand_os / src / solaris.rs
1 // Copyright 2018 Developers of the Rand project.
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 //! Implementation for the Solaris family
10 //!
11 //! Read from `/dev/random`, with chunks of limited size (1040 bytes).
12 //! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A.
13 //! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less
14 //! secure. We choose to read from `/dev/random`.
15 //!
16 //! Since Solaris 11.3 the `getrandom` syscall is available. To make sure we can
17 //! compile on both Solaris and on OpenSolaris derivatives, that do not have the
18 //! function, we do a direct syscall instead of calling a library function.
19 //!
20 //! We have no way to differentiate between Solaris, illumos, SmartOS, etc.
21 extern crate libc;
22
23 use rand_core::{Error, ErrorKind};
24 use super::random_device;
25 use super::OsRngImpl;
26
27 use std::io;
28 use std::io::Read;
29 use std::fs::{File, OpenOptions};
30 use std::os::unix::fs::OpenOptionsExt;
31 use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
32 use std::cmp;
33
34 #[derive(Clone, Debug)]
35 pub struct OsRng {
36 method: OsRngMethod,
37 initialized: bool,
38 }
39
40 #[derive(Clone, Debug)]
41 enum OsRngMethod {
42 GetRandom,
43 RandomDevice,
44 }
45
46 impl OsRngImpl for OsRng {
47 fn new() -> Result<OsRng, Error> {
48 if is_getrandom_available() {
49 return Ok(OsRng { method: OsRngMethod::GetRandom,
50 initialized: false });
51 }
52 let open = |p| OpenOptions::new()
53 .read(true)
54 .custom_flags(libc::O_NONBLOCK)
55 .open(p);
56 random_device::open("/dev/random", &open)?;
57 Ok(OsRng { method: OsRngMethod::RandomDevice, initialized: false })
58 }
59
60 fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> {
61 match self.method {
62 OsRngMethod::GetRandom => getrandom_try_fill(dest, false),
63 OsRngMethod::RandomDevice => random_device::read(dest),
64 }
65 }
66
67 fn test_initialized(&mut self, dest: &mut [u8], blocking: bool)
68 -> Result<usize, Error>
69 {
70 static OS_RNG_INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT;
71 if !self.initialized {
72 self.initialized = OS_RNG_INITIALIZED.load(Ordering::Relaxed);
73 }
74 if self.initialized { return Ok(0); }
75
76 let chunk_len = cmp::min(1024, dest.len());
77 let dest = &mut dest[..chunk_len];
78
79 match self.method {
80 OsRngMethod::GetRandom => getrandom_try_fill(dest, blocking)?,
81 OsRngMethod::RandomDevice => {
82 if blocking {
83 info!("OsRng: testing random device /dev/random");
84 // We already have a non-blocking handle, but now need a
85 // blocking one. Not much choice except opening it twice
86 let mut file = File::open("/dev/random")
87 .map_err(random_device::map_err)?;
88 file.read(dest).map_err(random_device::map_err)?;
89 } else {
90 self.fill_chunk(dest)?;
91 }
92 }
93 };
94 OS_RNG_INITIALIZED.store(true, Ordering::Relaxed);
95 self.initialized = true;
96 Ok(chunk_len)
97 }
98
99 fn max_chunk_size(&self) -> usize {
100 // The documentation says 1024 is the maximum for getrandom, but
101 // 1040 for /dev/random.
102 1024
103 }
104
105 fn method_str(&self) -> &'static str {
106 match self.method {
107 OsRngMethod::GetRandom => "getrandom",
108 OsRngMethod::RandomDevice => "/dev/random",
109 }
110 }
111 }
112
113 fn getrandom(buf: &mut [u8], blocking: bool) -> libc::c_long {
114 extern "C" {
115 fn syscall(number: libc::c_long, ...) -> libc::c_long;
116 }
117
118 const SYS_GETRANDOM: libc::c_long = 143;
119 const GRND_NONBLOCK: libc::c_uint = 0x0001;
120 const GRND_RANDOM: libc::c_uint = 0x0002;
121
122 unsafe {
123 syscall(SYS_GETRANDOM, buf.as_mut_ptr(), buf.len(),
124 if blocking { 0 } else { GRND_NONBLOCK } | GRND_RANDOM)
125 }
126 }
127
128 fn getrandom_try_fill(dest: &mut [u8], blocking: bool) -> Result<(), Error> {
129 let result = getrandom(dest, blocking);
130 if result == -1 || result == 0 {
131 let err = io::Error::last_os_error();
132 let kind = err.kind();
133 if kind == io::ErrorKind::WouldBlock {
134 return Err(Error::with_cause(
135 ErrorKind::NotReady,
136 "getrandom not ready",
137 err,
138 ));
139 } else {
140 return Err(Error::with_cause(
141 ErrorKind::Unavailable,
142 "unexpected getrandom error",
143 err,
144 ));
145 }
146 } else if result != dest.len() as i64 {
147 return Err(Error::new(ErrorKind::Unavailable,
148 "unexpected getrandom error"));
149 }
150 Ok(())
151 }
152
153 fn is_getrandom_available() -> bool {
154 use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
155 use std::sync::{Once, ONCE_INIT};
156
157 static CHECKER: Once = ONCE_INIT;
158 static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;
159
160 CHECKER.call_once(|| {
161 debug!("OsRng: testing getrandom");
162 let mut buf: [u8; 0] = [];
163 let result = getrandom(&mut buf, false);
164 let available = if result == -1 {
165 let err = io::Error::last_os_error().raw_os_error();
166 err != Some(libc::ENOSYS)
167 } else {
168 true
169 };
170 AVAILABLE.store(available, Ordering::Relaxed);
171 info!("OsRng: using {}", if available { "getrandom" } else { "/dev/random" });
172 });
173
174 AVAILABLE.load(Ordering::Relaxed)
175 }