|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include <stdio.h> |
|
6 #include <string.h> |
|
7 #include <signal.h> |
|
8 #include <unistd.h> |
|
9 #include <limits.h> |
|
10 #include <errno.h> |
|
11 #include <stdlib.h> |
|
12 #include <sys/time.h> |
|
13 #include <sys/wait.h> |
|
14 #include <sys/stat.h> |
|
15 #include "secrng.h" |
|
16 #include "secerr.h" |
|
17 #include "prerror.h" |
|
18 #include "prthread.h" |
|
19 #include "prprf.h" |
|
20 |
|
21 size_t RNG_FileUpdate(const char *fileName, size_t limit); |
|
22 |
|
23 /* |
|
24 * When copying data to the buffer we want the least signicant bytes |
|
25 * from the input since those bits are changing the fastest. The address |
|
26 * of least significant byte depends upon whether we are running on |
|
27 * a big-endian or little-endian machine. |
|
28 * |
|
29 * Does this mean the least signicant bytes are the most significant |
|
30 * to us? :-) |
|
31 */ |
|
32 |
|
33 static size_t CopyLowBits(void *dst, size_t dstlen, void *src, size_t srclen) |
|
34 { |
|
35 union endianness { |
|
36 PRInt32 i; |
|
37 char c[4]; |
|
38 } u; |
|
39 |
|
40 if (srclen <= dstlen) { |
|
41 memcpy(dst, src, srclen); |
|
42 return srclen; |
|
43 } |
|
44 u.i = 0x01020304; |
|
45 if (u.c[0] == 0x01) { |
|
46 /* big-endian case */ |
|
47 memcpy(dst, (char*)src + (srclen - dstlen), dstlen); |
|
48 } else { |
|
49 /* little-endian case */ |
|
50 memcpy(dst, src, dstlen); |
|
51 } |
|
52 return dstlen; |
|
53 } |
|
54 |
|
55 #ifdef SOLARIS |
|
56 |
|
57 #include <kstat.h> |
|
58 |
|
59 static const PRUint32 entropy_buf_len = 4096; /* buffer up to 4 KB */ |
|
60 |
|
61 /* Buffer entropy data, and feed it to the RNG, entropy_buf_len bytes at a time. |
|
62 * Returns error if RNG_RandomUpdate fails. Also increments *total_fed |
|
63 * by the number of bytes successfully buffered. |
|
64 */ |
|
65 static SECStatus BufferEntropy(char* inbuf, PRUint32 inlen, |
|
66 char* entropy_buf, PRUint32* entropy_buffered, |
|
67 PRUint32* total_fed) |
|
68 { |
|
69 PRUint32 tocopy = 0; |
|
70 PRUint32 avail = 0; |
|
71 SECStatus rv = SECSuccess; |
|
72 |
|
73 while (inlen) { |
|
74 avail = entropy_buf_len - *entropy_buffered; |
|
75 if (!avail) { |
|
76 /* Buffer is full, time to feed it to the RNG. */ |
|
77 rv = RNG_RandomUpdate(entropy_buf, entropy_buf_len); |
|
78 if (SECSuccess != rv) { |
|
79 break; |
|
80 } |
|
81 *entropy_buffered = 0; |
|
82 avail = entropy_buf_len; |
|
83 } |
|
84 tocopy = PR_MIN(avail, inlen); |
|
85 memcpy(entropy_buf + *entropy_buffered, inbuf, tocopy); |
|
86 *entropy_buffered += tocopy; |
|
87 inlen -= tocopy; |
|
88 inbuf += tocopy; |
|
89 *total_fed += tocopy; |
|
90 } |
|
91 return rv; |
|
92 } |
|
93 |
|
94 /* Feed kernel statistics structures and ks_data field to the RNG. |
|
95 * Returns status as well as the number of bytes successfully fed to the RNG. |
|
96 */ |
|
97 static SECStatus RNG_kstat(PRUint32* fed) |
|
98 { |
|
99 kstat_ctl_t* kc = NULL; |
|
100 kstat_t* ksp = NULL; |
|
101 PRUint32 entropy_buffered = 0; |
|
102 char* entropy_buf = NULL; |
|
103 SECStatus rv = SECSuccess; |
|
104 |
|
105 PORT_Assert(fed); |
|
106 if (!fed) { |
|
107 return SECFailure; |
|
108 } |
|
109 *fed = 0; |
|
110 |
|
111 kc = kstat_open(); |
|
112 PORT_Assert(kc); |
|
113 if (!kc) { |
|
114 return SECFailure; |
|
115 } |
|
116 entropy_buf = (char*) PORT_Alloc(entropy_buf_len); |
|
117 PORT_Assert(entropy_buf); |
|
118 if (entropy_buf) { |
|
119 for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { |
|
120 if (-1 == kstat_read(kc, ksp, NULL)) { |
|
121 /* missing data from a single kstat shouldn't be fatal */ |
|
122 continue; |
|
123 } |
|
124 rv = BufferEntropy((char*)ksp, sizeof(kstat_t), |
|
125 entropy_buf, &entropy_buffered, |
|
126 fed); |
|
127 if (SECSuccess != rv) { |
|
128 break; |
|
129 } |
|
130 |
|
131 if (ksp->ks_data && ksp->ks_data_size>0 && ksp->ks_ndata>0) { |
|
132 rv = BufferEntropy((char*)ksp->ks_data, ksp->ks_data_size, |
|
133 entropy_buf, &entropy_buffered, |
|
134 fed); |
|
135 if (SECSuccess != rv) { |
|
136 break; |
|
137 } |
|
138 } |
|
139 } |
|
140 if (SECSuccess == rv && entropy_buffered) { |
|
141 /* Buffer is not empty, time to feed it to the RNG */ |
|
142 rv = RNG_RandomUpdate(entropy_buf, entropy_buffered); |
|
143 } |
|
144 PORT_Free(entropy_buf); |
|
145 } else { |
|
146 rv = SECFailure; |
|
147 } |
|
148 if (kstat_close(kc)) { |
|
149 PORT_Assert(0); |
|
150 rv = SECFailure; |
|
151 } |
|
152 return rv; |
|
153 } |
|
154 |
|
155 #endif |
|
156 |
|
157 #if defined(SCO) || defined(UNIXWARE) || defined(BSDI) || defined(FREEBSD) \ |
|
158 || defined(NETBSD) || defined(DARWIN) || defined(OPENBSD) \ |
|
159 || defined(NTO) || defined(__riscos__) |
|
160 #include <sys/times.h> |
|
161 |
|
162 #define getdtablesize() sysconf(_SC_OPEN_MAX) |
|
163 |
|
164 static size_t |
|
165 GetHighResClock(void *buf, size_t maxbytes) |
|
166 { |
|
167 int ticks; |
|
168 struct tms buffer; |
|
169 |
|
170 ticks=times(&buffer); |
|
171 return CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks)); |
|
172 } |
|
173 |
|
174 static void |
|
175 GiveSystemInfo(void) |
|
176 { |
|
177 long si; |
|
178 |
|
179 /* |
|
180 * Is this really necessary? Why not use rand48 or something? |
|
181 */ |
|
182 si = sysconf(_SC_CHILD_MAX); |
|
183 RNG_RandomUpdate(&si, sizeof(si)); |
|
184 |
|
185 si = sysconf(_SC_STREAM_MAX); |
|
186 RNG_RandomUpdate(&si, sizeof(si)); |
|
187 |
|
188 si = sysconf(_SC_OPEN_MAX); |
|
189 RNG_RandomUpdate(&si, sizeof(si)); |
|
190 } |
|
191 #endif |
|
192 |
|
193 #if defined(__sun) |
|
194 #if defined(__svr4) || defined(SVR4) |
|
195 #include <sys/systeminfo.h> |
|
196 |
|
197 #define getdtablesize() sysconf(_SC_OPEN_MAX) |
|
198 |
|
199 static void |
|
200 GiveSystemInfo(void) |
|
201 { |
|
202 int rv; |
|
203 char buf[2000]; |
|
204 |
|
205 rv = sysinfo(SI_MACHINE, buf, sizeof(buf)); |
|
206 if (rv > 0) { |
|
207 RNG_RandomUpdate(buf, rv); |
|
208 } |
|
209 rv = sysinfo(SI_RELEASE, buf, sizeof(buf)); |
|
210 if (rv > 0) { |
|
211 RNG_RandomUpdate(buf, rv); |
|
212 } |
|
213 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf)); |
|
214 if (rv > 0) { |
|
215 RNG_RandomUpdate(buf, rv); |
|
216 } |
|
217 } |
|
218 |
|
219 static size_t |
|
220 GetHighResClock(void *buf, size_t maxbytes) |
|
221 { |
|
222 hrtime_t t; |
|
223 t = gethrtime(); |
|
224 if (t) { |
|
225 return CopyLowBits(buf, maxbytes, &t, sizeof(t)); |
|
226 } |
|
227 return 0; |
|
228 } |
|
229 #else /* SunOS (Sun, but not SVR4) */ |
|
230 |
|
231 extern long sysconf(int name); |
|
232 |
|
233 static size_t |
|
234 GetHighResClock(void *buf, size_t maxbytes) |
|
235 { |
|
236 return 0; |
|
237 } |
|
238 |
|
239 static void |
|
240 GiveSystemInfo(void) |
|
241 { |
|
242 long si; |
|
243 |
|
244 /* This is not very good */ |
|
245 si = sysconf(_SC_CHILD_MAX); |
|
246 RNG_RandomUpdate(&si, sizeof(si)); |
|
247 } |
|
248 #endif |
|
249 #endif /* Sun */ |
|
250 |
|
251 #if defined(__hpux) |
|
252 #include <sys/unistd.h> |
|
253 |
|
254 #define getdtablesize() sysconf(_SC_OPEN_MAX) |
|
255 |
|
256 #if defined(__ia64) |
|
257 #include <ia64/sys/inline.h> |
|
258 |
|
259 static size_t |
|
260 GetHighResClock(void *buf, size_t maxbytes) |
|
261 { |
|
262 PRUint64 t; |
|
263 |
|
264 t = _Asm_mov_from_ar(_AREG44); |
|
265 return CopyLowBits(buf, maxbytes, &t, sizeof(t)); |
|
266 } |
|
267 #else |
|
268 static size_t |
|
269 GetHighResClock(void *buf, size_t maxbytes) |
|
270 { |
|
271 extern int ret_cr16(); |
|
272 int cr16val; |
|
273 |
|
274 cr16val = ret_cr16(); |
|
275 return CopyLowBits(buf, maxbytes, &cr16val, sizeof(cr16val)); |
|
276 } |
|
277 #endif |
|
278 |
|
279 static void |
|
280 GiveSystemInfo(void) |
|
281 { |
|
282 long si; |
|
283 |
|
284 /* This is not very good */ |
|
285 si = sysconf(_AES_OS_VERSION); |
|
286 RNG_RandomUpdate(&si, sizeof(si)); |
|
287 si = sysconf(_SC_CPU_VERSION); |
|
288 RNG_RandomUpdate(&si, sizeof(si)); |
|
289 } |
|
290 #endif /* HPUX */ |
|
291 |
|
292 #if defined(OSF1) |
|
293 #include <sys/types.h> |
|
294 #include <sys/sysinfo.h> |
|
295 #include <sys/systeminfo.h> |
|
296 #include <c_asm.h> |
|
297 |
|
298 static void |
|
299 GiveSystemInfo(void) |
|
300 { |
|
301 char buf[BUFSIZ]; |
|
302 int rv; |
|
303 int off = 0; |
|
304 |
|
305 rv = sysinfo(SI_MACHINE, buf, sizeof(buf)); |
|
306 if (rv > 0) { |
|
307 RNG_RandomUpdate(buf, rv); |
|
308 } |
|
309 rv = sysinfo(SI_RELEASE, buf, sizeof(buf)); |
|
310 if (rv > 0) { |
|
311 RNG_RandomUpdate(buf, rv); |
|
312 } |
|
313 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf)); |
|
314 if (rv > 0) { |
|
315 RNG_RandomUpdate(buf, rv); |
|
316 } |
|
317 } |
|
318 |
|
319 /* |
|
320 * Use the "get the cycle counter" instruction on the alpha. |
|
321 * The low 32 bits completely turn over in less than a minute. |
|
322 * The high 32 bits are some non-counter gunk that changes sometimes. |
|
323 */ |
|
324 static size_t |
|
325 GetHighResClock(void *buf, size_t maxbytes) |
|
326 { |
|
327 unsigned long t; |
|
328 |
|
329 t = asm("rpcc %v0"); |
|
330 return CopyLowBits(buf, maxbytes, &t, sizeof(t)); |
|
331 } |
|
332 |
|
333 #endif /* Alpha */ |
|
334 |
|
335 #if defined(_IBMR2) |
|
336 static size_t |
|
337 GetHighResClock(void *buf, size_t maxbytes) |
|
338 { |
|
339 return 0; |
|
340 } |
|
341 |
|
342 static void |
|
343 GiveSystemInfo(void) |
|
344 { |
|
345 /* XXX haven't found any yet! */ |
|
346 } |
|
347 #endif /* IBM R2 */ |
|
348 |
|
349 #if defined(LINUX) |
|
350 #include <sys/sysinfo.h> |
|
351 |
|
352 static size_t |
|
353 GetHighResClock(void *buf, size_t maxbytes) |
|
354 { |
|
355 return 0; |
|
356 } |
|
357 |
|
358 static void |
|
359 GiveSystemInfo(void) |
|
360 { |
|
361 #ifndef NO_SYSINFO |
|
362 struct sysinfo si; |
|
363 if (sysinfo(&si) == 0) { |
|
364 RNG_RandomUpdate(&si, sizeof(si)); |
|
365 } |
|
366 #endif |
|
367 } |
|
368 #endif /* LINUX */ |
|
369 |
|
370 #if defined(NCR) |
|
371 |
|
372 #include <sys/utsname.h> |
|
373 #include <sys/systeminfo.h> |
|
374 |
|
375 #define getdtablesize() sysconf(_SC_OPEN_MAX) |
|
376 |
|
377 static size_t |
|
378 GetHighResClock(void *buf, size_t maxbytes) |
|
379 { |
|
380 return 0; |
|
381 } |
|
382 |
|
383 static void |
|
384 GiveSystemInfo(void) |
|
385 { |
|
386 int rv; |
|
387 char buf[2000]; |
|
388 |
|
389 rv = sysinfo(SI_MACHINE, buf, sizeof(buf)); |
|
390 if (rv > 0) { |
|
391 RNG_RandomUpdate(buf, rv); |
|
392 } |
|
393 rv = sysinfo(SI_RELEASE, buf, sizeof(buf)); |
|
394 if (rv > 0) { |
|
395 RNG_RandomUpdate(buf, rv); |
|
396 } |
|
397 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf)); |
|
398 if (rv > 0) { |
|
399 RNG_RandomUpdate(buf, rv); |
|
400 } |
|
401 } |
|
402 |
|
403 #endif /* NCR */ |
|
404 |
|
405 #if defined(sgi) |
|
406 #include <fcntl.h> |
|
407 #undef PRIVATE |
|
408 #include <sys/mman.h> |
|
409 #include <sys/syssgi.h> |
|
410 #include <sys/immu.h> |
|
411 #include <sys/systeminfo.h> |
|
412 #include <sys/utsname.h> |
|
413 #include <wait.h> |
|
414 |
|
415 static void |
|
416 GiveSystemInfo(void) |
|
417 { |
|
418 int rv; |
|
419 char buf[4096]; |
|
420 |
|
421 rv = syssgi(SGI_SYSID, &buf[0]); |
|
422 if (rv > 0) { |
|
423 RNG_RandomUpdate(buf, MAXSYSIDSIZE); |
|
424 } |
|
425 #ifdef SGI_RDUBLK |
|
426 rv = syssgi(SGI_RDUBLK, getpid(), &buf[0], sizeof(buf)); |
|
427 if (rv > 0) { |
|
428 RNG_RandomUpdate(buf, sizeof(buf)); |
|
429 } |
|
430 #endif /* SGI_RDUBLK */ |
|
431 rv = syssgi(SGI_INVENT, SGI_INV_READ, buf, sizeof(buf)); |
|
432 if (rv > 0) { |
|
433 RNG_RandomUpdate(buf, sizeof(buf)); |
|
434 } |
|
435 rv = sysinfo(SI_MACHINE, buf, sizeof(buf)); |
|
436 if (rv > 0) { |
|
437 RNG_RandomUpdate(buf, rv); |
|
438 } |
|
439 rv = sysinfo(SI_RELEASE, buf, sizeof(buf)); |
|
440 if (rv > 0) { |
|
441 RNG_RandomUpdate(buf, rv); |
|
442 } |
|
443 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf)); |
|
444 if (rv > 0) { |
|
445 RNG_RandomUpdate(buf, rv); |
|
446 } |
|
447 } |
|
448 |
|
449 static size_t GetHighResClock(void *buf, size_t maxbuf) |
|
450 { |
|
451 unsigned phys_addr, raddr, cycleval; |
|
452 static volatile unsigned *iotimer_addr = NULL; |
|
453 static int tries = 0; |
|
454 static int cntr_size; |
|
455 int mfd; |
|
456 long s0[2]; |
|
457 struct timeval tv; |
|
458 |
|
459 #ifndef SGI_CYCLECNTR_SIZE |
|
460 #define SGI_CYCLECNTR_SIZE 165 /* Size user needs to use to read CC */ |
|
461 #endif |
|
462 |
|
463 if (iotimer_addr == NULL) { |
|
464 if (tries++ > 1) { |
|
465 /* Don't keep trying if it didn't work */ |
|
466 return 0; |
|
467 } |
|
468 |
|
469 /* |
|
470 ** For SGI machines we can use the cycle counter, if it has one, |
|
471 ** to generate some truly random numbers |
|
472 */ |
|
473 phys_addr = syssgi(SGI_QUERY_CYCLECNTR, &cycleval); |
|
474 if (phys_addr) { |
|
475 int pgsz = getpagesize(); |
|
476 int pgoffmask = pgsz - 1; |
|
477 |
|
478 raddr = phys_addr & ~pgoffmask; |
|
479 mfd = open("/dev/mmem", O_RDONLY); |
|
480 if (mfd < 0) { |
|
481 return 0; |
|
482 } |
|
483 iotimer_addr = (unsigned *) |
|
484 mmap(0, pgoffmask, PROT_READ, MAP_PRIVATE, mfd, (int)raddr); |
|
485 if (iotimer_addr == (void*)-1) { |
|
486 close(mfd); |
|
487 iotimer_addr = NULL; |
|
488 return 0; |
|
489 } |
|
490 iotimer_addr = (unsigned*) |
|
491 ((__psint_t)iotimer_addr | (phys_addr & pgoffmask)); |
|
492 /* |
|
493 * The file 'mfd' is purposefully not closed. |
|
494 */ |
|
495 cntr_size = syssgi(SGI_CYCLECNTR_SIZE); |
|
496 if (cntr_size < 0) { |
|
497 struct utsname utsinfo; |
|
498 |
|
499 /* |
|
500 * We must be executing on a 6.0 or earlier system, since the |
|
501 * SGI_CYCLECNTR_SIZE call is not supported. |
|
502 * |
|
503 * The only pre-6.1 platforms with 64-bit counters are |
|
504 * IP19 and IP21 (Challenge, PowerChallenge, Onyx). |
|
505 */ |
|
506 uname(&utsinfo); |
|
507 if (!strncmp(utsinfo.machine, "IP19", 4) || |
|
508 !strncmp(utsinfo.machine, "IP21", 4)) |
|
509 cntr_size = 64; |
|
510 else |
|
511 cntr_size = 32; |
|
512 } |
|
513 cntr_size /= 8; /* Convert from bits to bytes */ |
|
514 } |
|
515 } |
|
516 |
|
517 s0[0] = *iotimer_addr; |
|
518 if (cntr_size > 4) |
|
519 s0[1] = *(iotimer_addr + 1); |
|
520 memcpy(buf, (char *)&s0[0], cntr_size); |
|
521 return CopyLowBits(buf, maxbuf, &s0, cntr_size); |
|
522 } |
|
523 #endif |
|
524 |
|
525 #if defined(sony) |
|
526 #include <sys/systeminfo.h> |
|
527 |
|
528 #define getdtablesize() sysconf(_SC_OPEN_MAX) |
|
529 |
|
530 static size_t |
|
531 GetHighResClock(void *buf, size_t maxbytes) |
|
532 { |
|
533 return 0; |
|
534 } |
|
535 |
|
536 static void |
|
537 GiveSystemInfo(void) |
|
538 { |
|
539 int rv; |
|
540 char buf[2000]; |
|
541 |
|
542 rv = sysinfo(SI_MACHINE, buf, sizeof(buf)); |
|
543 if (rv > 0) { |
|
544 RNG_RandomUpdate(buf, rv); |
|
545 } |
|
546 rv = sysinfo(SI_RELEASE, buf, sizeof(buf)); |
|
547 if (rv > 0) { |
|
548 RNG_RandomUpdate(buf, rv); |
|
549 } |
|
550 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf)); |
|
551 if (rv > 0) { |
|
552 RNG_RandomUpdate(buf, rv); |
|
553 } |
|
554 } |
|
555 #endif /* sony */ |
|
556 |
|
557 #if defined(sinix) |
|
558 #include <sys/systeminfo.h> |
|
559 #include <sys/times.h> |
|
560 |
|
561 int gettimeofday(struct timeval *, struct timezone *); |
|
562 int gethostname(char *, int); |
|
563 |
|
564 #define getdtablesize() sysconf(_SC_OPEN_MAX) |
|
565 |
|
566 static size_t |
|
567 GetHighResClock(void *buf, size_t maxbytes) |
|
568 { |
|
569 int ticks; |
|
570 struct tms buffer; |
|
571 |
|
572 ticks=times(&buffer); |
|
573 return CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks)); |
|
574 } |
|
575 |
|
576 static void |
|
577 GiveSystemInfo(void) |
|
578 { |
|
579 int rv; |
|
580 char buf[2000]; |
|
581 |
|
582 rv = sysinfo(SI_MACHINE, buf, sizeof(buf)); |
|
583 if (rv > 0) { |
|
584 RNG_RandomUpdate(buf, rv); |
|
585 } |
|
586 rv = sysinfo(SI_RELEASE, buf, sizeof(buf)); |
|
587 if (rv > 0) { |
|
588 RNG_RandomUpdate(buf, rv); |
|
589 } |
|
590 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf)); |
|
591 if (rv > 0) { |
|
592 RNG_RandomUpdate(buf, rv); |
|
593 } |
|
594 } |
|
595 #endif /* sinix */ |
|
596 |
|
597 |
|
598 #ifdef BEOS |
|
599 #include <be/kernel/OS.h> |
|
600 |
|
601 static size_t |
|
602 GetHighResClock(void *buf, size_t maxbytes) |
|
603 { |
|
604 bigtime_t bigtime; /* Actually a int64 */ |
|
605 |
|
606 bigtime = real_time_clock_usecs(); |
|
607 return CopyLowBits(buf, maxbytes, &bigtime, sizeof(bigtime)); |
|
608 } |
|
609 |
|
610 static void |
|
611 GiveSystemInfo(void) |
|
612 { |
|
613 system_info *info = NULL; |
|
614 PRInt32 val; |
|
615 get_system_info(info); |
|
616 if (info) { |
|
617 val = info->boot_time; |
|
618 RNG_RandomUpdate(&val, sizeof(val)); |
|
619 val = info->used_pages; |
|
620 RNG_RandomUpdate(&val, sizeof(val)); |
|
621 val = info->used_ports; |
|
622 RNG_RandomUpdate(&val, sizeof(val)); |
|
623 val = info->used_threads; |
|
624 RNG_RandomUpdate(&val, sizeof(val)); |
|
625 val = info->used_teams; |
|
626 RNG_RandomUpdate(&val, sizeof(val)); |
|
627 } |
|
628 } |
|
629 #endif /* BEOS */ |
|
630 |
|
631 #if defined(nec_ews) |
|
632 #include <sys/systeminfo.h> |
|
633 |
|
634 #define getdtablesize() sysconf(_SC_OPEN_MAX) |
|
635 |
|
636 static size_t |
|
637 GetHighResClock(void *buf, size_t maxbytes) |
|
638 { |
|
639 return 0; |
|
640 } |
|
641 |
|
642 static void |
|
643 GiveSystemInfo(void) |
|
644 { |
|
645 int rv; |
|
646 char buf[2000]; |
|
647 |
|
648 rv = sysinfo(SI_MACHINE, buf, sizeof(buf)); |
|
649 if (rv > 0) { |
|
650 RNG_RandomUpdate(buf, rv); |
|
651 } |
|
652 rv = sysinfo(SI_RELEASE, buf, sizeof(buf)); |
|
653 if (rv > 0) { |
|
654 RNG_RandomUpdate(buf, rv); |
|
655 } |
|
656 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf)); |
|
657 if (rv > 0) { |
|
658 RNG_RandomUpdate(buf, rv); |
|
659 } |
|
660 } |
|
661 #endif /* nec_ews */ |
|
662 |
|
663 size_t RNG_GetNoise(void *buf, size_t maxbytes) |
|
664 { |
|
665 struct timeval tv; |
|
666 int n = 0; |
|
667 int c; |
|
668 |
|
669 n = GetHighResClock(buf, maxbytes); |
|
670 maxbytes -= n; |
|
671 |
|
672 (void)gettimeofday(&tv, 0); |
|
673 c = CopyLowBits((char*)buf+n, maxbytes, &tv.tv_usec, sizeof(tv.tv_usec)); |
|
674 n += c; |
|
675 maxbytes -= c; |
|
676 c = CopyLowBits((char*)buf+n, maxbytes, &tv.tv_sec, sizeof(tv.tv_sec)); |
|
677 n += c; |
|
678 return n; |
|
679 } |
|
680 |
|
681 #define SAFE_POPEN_MAXARGS 10 /* must be at least 2 */ |
|
682 |
|
683 /* |
|
684 * safe_popen is static to this module and we know what arguments it is |
|
685 * called with. Note that this version only supports a single open child |
|
686 * process at any time. |
|
687 */ |
|
688 static pid_t safe_popen_pid; |
|
689 static struct sigaction oldact; |
|
690 |
|
691 static FILE * |
|
692 safe_popen(char *cmd) |
|
693 { |
|
694 int p[2], fd, argc; |
|
695 pid_t pid; |
|
696 char *argv[SAFE_POPEN_MAXARGS + 1]; |
|
697 FILE *fp; |
|
698 static char blank[] = " \t"; |
|
699 static struct sigaction newact; |
|
700 |
|
701 if (pipe(p) < 0) |
|
702 return 0; |
|
703 |
|
704 fp = fdopen(p[0], "r"); |
|
705 if (fp == 0) { |
|
706 close(p[0]); |
|
707 close(p[1]); |
|
708 return 0; |
|
709 } |
|
710 |
|
711 /* Setup signals so that SIGCHLD is ignored as we want to do waitpid */ |
|
712 newact.sa_handler = SIG_DFL; |
|
713 newact.sa_flags = 0; |
|
714 sigfillset(&newact.sa_mask); |
|
715 sigaction (SIGCHLD, &newact, &oldact); |
|
716 |
|
717 pid = fork(); |
|
718 switch (pid) { |
|
719 int ndesc; |
|
720 |
|
721 case -1: |
|
722 fclose(fp); /* this closes p[0], the fd associated with fp */ |
|
723 close(p[1]); |
|
724 sigaction (SIGCHLD, &oldact, NULL); |
|
725 return 0; |
|
726 |
|
727 case 0: |
|
728 /* dup write-side of pipe to stderr and stdout */ |
|
729 if (p[1] != 1) dup2(p[1], 1); |
|
730 if (p[1] != 2) dup2(p[1], 2); |
|
731 |
|
732 /* |
|
733 * close the other file descriptors, except stdin which we |
|
734 * try reassociating with /dev/null, first (bug 174993) |
|
735 */ |
|
736 if (!freopen("/dev/null", "r", stdin)) |
|
737 close(0); |
|
738 ndesc = getdtablesize(); |
|
739 for (fd = PR_MIN(65536, ndesc); --fd > 2; close(fd)); |
|
740 |
|
741 /* clean up environment in the child process */ |
|
742 putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc:/usr/etc"); |
|
743 putenv("SHELL=/bin/sh"); |
|
744 putenv("IFS= \t"); |
|
745 |
|
746 /* |
|
747 * The caller may have passed us a string that is in text |
|
748 * space. It may be illegal to modify the string |
|
749 */ |
|
750 cmd = strdup(cmd); |
|
751 /* format argv */ |
|
752 argv[0] = strtok(cmd, blank); |
|
753 argc = 1; |
|
754 while ((argv[argc] = strtok(0, blank)) != 0) { |
|
755 if (++argc == SAFE_POPEN_MAXARGS) { |
|
756 argv[argc] = 0; |
|
757 break; |
|
758 } |
|
759 } |
|
760 |
|
761 /* and away we go */ |
|
762 execvp(argv[0], argv); |
|
763 exit(127); |
|
764 break; |
|
765 |
|
766 default: |
|
767 close(p[1]); |
|
768 break; |
|
769 } |
|
770 |
|
771 /* non-zero means there's a cmd running */ |
|
772 safe_popen_pid = pid; |
|
773 return fp; |
|
774 } |
|
775 |
|
776 static int |
|
777 safe_pclose(FILE *fp) |
|
778 { |
|
779 pid_t pid; |
|
780 int status = -1, rv; |
|
781 |
|
782 if ((pid = safe_popen_pid) == 0) |
|
783 return -1; |
|
784 safe_popen_pid = 0; |
|
785 |
|
786 fclose(fp); |
|
787 |
|
788 /* yield the processor so the child gets some time to exit normally */ |
|
789 PR_Sleep(PR_INTERVAL_NO_WAIT); |
|
790 |
|
791 /* if the child hasn't exited, kill it -- we're done with its output */ |
|
792 while ((rv = waitpid(pid, &status, WNOHANG)) == -1 && errno == EINTR) |
|
793 ; |
|
794 if (rv == 0) { |
|
795 kill(pid, SIGKILL); |
|
796 while ((rv = waitpid(pid, &status, 0)) == -1 && errno == EINTR) |
|
797 ; |
|
798 } |
|
799 |
|
800 /* Reset SIGCHLD signal hander before returning */ |
|
801 sigaction(SIGCHLD, &oldact, NULL); |
|
802 |
|
803 return status; |
|
804 } |
|
805 |
|
806 #ifdef DARWIN |
|
807 #include <TargetConditionals.h> |
|
808 #if !TARGET_OS_IPHONE |
|
809 #include <crt_externs.h> |
|
810 #endif |
|
811 #endif |
|
812 |
|
813 /* Fork netstat to collect its output by default. Do not unset this unless |
|
814 * another source of entropy is available |
|
815 */ |
|
816 #define DO_NETSTAT 1 |
|
817 |
|
818 void RNG_SystemInfoForRNG(void) |
|
819 { |
|
820 FILE *fp; |
|
821 char buf[BUFSIZ]; |
|
822 size_t bytes; |
|
823 const char * const *cp; |
|
824 char *randfile; |
|
825 #ifdef DARWIN |
|
826 #if TARGET_OS_IPHONE |
|
827 /* iOS does not expose a way to access environ. */ |
|
828 char **environ = NULL; |
|
829 #else |
|
830 char **environ = *_NSGetEnviron(); |
|
831 #endif |
|
832 #else |
|
833 extern char **environ; |
|
834 #endif |
|
835 #ifdef BEOS |
|
836 static const char * const files[] = { |
|
837 "/boot/var/swap", |
|
838 "/boot/var/log/syslog", |
|
839 "/boot/var/tmp", |
|
840 "/boot/home/config/settings", |
|
841 "/boot/home", |
|
842 0 |
|
843 }; |
|
844 #else |
|
845 static const char * const files[] = { |
|
846 "/etc/passwd", |
|
847 "/etc/utmp", |
|
848 "/tmp", |
|
849 "/var/tmp", |
|
850 "/usr/tmp", |
|
851 0 |
|
852 }; |
|
853 #endif |
|
854 |
|
855 #if defined(BSDI) |
|
856 static char netstat_ni_cmd[] = "netstat -nis"; |
|
857 #else |
|
858 static char netstat_ni_cmd[] = "netstat -ni"; |
|
859 #endif |
|
860 |
|
861 GiveSystemInfo(); |
|
862 |
|
863 bytes = RNG_GetNoise(buf, sizeof(buf)); |
|
864 RNG_RandomUpdate(buf, bytes); |
|
865 |
|
866 /* |
|
867 * Pass the C environment and the addresses of the pointers to the |
|
868 * hash function. This makes the random number function depend on the |
|
869 * execution environment of the user and on the platform the program |
|
870 * is running on. |
|
871 */ |
|
872 if (environ != NULL) { |
|
873 cp = (const char * const *) environ; |
|
874 while (*cp) { |
|
875 RNG_RandomUpdate(*cp, strlen(*cp)); |
|
876 cp++; |
|
877 } |
|
878 RNG_RandomUpdate(environ, (char*)cp - (char*)environ); |
|
879 } |
|
880 |
|
881 /* Give in system information */ |
|
882 if (gethostname(buf, sizeof(buf)) == 0) { |
|
883 RNG_RandomUpdate(buf, strlen(buf)); |
|
884 } |
|
885 GiveSystemInfo(); |
|
886 |
|
887 /* grab some data from system's PRNG before any other files. */ |
|
888 bytes = RNG_FileUpdate("/dev/urandom", SYSTEM_RNG_SEED_COUNT); |
|
889 |
|
890 /* If the user points us to a random file, pass it through the rng */ |
|
891 randfile = getenv("NSRANDFILE"); |
|
892 if ( ( randfile != NULL ) && ( randfile[0] != '\0') ) { |
|
893 char *randCountString = getenv("NSRANDCOUNT"); |
|
894 int randCount = randCountString ? atoi(randCountString) : 0; |
|
895 if (randCount != 0) { |
|
896 RNG_FileUpdate(randfile, randCount); |
|
897 } else { |
|
898 RNG_FileForRNG(randfile); |
|
899 } |
|
900 } |
|
901 |
|
902 /* pass other files through */ |
|
903 for (cp = files; *cp; cp++) |
|
904 RNG_FileForRNG(*cp); |
|
905 |
|
906 /* |
|
907 * Bug 100447: On BSD/OS 4.2 and 4.3, we have problem calling safe_popen |
|
908 * in a pthreads environment. Therefore, we call safe_popen last and on |
|
909 * BSD/OS we do not call safe_popen when we succeeded in getting data |
|
910 * from /dev/urandom. |
|
911 * |
|
912 * Bug 174993: On platforms providing /dev/urandom, don't fork netstat |
|
913 * either, if data has been gathered successfully. |
|
914 */ |
|
915 |
|
916 #if defined(BSDI) || defined(FREEBSD) || defined(NETBSD) \ |
|
917 || defined(OPENBSD) || defined(DARWIN) || defined(LINUX) \ |
|
918 || defined(HPUX) |
|
919 if (bytes) |
|
920 return; |
|
921 #endif |
|
922 |
|
923 #ifdef SOLARIS |
|
924 |
|
925 /* |
|
926 * On Solaris, NSS may be initialized automatically from libldap in |
|
927 * applications that are unaware of the use of NSS. safe_popen forks, and |
|
928 * sometimes creates issues with some applications' pthread_atfork handlers. |
|
929 * We always have /dev/urandom on Solaris 9 and above as an entropy source, |
|
930 * and for Solaris 8 we have the libkstat interface, so we don't need to |
|
931 * fork netstat. |
|
932 */ |
|
933 |
|
934 #undef DO_NETSTAT |
|
935 if (!bytes) { |
|
936 /* On Solaris 8, /dev/urandom isn't available, so we use libkstat. */ |
|
937 PRUint32 kstat_bytes = 0; |
|
938 if (SECSuccess != RNG_kstat(&kstat_bytes)) { |
|
939 PORT_Assert(0); |
|
940 } |
|
941 bytes += kstat_bytes; |
|
942 PORT_Assert(bytes); |
|
943 } |
|
944 #endif |
|
945 |
|
946 #ifdef DO_NETSTAT |
|
947 fp = safe_popen(netstat_ni_cmd); |
|
948 if (fp != NULL) { |
|
949 while ((bytes = fread(buf, 1, sizeof(buf), fp)) > 0) |
|
950 RNG_RandomUpdate(buf, bytes); |
|
951 safe_pclose(fp); |
|
952 } |
|
953 #endif |
|
954 |
|
955 } |
|
956 |
|
957 #define TOTAL_FILE_LIMIT 1000000 /* one million */ |
|
958 |
|
959 size_t RNG_FileUpdate(const char *fileName, size_t limit) |
|
960 { |
|
961 FILE * file; |
|
962 int fd; |
|
963 int bytes; |
|
964 size_t fileBytes = 0; |
|
965 struct stat stat_buf; |
|
966 unsigned char buffer[BUFSIZ]; |
|
967 static size_t totalFileBytes = 0; |
|
968 |
|
969 /* suppress valgrind warnings due to holes in struct stat */ |
|
970 memset(&stat_buf, 0, sizeof(stat_buf)); |
|
971 |
|
972 if (stat((char *)fileName, &stat_buf) < 0) |
|
973 return fileBytes; |
|
974 RNG_RandomUpdate(&stat_buf, sizeof(stat_buf)); |
|
975 |
|
976 file = fopen(fileName, "r"); |
|
977 if (file != NULL) { |
|
978 /* Read from the underlying file descriptor directly to bypass stdio |
|
979 * buffering and avoid reading more bytes than we need from |
|
980 * /dev/urandom. NOTE: we can't use fread with unbuffered I/O because |
|
981 * fread may return EOF in unbuffered I/O mode on Android. |
|
982 * |
|
983 * Moreover, we read into a buffer of size BUFSIZ, so buffered I/O |
|
984 * has no performance advantage. */ |
|
985 fd = fileno(file); |
|
986 /* 'file' was just opened, so this should not fail. */ |
|
987 PORT_Assert(fd != -1); |
|
988 while (limit > fileBytes) { |
|
989 bytes = PR_MIN(sizeof buffer, limit - fileBytes); |
|
990 bytes = read(fd, buffer, bytes); |
|
991 if (bytes <= 0) |
|
992 break; |
|
993 RNG_RandomUpdate(buffer, bytes); |
|
994 fileBytes += bytes; |
|
995 totalFileBytes += bytes; |
|
996 /* after TOTAL_FILE_LIMIT has been reached, only read in first |
|
997 ** buffer of data from each subsequent file. |
|
998 */ |
|
999 if (totalFileBytes > TOTAL_FILE_LIMIT) |
|
1000 break; |
|
1001 } |
|
1002 fclose(file); |
|
1003 } |
|
1004 /* |
|
1005 * Pass yet another snapshot of our highest resolution clock into |
|
1006 * the hash function. |
|
1007 */ |
|
1008 bytes = RNG_GetNoise(buffer, sizeof(buffer)); |
|
1009 RNG_RandomUpdate(buffer, bytes); |
|
1010 return fileBytes; |
|
1011 } |
|
1012 |
|
1013 void RNG_FileForRNG(const char *fileName) |
|
1014 { |
|
1015 RNG_FileUpdate(fileName, TOTAL_FILE_LIMIT); |
|
1016 } |
|
1017 |
|
1018 void ReadSingleFile(const char *fileName) |
|
1019 { |
|
1020 FILE * file; |
|
1021 unsigned char buffer[BUFSIZ]; |
|
1022 |
|
1023 file = fopen(fileName, "rb"); |
|
1024 if (file != NULL) { |
|
1025 while (fread(buffer, 1, sizeof(buffer), file) > 0) |
|
1026 ; |
|
1027 fclose(file); |
|
1028 } |
|
1029 } |
|
1030 |
|
1031 #define _POSIX_PTHREAD_SEMANTICS |
|
1032 #include <dirent.h> |
|
1033 |
|
1034 PRBool |
|
1035 ReadFileOK(char *dir, char *file) |
|
1036 { |
|
1037 struct stat stat_buf; |
|
1038 char filename[PATH_MAX]; |
|
1039 int count = snprintf(filename, sizeof filename, "%s/%s",dir, file); |
|
1040 |
|
1041 if (count <= 0) { |
|
1042 return PR_FALSE; /* name too long, can't read it anyway */ |
|
1043 } |
|
1044 |
|
1045 if (stat(filename, &stat_buf) < 0) |
|
1046 return PR_FALSE; /* can't stat, probably can't read it then as well */ |
|
1047 return S_ISREG(stat_buf.st_mode) ? PR_TRUE : PR_FALSE; |
|
1048 } |
|
1049 |
|
1050 /* |
|
1051 * read one file out of either /etc or the user's home directory. |
|
1052 * fileToRead tells which file to read. |
|
1053 * |
|
1054 * return 1 if it's time to reset the fileToRead (no more files to read). |
|
1055 */ |
|
1056 int ReadOneFile(int fileToRead) |
|
1057 { |
|
1058 char *dir = "/etc"; |
|
1059 DIR *fd = opendir(dir); |
|
1060 int resetCount = 0; |
|
1061 #ifdef SOLARIS |
|
1062 /* grumble, Solaris does not define struct dirent to be the full length */ |
|
1063 typedef union { |
|
1064 unsigned char space[sizeof(struct dirent) + MAXNAMELEN]; |
|
1065 struct dirent dir; |
|
1066 } dirent_hack; |
|
1067 dirent_hack entry, firstEntry; |
|
1068 |
|
1069 #define entry_dir entry.dir |
|
1070 #else |
|
1071 struct dirent entry, firstEntry; |
|
1072 #define entry_dir entry |
|
1073 #endif |
|
1074 |
|
1075 int i, error = -1; |
|
1076 |
|
1077 if (fd == NULL) { |
|
1078 dir = getenv("HOME"); |
|
1079 if (dir) { |
|
1080 fd = opendir(dir); |
|
1081 } |
|
1082 } |
|
1083 if (fd == NULL) { |
|
1084 return 1; |
|
1085 } |
|
1086 |
|
1087 for (i=0; i <= fileToRead; i++) { |
|
1088 struct dirent *result = NULL; |
|
1089 do { |
|
1090 error = readdir_r(fd, &entry_dir, &result); |
|
1091 } while (error == 0 && result != NULL && |
|
1092 !ReadFileOK(dir,&result->d_name[0])); |
|
1093 if (error != 0 || result == NULL) { |
|
1094 resetCount = 1; /* read to the end, start again at the beginning */ |
|
1095 if (i != 0) { |
|
1096 /* ran out of entries in the directory, use the first one */ |
|
1097 entry = firstEntry; |
|
1098 error = 0; |
|
1099 break; |
|
1100 } |
|
1101 /* if i== 0, there were no readable entries in the directory */ |
|
1102 break; |
|
1103 } |
|
1104 if (i==0) { |
|
1105 /* save the first entry in case we run out of entries */ |
|
1106 firstEntry = entry; |
|
1107 } |
|
1108 } |
|
1109 |
|
1110 if (error == 0) { |
|
1111 char filename[PATH_MAX]; |
|
1112 int count = snprintf(filename, sizeof filename, |
|
1113 "%s/%s",dir, &entry_dir.d_name[0]); |
|
1114 if (count >= 1) { |
|
1115 ReadSingleFile(filename); |
|
1116 } |
|
1117 } |
|
1118 |
|
1119 closedir(fd); |
|
1120 return resetCount; |
|
1121 } |
|
1122 |
|
1123 /* |
|
1124 * do something to try to introduce more noise into the 'GetNoise' call |
|
1125 */ |
|
1126 static void rng_systemJitter(void) |
|
1127 { |
|
1128 static int fileToRead = 1; |
|
1129 |
|
1130 if (ReadOneFile(fileToRead)) { |
|
1131 fileToRead = 1; |
|
1132 } else { |
|
1133 fileToRead++; |
|
1134 } |
|
1135 } |
|
1136 |
|
1137 size_t RNG_SystemRNG(void *dest, size_t maxLen) |
|
1138 { |
|
1139 FILE *file; |
|
1140 int fd; |
|
1141 int bytes; |
|
1142 size_t fileBytes = 0; |
|
1143 unsigned char *buffer = dest; |
|
1144 |
|
1145 file = fopen("/dev/urandom", "r"); |
|
1146 if (file == NULL) { |
|
1147 return rng_systemFromNoise(dest, maxLen); |
|
1148 } |
|
1149 /* Read from the underlying file descriptor directly to bypass stdio |
|
1150 * buffering and avoid reading more bytes than we need from /dev/urandom. |
|
1151 * NOTE: we can't use fread with unbuffered I/O because fread may return |
|
1152 * EOF in unbuffered I/O mode on Android. |
|
1153 */ |
|
1154 fd = fileno(file); |
|
1155 /* 'file' was just opened, so this should not fail. */ |
|
1156 PORT_Assert(fd != -1); |
|
1157 while (maxLen > fileBytes) { |
|
1158 bytes = maxLen - fileBytes; |
|
1159 bytes = read(fd, buffer, bytes); |
|
1160 if (bytes <= 0) |
|
1161 break; |
|
1162 fileBytes += bytes; |
|
1163 buffer += bytes; |
|
1164 } |
|
1165 fclose(file); |
|
1166 if (fileBytes != maxLen) { |
|
1167 PORT_SetError(SEC_ERROR_NEED_RANDOM); /* system RNG failed */ |
|
1168 fileBytes = 0; |
|
1169 } |
|
1170 return fileBytes; |
|
1171 } |