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