comparison dbrandom.c @ 1698:f966834f0f9c

Use Linux getrandom() to ensure random device is initialised Remove old code warning about random device being not ready, /dev/random isn't used by default anyway.
author Matt Johnston <matt@ucc.asn.au>
date Thu, 28 May 2020 22:50:41 +0800
parents 60fceff95858
children 6e5037ae2c1c
comparison
equal deleted inserted replaced
1697:789466c5956b 1698:f966834f0f9c
47 * It is important to ensure that counter doesn't wrap around before we 47 * It is important to ensure that counter doesn't wrap around before we
48 * feed in new entropy. 48 * feed in new entropy.
49 * 49 *
50 */ 50 */
51 51
52 /* Pass len=0 to hash an entire file */ 52 /* Pass wantlen=0 to hash an entire file */
53 static int 53 static int
54 process_file(hash_state *hs, const char *filename, 54 process_file(hash_state *hs, const char *filename,
55 unsigned int len, int prngd) 55 unsigned int wantlen, int prngd) {
56 {
57 static int already_blocked = 0;
58 int readfd; 56 int readfd;
59 unsigned int readcount; 57 unsigned int readcount;
60 int ret = DROPBEAR_FAILURE; 58 int ret = DROPBEAR_FAILURE;
61 59
60 if (prngd) {
62 #if DROPBEAR_USE_PRNGD 61 #if DROPBEAR_USE_PRNGD
63 if (prngd)
64 {
65 readfd = connect_unix(filename); 62 readfd = connect_unix(filename);
66 } 63 #endif
67 else 64 } else {
68 #endif
69 {
70 readfd = open(filename, O_RDONLY); 65 readfd = open(filename, O_RDONLY);
71 } 66 }
72 67
73 if (readfd < 0) { 68 if (readfd < 0) {
74 goto out; 69 goto out;
75 } 70 }
76 71
77 readcount = 0; 72 readcount = 0;
78 while (len == 0 || readcount < len) 73 while (wantlen == 0 || readcount < wantlen) {
79 {
80 int readlen, wantread; 74 int readlen, wantread;
81 unsigned char readbuf[4096]; 75 unsigned char readbuf[4096];
82 if (!already_blocked && !prngd) 76 if (wantlen == 0) {
83 {
84 int res;
85 struct timeval timeout;
86 fd_set read_fds;
87
88 timeout.tv_sec = 2;
89 timeout.tv_usec = 0;
90
91 DROPBEAR_FD_ZERO(&read_fds);
92 FD_SET(readfd, &read_fds);
93 res = select(readfd + 1, &read_fds, NULL, NULL, &timeout);
94 if (res == 0)
95 {
96 dropbear_log(LOG_WARNING, "Warning: Reading the randomness source '%s' seems to have blocked.\nYou may need to find a better entropy source.", filename);
97 already_blocked = 1;
98 }
99 }
100
101 if (len == 0)
102 {
103 wantread = sizeof(readbuf); 77 wantread = sizeof(readbuf);
104 } 78 } else {
105 else 79 wantread = MIN(sizeof(readbuf), wantlen-readcount);
106 {
107 wantread = MIN(sizeof(readbuf), len-readcount);
108 } 80 }
109 81
110 #if DROPBEAR_USE_PRNGD 82 #if DROPBEAR_USE_PRNGD
111 if (prngd) 83 if (prngd) {
112 {
113 char egdcmd[2]; 84 char egdcmd[2];
114 egdcmd[0] = 0x02; /* blocking read */ 85 egdcmd[0] = 0x02; /* blocking read */
115 egdcmd[1] = (unsigned char)wantread; 86 egdcmd[1] = (unsigned char)wantread;
116 if (write(readfd, egdcmd, 2) < 0) 87 if (write(readfd, egdcmd, 2) < 0) {
117 {
118 dropbear_exit("Can't send command to egd"); 88 dropbear_exit("Can't send command to egd");
119 } 89 }
120 } 90 }
121 #endif 91 #endif
122
123 readlen = read(readfd, readbuf, wantread); 92 readlen = read(readfd, readbuf, wantread);
124 if (readlen <= 0) { 93 if (readlen <= 0) {
125 if (readlen < 0 && errno == EINTR) { 94 if (readlen < 0 && errno == EINTR) {
126 continue; 95 continue;
127 } 96 }
128 if (readlen == 0 && len == 0) 97 if (readlen == 0 && wantlen == 0) {
129 {
130 /* whole file was read as requested */ 98 /* whole file was read as requested */
131 break; 99 break;
132 } 100 }
133 goto out; 101 goto out;
134 } 102 }
191 counter = 0; 159 counter = 0;
192 donerandinit = 1; 160 donerandinit = 1;
193 } 161 }
194 #endif 162 #endif
195 163
164
165 #ifdef HAVE_GETRANDOM
166 /* Reads entropy seed with getrandom().
167 * May block if the kernel isn't ready.
168 * Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
169 static int process_getrandom(hash_state *hs) {
170 char buf[INIT_SEED_SIZE];
171 ssize_t ret;
172
173 /* First try non-blocking so that we can warn about waiting */
174 ret = getrandom(buf, sizeof(buf), GRND_NONBLOCK);
175 if (ret == -1) {
176 if (errno == ENOSYS) {
177 /* Old kernel */
178 return DROPBEAR_FAILURE;
179 }
180 /* Other errors fall through to blocking getrandom() */
181 TRACE(("first getrandom() failed: %d %s", errno, strerror(errno)))
182 if (errno == EAGAIN) {
183 dropbear_log(LOG_WARNING, "Waiting for kernel randomness to be initialised...");
184 }
185 }
186
187 /* Wait blocking if needed. Loop in case we get EINTR */
188 while (ret != sizeof(buf)) {
189 ret = getrandom(buf, sizeof(buf), 0);
190
191 if (ret == sizeof(buf)) {
192 /* Success */
193 break;
194 }
195 if (ret == -1 && errno == EINTR) {
196 /* Try again. */
197 continue;
198 }
199 if (ret >= 0) {
200 TRACE(("Short read %zd from getrandom() shouldn't happen", ret))
201 /* Try again? */
202 continue;
203 }
204
205 /* Unexpected problem, fall back to /dev/urandom */
206 TRACE(("2nd getrandom() failed: %d %s", errno, strerror(errno)))
207 break;
208 }
209
210 if (ret == sizeof(buf)) {
211 /* Success, stir in the entropy */
212 sha1_process(hs, (void*)buf, sizeof(buf));
213 return DROPBEAR_SUCCESS;
214 }
215
216 return DROPBEAR_FAILURE;
217
218 }
219 #endif /* HAVE_GETRANDOM */
220
196 /* Initialise the prng from /dev/urandom or prngd. This function can 221 /* Initialise the prng from /dev/urandom or prngd. This function can
197 * be called multiple times */ 222 * be called multiple times */
198 void seedrandom() { 223 void seedrandom() {
199 224
200 hash_state hs; 225 hash_state hs;
201 226
202 pid_t pid; 227 pid_t pid;
203 struct timeval tv; 228 struct timeval tv;
204 clock_t clockval; 229 clock_t clockval;
230 int urandom_seeded = 0;
205 231
206 #if DROPBEAR_FUZZ 232 #if DROPBEAR_FUZZ
207 if (fuzz.fuzzing) { 233 if (fuzz.fuzzing) {
208 return; 234 return;
209 } 235 }
213 sha1_init(&hs); 239 sha1_init(&hs);
214 240
215 /* existing state */ 241 /* existing state */
216 sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); 242 sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
217 243
244 #ifdef HAVE_GETRANDOM
245 if (process_getrandom(&hs) == DROPBEAR_SUCCESS) {
246 urandom_seeded = 1;
247 }
248 #endif
249
250 if (!urandom_seeded) {
218 #if DROPBEAR_USE_PRNGD 251 #if DROPBEAR_USE_PRNGD
219 if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1) 252 if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1)
220 != DROPBEAR_SUCCESS) { 253 != DROPBEAR_SUCCESS) {
221 dropbear_exit("Failure reading random device %s", 254 dropbear_exit("Failure reading random device %s",
222 DROPBEAR_PRNGD_SOCKET); 255 DROPBEAR_PRNGD_SOCKET);
223 } 256 urandom_seeded = 1;
257 }
224 #else 258 #else
225 /* non-blocking random source (probably /dev/urandom) */ 259 /* non-blocking random source (probably /dev/urandom) */
226 if (process_file(&hs, DROPBEAR_URANDOM_DEV, INIT_SEED_SIZE, 0) 260 if (process_file(&hs, DROPBEAR_URANDOM_DEV, INIT_SEED_SIZE, 0)
227 != DROPBEAR_SUCCESS) { 261 != DROPBEAR_SUCCESS) {
228 dropbear_exit("Failure reading random device %s", 262 dropbear_exit("Failure reading random device %s",
229 DROPBEAR_URANDOM_DEV); 263 DROPBEAR_URANDOM_DEV);
230 } 264 urandom_seeded = 1;
231 #endif 265 }
266 #endif
267 } /* urandom_seeded */
232 268
233 /* A few other sources to fall back on. 269 /* A few other sources to fall back on.
234 * Add more here for other platforms */ 270 * Add more here for other platforms */
235 #ifdef __linux__ 271 #ifdef __linux__
236 /* Seems to be a reasonable source of entropy from timers. Possibly hard 272 /* Seems to be a reasonable source of entropy from timers. Possibly hard