Import 3.0 beta 3 tarball
[mozilla-nss.git] / security / nss / lib / freebl / unix_rand.c
blobb9664583c7f04f1c35540c88b94819601662e333
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include <stdio.h>
38 #include <string.h>
39 #include <signal.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <sys/time.h>
44 #include <sys/wait.h>
45 #include <sys/stat.h>
46 #include "secrng.h"
47 #include "secerr.h"
48 #include "prerror.h"
49 #include "prthread.h"
51 size_t RNG_FileUpdate(const char *fileName, size_t limit);
54 * When copying data to the buffer we want the least signicant bytes
55 * from the input since those bits are changing the fastest. The address
56 * of least significant byte depends upon whether we are running on
57 * a big-endian or little-endian machine.
59 * Does this mean the least signicant bytes are the most significant
60 * to us? :-)
63 static size_t CopyLowBits(void *dst, size_t dstlen, void *src, size_t srclen)
65 union endianness {
66 int32 i;
67 char c[4];
68 } u;
70 if (srclen <= dstlen) {
71 memcpy(dst, src, srclen);
72 return srclen;
74 u.i = 0x01020304;
75 if (u.c[0] == 0x01) {
76 /* big-endian case */
77 memcpy(dst, (char*)src + (srclen - dstlen), dstlen);
78 } else {
79 /* little-endian case */
80 memcpy(dst, src, dstlen);
82 return dstlen;
85 #ifdef SOLARIS
87 #include <kstat.h>
89 static const PRUint32 entropy_buf_len = 4096; /* buffer up to 4 KB */
91 /* Buffer entropy data, and feed it to the RNG, entropy_buf_len bytes at a time.
92 * Returns error if RNG_RandomUpdate fails. Also increments *total_fed
93 * by the number of bytes successfully buffered.
95 static SECStatus BufferEntropy(char* inbuf, PRUint32 inlen,
96 char* entropy_buf, PRUint32* entropy_buffered,
97 PRUint32* total_fed)
99 PRUint32 tocopy = 0;
100 PRUint32 avail = 0;
101 SECStatus rv = SECSuccess;
103 while (inlen) {
104 avail = entropy_buf_len - *entropy_buffered;
105 if (!avail) {
106 /* Buffer is full, time to feed it to the RNG. */
107 rv = RNG_RandomUpdate(entropy_buf, entropy_buf_len);
108 if (SECSuccess != rv) {
109 break;
111 *entropy_buffered = 0;
112 avail = entropy_buf_len;
114 tocopy = PR_MIN(avail, inlen);
115 memcpy(entropy_buf + *entropy_buffered, inbuf, tocopy);
116 *entropy_buffered += tocopy;
117 inlen -= tocopy;
118 inbuf += tocopy;
119 *total_fed += tocopy;
121 return rv;
124 /* Feed kernel statistics structures and ks_data field to the RNG.
125 * Returns status as well as the number of bytes successfully fed to the RNG.
127 static SECStatus RNG_kstat(PRUint32* fed)
129 kstat_ctl_t* kc = NULL;
130 kstat_t* ksp = NULL;
131 PRUint32 entropy_buffered = 0;
132 char* entropy_buf = NULL;
133 SECStatus rv = SECSuccess;
135 PORT_Assert(fed);
136 if (!fed) {
137 return SECFailure;
139 *fed = 0;
141 kc = kstat_open();
142 PORT_Assert(kc);
143 if (!kc) {
144 return SECFailure;
146 entropy_buf = (char*) PORT_Alloc(entropy_buf_len);
147 PORT_Assert(entropy_buf);
148 if (entropy_buf) {
149 for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
150 if (-1 == kstat_read(kc, ksp, NULL)) {
151 /* missing data from a single kstat shouldn't be fatal */
152 continue;
154 rv = BufferEntropy((char*)ksp, sizeof(kstat_t),
155 entropy_buf, &entropy_buffered,
156 fed);
157 if (SECSuccess != rv) {
158 break;
161 if (ksp->ks_data && ksp->ks_data_size>0 && ksp->ks_ndata>0) {
162 rv = BufferEntropy((char*)ksp->ks_data, ksp->ks_data_size,
163 entropy_buf, &entropy_buffered,
164 fed);
165 if (SECSuccess != rv) {
166 break;
170 if (SECSuccess == rv && entropy_buffered) {
171 /* Buffer is not empty, time to feed it to the RNG */
172 rv = RNG_RandomUpdate(entropy_buf, entropy_buffered);
174 PORT_Free(entropy_buf);
175 } else {
176 rv = SECFailure;
178 if (kstat_close(kc)) {
179 PORT_Assert(0);
180 rv = SECFailure;
182 return rv;
185 #endif
187 #if defined(SCO) || defined(UNIXWARE) || defined(BSDI) || defined(FREEBSD) \
188 || defined(NETBSD) || defined(NTO) || defined(DARWIN) || defined(OPENBSD)
189 #include <sys/times.h>
191 #define getdtablesize() sysconf(_SC_OPEN_MAX)
193 static size_t
194 GetHighResClock(void *buf, size_t maxbytes)
196 int ticks;
197 struct tms buffer;
199 ticks=times(&buffer);
200 return CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks));
203 static void
204 GiveSystemInfo(void)
206 long si;
209 * Is this really necessary? Why not use rand48 or something?
211 si = sysconf(_SC_CHILD_MAX);
212 RNG_RandomUpdate(&si, sizeof(si));
214 si = sysconf(_SC_STREAM_MAX);
215 RNG_RandomUpdate(&si, sizeof(si));
217 si = sysconf(_SC_OPEN_MAX);
218 RNG_RandomUpdate(&si, sizeof(si));
220 #endif
222 #if defined(__sun)
223 #if defined(__svr4) || defined(SVR4)
224 #include <sys/systeminfo.h>
225 #include <sys/times.h>
226 #include <wait.h>
228 int gettimeofday(struct timeval *);
229 int gethostname(char *, int);
231 #define getdtablesize() sysconf(_SC_OPEN_MAX)
233 static void
234 GiveSystemInfo(void)
236 int rv;
237 char buf[2000];
239 rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
240 if (rv > 0) {
241 RNG_RandomUpdate(buf, rv);
243 rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
244 if (rv > 0) {
245 RNG_RandomUpdate(buf, rv);
247 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
248 if (rv > 0) {
249 RNG_RandomUpdate(buf, rv);
253 static size_t
254 GetHighResClock(void *buf, size_t maxbytes)
256 hrtime_t t;
257 t = gethrtime();
258 if (t) {
259 return CopyLowBits(buf, maxbytes, &t, sizeof(t));
261 return 0;
263 #else /* SunOS (Sun, but not SVR4) */
265 extern long sysconf(int name);
267 static size_t
268 GetHighResClock(void *buf, size_t maxbytes)
270 return 0;
273 static void
274 GiveSystemInfo(void)
276 long si;
278 /* This is not very good */
279 si = sysconf(_SC_CHILD_MAX);
280 RNG_RandomUpdate(&si, sizeof(si));
282 #endif
283 #endif /* Sun */
285 #if defined(__hpux)
286 #include <sys/unistd.h>
288 #define getdtablesize() sysconf(_SC_OPEN_MAX)
290 #if defined(__ia64)
291 #include <ia64/sys/inline.h>
293 static size_t
294 GetHighResClock(void *buf, size_t maxbytes)
296 PRUint64 t;
298 t = _Asm_mov_from_ar(_AREG44);
299 return CopyLowBits(buf, maxbytes, &t, sizeof(t));
301 #else
302 static size_t
303 GetHighResClock(void *buf, size_t maxbytes)
305 extern int ret_cr16();
306 int cr16val;
308 cr16val = ret_cr16();
309 return CopyLowBits(buf, maxbytes, &cr16val, sizeof(cr16val));
311 #endif
313 static void
314 GiveSystemInfo(void)
316 long si;
318 /* This is not very good */
319 si = sysconf(_AES_OS_VERSION);
320 RNG_RandomUpdate(&si, sizeof(si));
321 si = sysconf(_SC_CPU_VERSION);
322 RNG_RandomUpdate(&si, sizeof(si));
324 #endif /* HPUX */
326 #if defined(OSF1)
327 #include <sys/types.h>
328 #include <sys/sysinfo.h>
329 #include <sys/systeminfo.h>
330 #include <c_asm.h>
332 static void
333 GiveSystemInfo(void)
335 char buf[BUFSIZ];
336 int rv;
337 int off = 0;
339 rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
340 if (rv > 0) {
341 RNG_RandomUpdate(buf, rv);
343 rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
344 if (rv > 0) {
345 RNG_RandomUpdate(buf, rv);
347 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
348 if (rv > 0) {
349 RNG_RandomUpdate(buf, rv);
354 * Use the "get the cycle counter" instruction on the alpha.
355 * The low 32 bits completely turn over in less than a minute.
356 * The high 32 bits are some non-counter gunk that changes sometimes.
358 static size_t
359 GetHighResClock(void *buf, size_t maxbytes)
361 unsigned long t;
363 t = asm("rpcc %v0");
364 return CopyLowBits(buf, maxbytes, &t, sizeof(t));
367 #endif /* Alpha */
369 #if defined(_IBMR2)
370 static size_t
371 GetHighResClock(void *buf, size_t maxbytes)
373 return 0;
376 static void
377 GiveSystemInfo(void)
379 /* XXX haven't found any yet! */
381 #endif /* IBM R2 */
383 #if defined(LINUX)
384 #include <sys/sysinfo.h>
386 static size_t
387 GetHighResClock(void *buf, size_t maxbytes)
389 return 0;
392 static void
393 GiveSystemInfo(void)
395 struct sysinfo si;
396 if (sysinfo(&si) == 0) {
397 RNG_RandomUpdate(&si, sizeof(si));
400 #endif /* LINUX */
402 #if defined(NCR)
404 #include <sys/utsname.h>
405 #include <sys/systeminfo.h>
407 #define getdtablesize() sysconf(_SC_OPEN_MAX)
409 static size_t
410 GetHighResClock(void *buf, size_t maxbytes)
412 return 0;
415 static void
416 GiveSystemInfo(void)
418 int rv;
419 char buf[2000];
421 rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
422 if (rv > 0) {
423 RNG_RandomUpdate(buf, rv);
425 rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
426 if (rv > 0) {
427 RNG_RandomUpdate(buf, rv);
429 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
430 if (rv > 0) {
431 RNG_RandomUpdate(buf, rv);
435 #endif /* NCR */
438 #if defined(sgi)
439 #include <fcntl.h>
440 #undef PRIVATE
441 #include <sys/mman.h>
442 #include <sys/syssgi.h>
443 #include <sys/immu.h>
444 #include <sys/systeminfo.h>
445 #include <sys/utsname.h>
446 #include <wait.h>
448 static void
449 GiveSystemInfo(void)
451 int rv;
452 char buf[4096];
454 rv = syssgi(SGI_SYSID, &buf[0]);
455 if (rv > 0) {
456 RNG_RandomUpdate(buf, MAXSYSIDSIZE);
458 #ifdef SGI_RDUBLK
459 rv = syssgi(SGI_RDUBLK, getpid(), &buf[0], sizeof(buf));
460 if (rv > 0) {
461 RNG_RandomUpdate(buf, sizeof(buf));
463 #endif /* SGI_RDUBLK */
464 rv = syssgi(SGI_INVENT, SGI_INV_READ, buf, sizeof(buf));
465 if (rv > 0) {
466 RNG_RandomUpdate(buf, sizeof(buf));
468 rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
469 if (rv > 0) {
470 RNG_RandomUpdate(buf, rv);
472 rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
473 if (rv > 0) {
474 RNG_RandomUpdate(buf, rv);
476 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
477 if (rv > 0) {
478 RNG_RandomUpdate(buf, rv);
482 static size_t GetHighResClock(void *buf, size_t maxbuf)
484 unsigned phys_addr, raddr, cycleval;
485 static volatile unsigned *iotimer_addr = NULL;
486 static int tries = 0;
487 static int cntr_size;
488 int mfd;
489 long s0[2];
490 struct timeval tv;
492 #ifndef SGI_CYCLECNTR_SIZE
493 #define SGI_CYCLECNTR_SIZE 165 /* Size user needs to use to read CC */
494 #endif
496 if (iotimer_addr == NULL) {
497 if (tries++ > 1) {
498 /* Don't keep trying if it didn't work */
499 return 0;
503 ** For SGI machines we can use the cycle counter, if it has one,
504 ** to generate some truly random numbers
506 phys_addr = syssgi(SGI_QUERY_CYCLECNTR, &cycleval);
507 if (phys_addr) {
508 int pgsz = getpagesize();
509 int pgoffmask = pgsz - 1;
511 raddr = phys_addr & ~pgoffmask;
512 mfd = open("/dev/mmem", O_RDONLY);
513 if (mfd < 0) {
514 return 0;
516 iotimer_addr = (unsigned *)
517 mmap(0, pgoffmask, PROT_READ, MAP_PRIVATE, mfd, (int)raddr);
518 if (iotimer_addr == (void*)-1) {
519 close(mfd);
520 iotimer_addr = NULL;
521 return 0;
523 iotimer_addr = (unsigned*)
524 ((__psint_t)iotimer_addr | (phys_addr & pgoffmask));
526 * The file 'mfd' is purposefully not closed.
528 cntr_size = syssgi(SGI_CYCLECNTR_SIZE);
529 if (cntr_size < 0) {
530 struct utsname utsinfo;
533 * We must be executing on a 6.0 or earlier system, since the
534 * SGI_CYCLECNTR_SIZE call is not supported.
536 * The only pre-6.1 platforms with 64-bit counters are
537 * IP19 and IP21 (Challenge, PowerChallenge, Onyx).
539 uname(&utsinfo);
540 if (!strncmp(utsinfo.machine, "IP19", 4) ||
541 !strncmp(utsinfo.machine, "IP21", 4))
542 cntr_size = 64;
543 else
544 cntr_size = 32;
546 cntr_size /= 8; /* Convert from bits to bytes */
550 s0[0] = *iotimer_addr;
551 if (cntr_size > 4)
552 s0[1] = *(iotimer_addr + 1);
553 memcpy(buf, (char *)&s0[0], cntr_size);
554 return CopyLowBits(buf, maxbuf, &s0, cntr_size);
556 #endif
558 #if defined(sony)
559 #include <sys/systeminfo.h>
561 #define getdtablesize() sysconf(_SC_OPEN_MAX)
563 static size_t
564 GetHighResClock(void *buf, size_t maxbytes)
566 return 0;
569 static void
570 GiveSystemInfo(void)
572 int rv;
573 char buf[2000];
575 rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
576 if (rv > 0) {
577 RNG_RandomUpdate(buf, rv);
579 rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
580 if (rv > 0) {
581 RNG_RandomUpdate(buf, rv);
583 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
584 if (rv > 0) {
585 RNG_RandomUpdate(buf, rv);
588 #endif /* sony */
590 #if defined(sinix)
591 #include <sys/systeminfo.h>
592 #include <sys/times.h>
594 int gettimeofday(struct timeval *, struct timezone *);
595 int gethostname(char *, int);
597 #define getdtablesize() sysconf(_SC_OPEN_MAX)
599 static size_t
600 GetHighResClock(void *buf, size_t maxbytes)
602 int ticks;
603 struct tms buffer;
605 ticks=times(&buffer);
606 return CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks));
609 static void
610 GiveSystemInfo(void)
612 int rv;
613 char buf[2000];
615 rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
616 if (rv > 0) {
617 RNG_RandomUpdate(buf, rv);
619 rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
620 if (rv > 0) {
621 RNG_RandomUpdate(buf, rv);
623 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
624 if (rv > 0) {
625 RNG_RandomUpdate(buf, rv);
628 #endif /* sinix */
630 #if defined(VMS)
631 #include <c_asm.h>
633 static void
634 GiveSystemInfo(void)
636 long si;
639 * This is copied from the SCO/UNIXWARE etc section. And like the comment
640 * there says, what's the point? This isn't random, it generates the same
641 * stuff every time its run!
643 si = sysconf(_SC_CHILD_MAX);
644 RNG_RandomUpdate(&si, sizeof(si));
646 si = sysconf(_SC_STREAM_MAX);
647 RNG_RandomUpdate(&si, sizeof(si));
649 si = sysconf(_SC_OPEN_MAX);
650 RNG_RandomUpdate(&si, sizeof(si));
654 * Use the "get the cycle counter" instruction on the alpha.
655 * The low 32 bits completely turn over in less than a minute.
656 * The high 32 bits are some non-counter gunk that changes sometimes.
658 static size_t
659 GetHighResClock(void *buf, size_t maxbytes)
661 unsigned long t;
663 t = asm("rpcc %v0");
664 return CopyLowBits(buf, maxbytes, &t, sizeof(t));
667 #endif /* VMS */
669 #ifdef BEOS
670 #include <be/kernel/OS.h>
672 static size_t
673 GetHighResClock(void *buf, size_t maxbytes)
675 bigtime_t bigtime; /* Actually an int64 */
677 bigtime = real_time_clock_usecs();
678 return CopyLowBits(buf, maxbytes, &bigtime, sizeof(bigtime));
681 static void
682 GiveSystemInfo(void)
684 system_info *info = NULL;
685 int32 val;
686 get_system_info(info);
687 if (info) {
688 val = info->boot_time;
689 RNG_RandomUpdate(&val, sizeof(val));
690 val = info->used_pages;
691 RNG_RandomUpdate(&val, sizeof(val));
692 val = info->used_ports;
693 RNG_RandomUpdate(&val, sizeof(val));
694 val = info->used_threads;
695 RNG_RandomUpdate(&val, sizeof(val));
696 val = info->used_teams;
697 RNG_RandomUpdate(&val, sizeof(val));
700 #endif /* BEOS */
702 #if defined(nec_ews)
703 #include <sys/systeminfo.h>
705 #define getdtablesize() sysconf(_SC_OPEN_MAX)
707 static size_t
708 GetHighResClock(void *buf, size_t maxbytes)
710 return 0;
713 static void
714 GiveSystemInfo(void)
716 int rv;
717 char buf[2000];
719 rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
720 if (rv > 0) {
721 RNG_RandomUpdate(buf, rv);
723 rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
724 if (rv > 0) {
725 RNG_RandomUpdate(buf, rv);
727 rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
728 if (rv > 0) {
729 RNG_RandomUpdate(buf, rv);
732 #endif /* nec_ews */
734 size_t RNG_GetNoise(void *buf, size_t maxbytes)
736 struct timeval tv;
737 int n = 0;
738 int c;
740 n = GetHighResClock(buf, maxbytes);
741 maxbytes -= n;
743 #if defined(__sun) && (defined(_svr4) || defined(SVR4)) || defined(sony)
744 (void)gettimeofday(&tv);
745 #else
746 (void)gettimeofday(&tv, 0);
747 #endif
748 c = CopyLowBits((char*)buf+n, maxbytes, &tv.tv_usec, sizeof(tv.tv_usec));
749 n += c;
750 maxbytes -= c;
751 c = CopyLowBits((char*)buf+n, maxbytes, &tv.tv_sec, sizeof(tv.tv_sec));
752 n += c;
753 return n;
756 #define SAFE_POPEN_MAXARGS 10 /* must be at least 2 */
759 * safe_popen is static to this module and we know what arguments it is
760 * called with. Note that this version only supports a single open child
761 * process at any time.
763 static pid_t safe_popen_pid;
764 static struct sigaction oldact;
766 static FILE *
767 safe_popen(char *cmd)
769 int p[2], fd, argc;
770 pid_t pid;
771 char *argv[SAFE_POPEN_MAXARGS + 1];
772 FILE *fp;
773 static char blank[] = " \t";
774 static struct sigaction newact;
776 if (pipe(p) < 0)
777 return 0;
779 fp = fdopen(p[0], "r");
780 if (fp == 0) {
781 close(p[0]);
782 close(p[1]);
783 return 0;
786 /* Setup signals so that SIGCHLD is ignored as we want to do waitpid */
787 newact.sa_handler = SIG_DFL;
788 newact.sa_flags = 0;
789 sigfillset(&newact.sa_mask);
790 sigaction (SIGCHLD, &newact, &oldact);
792 pid = fork();
793 switch (pid) {
794 int ndesc;
796 case -1:
797 fclose(fp); /* this closes p[0], the fd associated with fp */
798 close(p[1]);
799 sigaction (SIGCHLD, &oldact, NULL);
800 return 0;
802 case 0:
803 /* dup write-side of pipe to stderr and stdout */
804 if (p[1] != 1) dup2(p[1], 1);
805 if (p[1] != 2) dup2(p[1], 2);
808 * close the other file descriptors, except stdin which we
809 * try reassociating with /dev/null, first (bug 174993)
811 if (!freopen("/dev/null", "r", stdin))
812 close(0);
813 ndesc = getdtablesize();
814 for (fd = PR_MIN(65536, ndesc); --fd > 2; close(fd));
816 /* clean up environment in the child process */
817 putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc:/usr/etc");
818 putenv("SHELL=/bin/sh");
819 putenv("IFS= \t");
822 * The caller may have passed us a string that is in text
823 * space. It may be illegal to modify the string
825 cmd = strdup(cmd);
826 /* format argv */
827 argv[0] = strtok(cmd, blank);
828 argc = 1;
829 while ((argv[argc] = strtok(0, blank)) != 0) {
830 if (++argc == SAFE_POPEN_MAXARGS) {
831 argv[argc] = 0;
832 break;
836 /* and away we go */
837 execvp(argv[0], argv);
838 exit(127);
839 break;
841 default:
842 close(p[1]);
843 break;
846 /* non-zero means there's a cmd running */
847 safe_popen_pid = pid;
848 return fp;
851 static int
852 safe_pclose(FILE *fp)
854 pid_t pid;
855 int status = -1, rv;
857 if ((pid = safe_popen_pid) == 0)
858 return -1;
859 safe_popen_pid = 0;
861 fclose(fp);
863 /* yield the processor so the child gets some time to exit normally */
864 PR_Sleep(PR_INTERVAL_NO_WAIT);
866 /* if the child hasn't exited, kill it -- we're done with its output */
867 while ((rv = waitpid(pid, &status, WNOHANG)) == -1 && errno == EINTR)
869 if (rv == 0) {
870 kill(pid, SIGKILL);
871 while ((rv = waitpid(pid, &status, 0)) == -1 && errno == EINTR)
875 /* Reset SIGCHLD signal hander before returning */
876 sigaction(SIGCHLD, &oldact, NULL);
878 return status;
882 #if !defined(VMS)
884 #ifdef DARWIN
885 #include <crt_externs.h>
886 #endif
888 /* Fork netstat to collect its output by default. Do not unset this unless
889 * another source of entropy is available
891 #define DO_NETSTAT 1
893 void RNG_SystemInfoForRNG(void)
895 FILE *fp;
896 char buf[BUFSIZ];
897 size_t bytes;
898 const char * const *cp;
899 char *randfile;
900 #ifdef DARWIN
901 char **environ = *_NSGetEnviron();
902 #else
903 extern char **environ;
904 #endif
905 #ifdef BEOS
906 static const char * const files[] = {
907 "/boot/var/swap",
908 "/boot/var/log/syslog",
909 "/boot/var/tmp",
910 "/boot/home/config/settings",
911 "/boot/home",
914 #else
915 static const char * const files[] = {
916 "/etc/passwd",
917 "/etc/utmp",
918 "/tmp",
919 "/var/tmp",
920 "/usr/tmp",
923 #endif
925 #if defined(BSDI)
926 static char netstat_ni_cmd[] = "netstat -nis";
927 #else
928 static char netstat_ni_cmd[] = "netstat -ni";
929 #endif
931 GiveSystemInfo();
933 bytes = RNG_GetNoise(buf, sizeof(buf));
934 RNG_RandomUpdate(buf, bytes);
937 * Pass the C environment and the addresses of the pointers to the
938 * hash function. This makes the random number function depend on the
939 * execution environment of the user and on the platform the program
940 * is running on.
942 if (environ != NULL) {
943 cp = (const char * const *) environ;
944 while (*cp) {
945 RNG_RandomUpdate(*cp, strlen(*cp));
946 cp++;
948 RNG_RandomUpdate(environ, (char*)cp - (char*)environ);
951 /* Give in system information */
952 if (gethostname(buf, sizeof(buf)) == 0) {
953 RNG_RandomUpdate(buf, strlen(buf));
955 GiveSystemInfo();
957 /* grab some data from system's PRNG before any other files. */
958 bytes = RNG_FileUpdate("/dev/urandom", SYSTEM_RNG_SEED_COUNT);
960 /* If the user points us to a random file, pass it through the rng */
961 randfile = getenv("NSRANDFILE");
962 if ( ( randfile != NULL ) && ( randfile[0] != '\0') ) {
963 RNG_FileForRNG(randfile);
966 /* pass other files through */
967 for (cp = files; *cp; cp++)
968 RNG_FileForRNG(*cp);
971 * Bug 100447: On BSD/OS 4.2 and 4.3, we have problem calling safe_popen
972 * in a pthreads environment. Therefore, we call safe_popen last and on
973 * BSD/OS we do not call safe_popen when we succeeded in getting data
974 * from /dev/urandom.
976 * Bug 174993: LINUX provides /dev/urandom, don't fork netstat
977 * if data has been gathered successfully
980 #if defined(BSDI) || defined(LINUX)
981 if (bytes)
982 return;
983 #endif
985 #ifdef SOLARIS
988 * On Solaris, NSS may be initialized automatically from libldap in
989 * applications that are unaware of the use of NSS. safe_popen forks, and
990 * sometimes creates issues with some applications' pthread_atfork handlers.
991 * We always have /dev/urandom on Solaris 9 and above as an entropy source,
992 * and for Solaris 8 we have the libkstat interface, so we don't need to
993 * fork netstat.
996 #undef DO_NETSTAT
997 if (!bytes) {
998 /* On Solaris 8, /dev/urandom isn't available, so we use libkstat. */
999 PRUint32 kstat_bytes = 0;
1000 if (SECSuccess != RNG_kstat(&kstat_bytes)) {
1001 PORT_Assert(0);
1003 bytes += kstat_bytes;
1004 PORT_Assert(bytes);
1006 #endif
1008 #ifdef DO_NETSTAT
1009 fp = safe_popen(netstat_ni_cmd);
1010 if (fp != NULL) {
1011 while ((bytes = fread(buf, 1, sizeof(buf), fp)) > 0)
1012 RNG_RandomUpdate(buf, bytes);
1013 safe_pclose(fp);
1015 #endif
1018 #else
1019 void RNG_SystemInfoForRNG(void)
1021 FILE *fp;
1022 char buf[BUFSIZ];
1023 size_t bytes;
1024 int extra;
1025 char **cp;
1026 extern char **environ;
1027 char *randfile;
1029 GiveSystemInfo();
1031 bytes = RNG_GetNoise(buf, sizeof(buf));
1032 RNG_RandomUpdate(buf, bytes);
1035 * Pass the C environment and the addresses of the pointers to the
1036 * hash function. This makes the random number function depend on the
1037 * execution environment of the user and on the platform the program
1038 * is running on.
1040 cp = environ;
1041 while (*cp) {
1042 RNG_RandomUpdate(*cp, strlen(*cp));
1043 cp++;
1045 RNG_RandomUpdate(environ, (char*)cp - (char*)environ);
1047 /* Give in system information */
1048 if (gethostname(buf, sizeof(buf)) > 0) {
1049 RNG_RandomUpdate(buf, strlen(buf));
1051 GiveSystemInfo();
1053 /* If the user points us to a random file, pass it through the rng */
1054 randfile = getenv("NSRANDFILE");
1055 if ( ( randfile != NULL ) && ( randfile[0] != '\0') ) {
1056 RNG_FileForRNG(randfile);
1060 ** We need to generate at least 1024 bytes of seed data. Since we don't
1061 ** do the file stuff for VMS, and because the environ list is so short
1062 ** on VMS, we need to make sure we generate enough. So do another 1000
1063 ** bytes to be sure.
1065 extra = 1000;
1066 while (extra > 0) {
1067 cp = environ;
1068 while (*cp) {
1069 int n = strlen(*cp);
1070 RNG_RandomUpdate(*cp, n);
1071 extra -= n;
1072 cp++;
1076 #endif
1078 #define TOTAL_FILE_LIMIT 1000000 /* one million */
1080 size_t RNG_FileUpdate(const char *fileName, size_t limit)
1082 FILE * file;
1083 size_t bytes;
1084 size_t fileBytes = 0;
1085 struct stat stat_buf;
1086 unsigned char buffer[BUFSIZ];
1087 static size_t totalFileBytes = 0;
1089 /* suppress valgrind warnings due to holes in struct stat */
1090 memset(&stat_buf, 0, sizeof(stat_buf));
1092 if (stat((char *)fileName, &stat_buf) < 0)
1093 return fileBytes;
1094 RNG_RandomUpdate(&stat_buf, sizeof(stat_buf));
1096 file = fopen((char *)fileName, "r");
1097 if (file != NULL) {
1098 while (limit > fileBytes) {
1099 bytes = PR_MIN(sizeof buffer, limit - fileBytes);
1100 bytes = fread(buffer, 1, bytes, file);
1101 if (bytes == 0)
1102 break;
1103 RNG_RandomUpdate(buffer, bytes);
1104 fileBytes += bytes;
1105 totalFileBytes += bytes;
1106 /* after TOTAL_FILE_LIMIT has been reached, only read in first
1107 ** buffer of data from each subsequent file.
1109 if (totalFileBytes > TOTAL_FILE_LIMIT)
1110 break;
1112 fclose(file);
1115 * Pass yet another snapshot of our highest resolution clock into
1116 * the hash function.
1118 bytes = RNG_GetNoise(buffer, sizeof(buffer));
1119 RNG_RandomUpdate(buffer, bytes);
1120 return fileBytes;
1123 void RNG_FileForRNG(const char *fileName)
1125 RNG_FileUpdate(fileName, TOTAL_FILE_LIMIT);
1128 size_t RNG_SystemRNG(void *dest, size_t maxLen)
1130 FILE *file;
1131 size_t bytes;
1132 size_t fileBytes = 0;
1133 unsigned char *buffer = dest;
1135 file = fopen("/dev/urandom", "r");
1136 if (file == NULL) {
1137 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
1138 return fileBytes;
1140 while (maxLen > fileBytes) {
1141 bytes = maxLen - fileBytes;
1142 bytes = fread(buffer, 1, bytes, file);
1143 if (bytes == 0)
1144 break;
1145 fileBytes += bytes;
1146 buffer += bytes;
1148 fclose(file);
1149 if (fileBytes != maxLen) {
1150 PORT_SetError(SEC_ERROR_NEED_RANDOM); /* system RNG failed */
1151 fileBytes = 0;
1153 return fileBytes;