Back to index

lightning-sunbird  0.9+nobinonly
unix_rand.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include <stdio.h>
00038 #include <string.h>
00039 #include <signal.h>
00040 #include <unistd.h>
00041 #include <errno.h>
00042 #include <stdlib.h>
00043 #include <sys/time.h>
00044 #include <sys/wait.h>
00045 #include <sys/stat.h>
00046 #include "secrng.h"
00047 #include "secerr.h"
00048 #include "prerror.h"
00049 #include "prthread.h"
00050 
00051 size_t RNG_FileUpdate(const char *fileName, size_t limit);
00052 
00053 /*
00054  * When copying data to the buffer we want the least signicant bytes
00055  * from the input since those bits are changing the fastest. The address
00056  * of least significant byte depends upon whether we are running on
00057  * a big-endian or little-endian machine.
00058  *
00059  * Does this mean the least signicant bytes are the most significant
00060  * to us? :-)
00061  */
00062     
00063 static size_t CopyLowBits(void *dst, size_t dstlen, void *src, size_t srclen)
00064 {
00065     union endianness {
00066        int32 i;
00067        char c[4];
00068     } u;
00069 
00070     if (srclen <= dstlen) {
00071        memcpy(dst, src, srclen);
00072        return srclen;
00073     }
00074     u.i = 0x01020304;
00075     if (u.c[0] == 0x01) {
00076        /* big-endian case */
00077        memcpy(dst, (char*)src + (srclen - dstlen), dstlen);
00078     } else {
00079        /* little-endian case */
00080        memcpy(dst, src, dstlen);
00081     }
00082     return dstlen;
00083 }
00084 
00085 #ifdef SOLARIS
00086 
00087 #include <kstat.h>
00088 
00089 static const PRUint32 entropy_buf_len = 4096; /* buffer up to 4 KB */
00090 
00091 /* Buffer entropy data, and feed it to the RNG, entropy_buf_len bytes at a time.
00092  * Returns error if RNG_RandomUpdate fails. Also increments *total_fed
00093  * by the number of bytes successfully buffered.
00094  */
00095 static SECStatus BufferEntropy(char* inbuf, PRUint32 inlen,
00096                                 char* entropy_buf, PRUint32* entropy_buffered,
00097                                 PRUint32* total_fed)
00098 {
00099     PRUint32 tocopy = 0;
00100     PRUint32 avail = 0;
00101     SECStatus rv = SECSuccess;
00102 
00103     while (inlen) {
00104         avail = entropy_buf_len - *entropy_buffered;
00105         if (!avail) {
00106             /* Buffer is full, time to feed it to the RNG. */
00107             rv = RNG_RandomUpdate(entropy_buf, entropy_buf_len);
00108             if (SECSuccess != rv) {
00109                 break;
00110             }
00111             *entropy_buffered = 0;
00112             avail = entropy_buf_len;
00113         }
00114         tocopy = PR_MIN(avail, inlen);
00115         memcpy(entropy_buf + *entropy_buffered, inbuf, tocopy);
00116         *entropy_buffered += tocopy;
00117         inlen -= tocopy;
00118         inbuf += tocopy;
00119         *total_fed += tocopy;
00120     }
00121     return rv;
00122 }
00123 
00124 /* Feed kernel statistics structures and ks_data field to the RNG.
00125  * Returns status as well as the number of bytes successfully fed to the RNG.
00126  */
00127 static SECStatus RNG_kstat(PRUint32* fed)
00128 {
00129     kstat_ctl_t*    kc = NULL;
00130     kstat_t*        ksp = NULL;
00131     PRUint32        entropy_buffered = 0;
00132     char*           entropy_buf = NULL;
00133     SECStatus       rv = SECSuccess;
00134 
00135     PORT_Assert(fed);
00136     if (!fed) {
00137         return SECFailure;
00138     }
00139     *fed = 0;
00140 
00141     kc = kstat_open();
00142     PORT_Assert(kc);
00143     if (!kc) {
00144         return SECFailure;
00145     }
00146     entropy_buf = (char*) PORT_Alloc(entropy_buf_len);
00147     PORT_Assert(entropy_buf);
00148     if (entropy_buf) {
00149         for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
00150             if (-1 == kstat_read(kc, ksp, NULL)) {
00151                 /* missing data from a single kstat shouldn't be fatal */
00152                 continue;
00153             }
00154             rv = BufferEntropy((char*)ksp, sizeof(kstat_t),
00155                                     entropy_buf, &entropy_buffered,
00156                                     fed);
00157             if (SECSuccess != rv) {
00158                 break;
00159             }
00160 
00161             if (ksp->ks_data && ksp->ks_data_size>0 && ksp->ks_ndata>0) {
00162                 rv = BufferEntropy((char*)ksp->ks_data, ksp->ks_data_size,
00163                                         entropy_buf, &entropy_buffered,
00164                                         fed);
00165                 if (SECSuccess != rv) {
00166                     break;
00167                 }
00168             }
00169         }
00170         if (SECSuccess == rv && entropy_buffered) {
00171             /* Buffer is not empty, time to feed it to the RNG */
00172             rv = RNG_RandomUpdate(entropy_buf, entropy_buffered);
00173         }
00174         PORT_Free(entropy_buf);
00175     } else {
00176         rv = SECFailure;
00177     }
00178     if (kstat_close(kc)) {
00179         PORT_Assert(0);
00180         rv = SECFailure;
00181     }
00182     return rv;
00183 }
00184 
00185 #endif
00186 
00187 #if defined(SCO) || defined(UNIXWARE) || defined(BSDI) || defined(FREEBSD) \
00188     || defined(NETBSD) || defined(NTO) || defined(DARWIN) || defined(OPENBSD)
00189 #include <sys/times.h>
00190 
00191 #define getdtablesize() sysconf(_SC_OPEN_MAX)
00192 
00193 static size_t
00194 GetHighResClock(void *buf, size_t maxbytes)
00195 {
00196     int ticks;
00197     struct tms buffer;
00198 
00199     ticks=times(&buffer);
00200     return CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks));
00201 }
00202 
00203 static void
00204 GiveSystemInfo(void)
00205 {
00206     long si;
00207 
00208     /* 
00209      * Is this really necessary?  Why not use rand48 or something?
00210      */
00211     si = sysconf(_SC_CHILD_MAX);
00212     RNG_RandomUpdate(&si, sizeof(si));
00213 
00214     si = sysconf(_SC_STREAM_MAX);
00215     RNG_RandomUpdate(&si, sizeof(si));
00216 
00217     si = sysconf(_SC_OPEN_MAX);
00218     RNG_RandomUpdate(&si, sizeof(si));
00219 }
00220 #endif
00221 
00222 #if defined(__sun)
00223 #if defined(__svr4) || defined(SVR4)
00224 #include <sys/systeminfo.h>
00225 #include <sys/times.h>
00226 #include <wait.h>
00227 
00228 int gettimeofday(struct timeval *);
00229 int gethostname(char *, int);
00230 
00231 #define getdtablesize() sysconf(_SC_OPEN_MAX)
00232 
00233 static void
00234 GiveSystemInfo(void)
00235 {
00236     int rv;
00237     char buf[2000];
00238 
00239     rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
00240     if (rv > 0) {
00241        RNG_RandomUpdate(buf, rv);
00242     }
00243     rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
00244     if (rv > 0) {
00245        RNG_RandomUpdate(buf, rv);
00246     }
00247     rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
00248     if (rv > 0) {
00249        RNG_RandomUpdate(buf, rv);
00250     }
00251 }
00252 
00253 static size_t
00254 GetHighResClock(void *buf, size_t maxbytes)
00255 {
00256     hrtime_t t;
00257     t = gethrtime();
00258     if (t) {
00259        return CopyLowBits(buf, maxbytes, &t, sizeof(t));
00260     }
00261     return 0;
00262 }
00263 #else /* SunOS (Sun, but not SVR4) */
00264 
00265 extern long sysconf(int name);
00266 
00267 static size_t
00268 GetHighResClock(void *buf, size_t maxbytes)
00269 {
00270     return 0;
00271 }
00272 
00273 static void
00274 GiveSystemInfo(void)
00275 {
00276     long si;
00277 
00278     /* This is not very good */
00279     si = sysconf(_SC_CHILD_MAX);
00280     RNG_RandomUpdate(&si, sizeof(si));
00281 }
00282 #endif
00283 #endif /* Sun */
00284 
00285 #if defined(__hpux)
00286 #include <sys/unistd.h>
00287 
00288 #define getdtablesize() sysconf(_SC_OPEN_MAX)
00289 
00290 #if defined(__ia64)
00291 #include <ia64/sys/inline.h>
00292 
00293 static size_t
00294 GetHighResClock(void *buf, size_t maxbytes)
00295 {
00296     PRUint64 t;
00297 
00298     t = _Asm_mov_from_ar(_AREG44);
00299     return CopyLowBits(buf, maxbytes, &t, sizeof(t));
00300 }
00301 #else
00302 static size_t
00303 GetHighResClock(void *buf, size_t maxbytes)
00304 {
00305     extern int ret_cr16();
00306     int cr16val;
00307 
00308     cr16val = ret_cr16();
00309     return CopyLowBits(buf, maxbytes, &cr16val, sizeof(cr16val));
00310 }
00311 #endif
00312 
00313 static void
00314 GiveSystemInfo(void)
00315 {
00316     long si;
00317 
00318     /* This is not very good */
00319     si = sysconf(_AES_OS_VERSION);
00320     RNG_RandomUpdate(&si, sizeof(si));
00321     si = sysconf(_SC_CPU_VERSION);
00322     RNG_RandomUpdate(&si, sizeof(si));
00323 }
00324 #endif /* HPUX */
00325 
00326 #if defined(OSF1)
00327 #include <sys/types.h>
00328 #include <sys/sysinfo.h>
00329 #include <sys/systeminfo.h>
00330 #include <c_asm.h>
00331 
00332 static void
00333 GiveSystemInfo(void)
00334 {
00335     char buf[BUFSIZ];
00336     int rv;
00337     int off = 0;
00338 
00339     rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
00340     if (rv > 0) {
00341        RNG_RandomUpdate(buf, rv);
00342     }
00343     rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
00344     if (rv > 0) {
00345        RNG_RandomUpdate(buf, rv);
00346     }
00347     rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
00348     if (rv > 0) {
00349        RNG_RandomUpdate(buf, rv);
00350     }
00351 }
00352 
00353 /*
00354  * Use the "get the cycle counter" instruction on the alpha.
00355  * The low 32 bits completely turn over in less than a minute.
00356  * The high 32 bits are some non-counter gunk that changes sometimes.
00357  */
00358 static size_t
00359 GetHighResClock(void *buf, size_t maxbytes)
00360 {
00361     unsigned long t;
00362 
00363     t = asm("rpcc %v0");
00364     return CopyLowBits(buf, maxbytes, &t, sizeof(t));
00365 }
00366 
00367 #endif /* Alpha */
00368 
00369 #if defined(_IBMR2)
00370 static size_t
00371 GetHighResClock(void *buf, size_t maxbytes)
00372 {
00373     return 0;
00374 }
00375 
00376 static void
00377 GiveSystemInfo(void)
00378 {
00379     /* XXX haven't found any yet! */
00380 }
00381 #endif /* IBM R2 */
00382 
00383 #if defined(LINUX)
00384 #include <sys/sysinfo.h>
00385 
00386 static size_t
00387 GetHighResClock(void *buf, size_t maxbytes)
00388 {
00389     return 0;
00390 }
00391 
00392 static void
00393 GiveSystemInfo(void)
00394 {
00395     struct sysinfo si;
00396     if (sysinfo(&si) == 0) {
00397        RNG_RandomUpdate(&si, sizeof(si));
00398     }
00399 }
00400 #endif /* LINUX */
00401 
00402 #if defined(NCR)
00403 
00404 #include <sys/utsname.h>
00405 #include <sys/systeminfo.h>
00406 
00407 #define getdtablesize() sysconf(_SC_OPEN_MAX)
00408 
00409 static size_t
00410 GetHighResClock(void *buf, size_t maxbytes)
00411 {
00412     return 0;
00413 }
00414 
00415 static void
00416 GiveSystemInfo(void)
00417 {
00418     int rv;
00419     char buf[2000];
00420 
00421     rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
00422     if (rv > 0) {
00423        RNG_RandomUpdate(buf, rv);
00424     }
00425     rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
00426     if (rv > 0) {
00427        RNG_RandomUpdate(buf, rv);
00428     }
00429     rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
00430     if (rv > 0) {
00431        RNG_RandomUpdate(buf, rv);
00432     }
00433 }
00434 
00435 #endif /* NCR */
00436 
00437 
00438 #if defined(sgi)
00439 #include <fcntl.h>
00440 #undef PRIVATE
00441 #include <sys/mman.h>
00442 #include <sys/syssgi.h>
00443 #include <sys/immu.h>
00444 #include <sys/systeminfo.h>
00445 #include <sys/utsname.h>
00446 #include <wait.h>
00447 
00448 static void
00449 GiveSystemInfo(void)
00450 {
00451     int rv;
00452     char buf[4096];
00453 
00454     rv = syssgi(SGI_SYSID, &buf[0]);
00455     if (rv > 0) {
00456        RNG_RandomUpdate(buf, MAXSYSIDSIZE);
00457     }
00458 #ifdef SGI_RDUBLK
00459     rv = syssgi(SGI_RDUBLK, getpid(), &buf[0], sizeof(buf));
00460     if (rv > 0) {
00461        RNG_RandomUpdate(buf, sizeof(buf));
00462     }
00463 #endif /* SGI_RDUBLK */
00464     rv = syssgi(SGI_INVENT, SGI_INV_READ, buf, sizeof(buf));
00465     if (rv > 0) {
00466        RNG_RandomUpdate(buf, sizeof(buf));
00467     }
00468     rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
00469     if (rv > 0) {
00470        RNG_RandomUpdate(buf, rv);
00471     }
00472     rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
00473     if (rv > 0) {
00474        RNG_RandomUpdate(buf, rv);
00475     }
00476     rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
00477     if (rv > 0) {
00478        RNG_RandomUpdate(buf, rv);
00479     }
00480 }
00481 
00482 static size_t GetHighResClock(void *buf, size_t maxbuf)
00483 {
00484     unsigned phys_addr, raddr, cycleval;
00485     static volatile unsigned *iotimer_addr = NULL;
00486     static int tries = 0;
00487     static int cntr_size;
00488     int mfd;
00489     long s0[2];
00490     struct timeval tv;
00491 
00492 #ifndef SGI_CYCLECNTR_SIZE
00493 #define SGI_CYCLECNTR_SIZE      165     /* Size user needs to use to read CC */
00494 #endif
00495 
00496     if (iotimer_addr == NULL) {
00497        if (tries++ > 1) {
00498            /* Don't keep trying if it didn't work */
00499            return 0;
00500        }
00501 
00502        /*
00503        ** For SGI machines we can use the cycle counter, if it has one,
00504        ** to generate some truly random numbers
00505        */
00506        phys_addr = syssgi(SGI_QUERY_CYCLECNTR, &cycleval);
00507        if (phys_addr) {
00508            int pgsz = getpagesize();
00509            int pgoffmask = pgsz - 1;
00510 
00511            raddr = phys_addr & ~pgoffmask;
00512            mfd = open("/dev/mmem", O_RDONLY);
00513            if (mfd < 0) {
00514               return 0;
00515            }
00516            iotimer_addr = (unsigned *)
00517               mmap(0, pgoffmask, PROT_READ, MAP_PRIVATE, mfd, (int)raddr);
00518            if (iotimer_addr == (void*)-1) {
00519               close(mfd);
00520               iotimer_addr = NULL;
00521               return 0;
00522            }
00523            iotimer_addr = (unsigned*)
00524               ((__psint_t)iotimer_addr | (phys_addr & pgoffmask));
00525            /*
00526             * The file 'mfd' is purposefully not closed.
00527             */
00528            cntr_size = syssgi(SGI_CYCLECNTR_SIZE);
00529            if (cntr_size < 0) {
00530               struct utsname utsinfo;
00531 
00532               /* 
00533                * We must be executing on a 6.0 or earlier system, since the
00534                * SGI_CYCLECNTR_SIZE call is not supported.
00535                * 
00536                * The only pre-6.1 platforms with 64-bit counters are
00537                * IP19 and IP21 (Challenge, PowerChallenge, Onyx).
00538                */
00539               uname(&utsinfo);
00540               if (!strncmp(utsinfo.machine, "IP19", 4) ||
00541                   !strncmp(utsinfo.machine, "IP21", 4))
00542                      cntr_size = 64;
00543               else
00544                      cntr_size = 32;
00545            }
00546            cntr_size /= 8;  /* Convert from bits to bytes */
00547        }
00548     }
00549 
00550     s0[0] = *iotimer_addr;
00551     if (cntr_size > 4)
00552        s0[1] = *(iotimer_addr + 1);
00553     memcpy(buf, (char *)&s0[0], cntr_size);
00554     return CopyLowBits(buf, maxbuf, &s0, cntr_size);
00555 }
00556 #endif
00557 
00558 #if defined(sony)
00559 #include <sys/systeminfo.h>
00560 
00561 #define getdtablesize() sysconf(_SC_OPEN_MAX)
00562 
00563 static size_t
00564 GetHighResClock(void *buf, size_t maxbytes)
00565 {
00566     return 0;
00567 }
00568 
00569 static void
00570 GiveSystemInfo(void)
00571 {
00572     int rv;
00573     char buf[2000];
00574 
00575     rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
00576     if (rv > 0) {
00577        RNG_RandomUpdate(buf, rv);
00578     }
00579     rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
00580     if (rv > 0) {
00581        RNG_RandomUpdate(buf, rv);
00582     }
00583     rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
00584     if (rv > 0) {
00585        RNG_RandomUpdate(buf, rv);
00586     }
00587 }
00588 #endif /* sony */
00589 
00590 #if defined(sinix)
00591 #include <sys/systeminfo.h>
00592 #include <sys/times.h>
00593 
00594 int gettimeofday(struct timeval *, struct timezone *);
00595 int gethostname(char *, int);
00596 
00597 #define getdtablesize() sysconf(_SC_OPEN_MAX)
00598 
00599 static size_t
00600 GetHighResClock(void *buf, size_t maxbytes)
00601 {
00602     int ticks;
00603     struct tms buffer;
00604 
00605     ticks=times(&buffer);
00606     return CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks));
00607 }
00608 
00609 static void
00610 GiveSystemInfo(void)
00611 {
00612     int rv;
00613     char buf[2000];
00614 
00615     rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
00616     if (rv > 0) {
00617        RNG_RandomUpdate(buf, rv);
00618     }
00619     rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
00620     if (rv > 0) {
00621        RNG_RandomUpdate(buf, rv);
00622     }
00623     rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
00624     if (rv > 0) {
00625        RNG_RandomUpdate(buf, rv);
00626     }
00627 }
00628 #endif /* sinix */
00629 
00630 #if defined(VMS)
00631 #include <c_asm.h>
00632 
00633 static void
00634 GiveSystemInfo(void)
00635 {
00636     long si;
00637  
00638     /* 
00639      * This is copied from the SCO/UNIXWARE etc section. And like the comment
00640      * there says, what's the point? This isn't random, it generates the same
00641      * stuff every time its run!
00642      */
00643     si = sysconf(_SC_CHILD_MAX);
00644     RNG_RandomUpdate(&si, sizeof(si));
00645  
00646     si = sysconf(_SC_STREAM_MAX);
00647     RNG_RandomUpdate(&si, sizeof(si));
00648  
00649     si = sysconf(_SC_OPEN_MAX);
00650     RNG_RandomUpdate(&si, sizeof(si));
00651 }
00652  
00653 /*
00654  * Use the "get the cycle counter" instruction on the alpha.
00655  * The low 32 bits completely turn over in less than a minute.
00656  * The high 32 bits are some non-counter gunk that changes sometimes.
00657  */
00658 static size_t
00659 GetHighResClock(void *buf, size_t maxbytes)
00660 {
00661     unsigned long t;
00662  
00663     t = asm("rpcc %v0");
00664     return CopyLowBits(buf, maxbytes, &t, sizeof(t));
00665 }
00666  
00667 #endif /* VMS */
00668 
00669 #ifdef BEOS
00670 #include <be/kernel/OS.h>
00671 
00672 static size_t
00673 GetHighResClock(void *buf, size_t maxbytes)
00674 {
00675     bigtime_t bigtime; /* Actually an int64 */
00676 
00677     bigtime = real_time_clock_usecs();
00678     return CopyLowBits(buf, maxbytes, &bigtime, sizeof(bigtime));
00679 }
00680 
00681 static void
00682 GiveSystemInfo(void)
00683 {
00684     system_info *info = NULL;
00685     int32 val;                     
00686     get_system_info(info);
00687     if (info) {
00688         val = info->boot_time;
00689         RNG_RandomUpdate(&val, sizeof(val));
00690         val = info->used_pages;
00691         RNG_RandomUpdate(&val, sizeof(val));
00692         val = info->used_ports;
00693         RNG_RandomUpdate(&val, sizeof(val));
00694         val = info->used_threads;
00695         RNG_RandomUpdate(&val, sizeof(val));
00696         val = info->used_teams;
00697         RNG_RandomUpdate(&val, sizeof(val));
00698     }
00699 }
00700 #endif /* BEOS */
00701 
00702 #if defined(nec_ews)
00703 #include <sys/systeminfo.h>
00704 
00705 #define getdtablesize() sysconf(_SC_OPEN_MAX)
00706 
00707 static size_t
00708 GetHighResClock(void *buf, size_t maxbytes)
00709 {
00710     return 0;
00711 }
00712 
00713 static void
00714 GiveSystemInfo(void)
00715 {
00716     int rv;
00717     char buf[2000];
00718 
00719     rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
00720     if (rv > 0) {
00721        RNG_RandomUpdate(buf, rv);
00722     }
00723     rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
00724     if (rv > 0) {
00725        RNG_RandomUpdate(buf, rv);
00726     }
00727     rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
00728     if (rv > 0) {
00729        RNG_RandomUpdate(buf, rv);
00730     }
00731 }
00732 #endif /* nec_ews */
00733 
00734 size_t RNG_GetNoise(void *buf, size_t maxbytes)
00735 {
00736     struct timeval tv;
00737     int n = 0;
00738     int c;
00739 
00740     n = GetHighResClock(buf, maxbytes);
00741     maxbytes -= n;
00742 
00743 #if defined(__sun) && (defined(_svr4) || defined(SVR4)) || defined(sony)
00744     (void)gettimeofday(&tv);
00745 #else
00746     (void)gettimeofday(&tv, 0);
00747 #endif
00748     c = CopyLowBits((char*)buf+n, maxbytes, &tv.tv_usec, sizeof(tv.tv_usec));
00749     n += c;
00750     maxbytes -= c;
00751     c = CopyLowBits((char*)buf+n, maxbytes, &tv.tv_sec, sizeof(tv.tv_sec));
00752     n += c;
00753     return n;
00754 }
00755 
00756 #define SAFE_POPEN_MAXARGS  10     /* must be at least 2 */
00757 
00758 /*
00759  * safe_popen is static to this module and we know what arguments it is
00760  * called with. Note that this version only supports a single open child
00761  * process at any time.
00762  */
00763 static pid_t safe_popen_pid;
00764 static struct sigaction oldact;
00765 
00766 static FILE *
00767 safe_popen(char *cmd)
00768 {
00769     int p[2], fd, argc;
00770     pid_t pid;
00771     char *argv[SAFE_POPEN_MAXARGS + 1];
00772     FILE *fp;
00773     static char blank[] = " \t";
00774     static struct sigaction newact;
00775 
00776     if (pipe(p) < 0)
00777        return 0;
00778 
00779     fp = fdopen(p[0], "r");
00780     if (fp == 0) {
00781        close(p[0]);
00782        close(p[1]);
00783        return 0;
00784     }
00785 
00786     /* Setup signals so that SIGCHLD is ignored as we want to do waitpid */
00787     newact.sa_handler = SIG_DFL;
00788     newact.sa_flags = 0;
00789     sigfillset(&newact.sa_mask);
00790     sigaction (SIGCHLD, &newact, &oldact);
00791 
00792     pid = fork();
00793     switch (pid) {
00794       int ndesc;
00795 
00796       case -1:
00797        fclose(fp); /* this closes p[0], the fd associated with fp */
00798        close(p[1]);
00799        sigaction (SIGCHLD, &oldact, NULL);
00800        return 0;
00801 
00802       case 0:
00803        /* dup write-side of pipe to stderr and stdout */
00804        if (p[1] != 1) dup2(p[1], 1);
00805        if (p[1] != 2) dup2(p[1], 2);
00806 
00807        /* 
00808         * close the other file descriptors, except stdin which we
00809         * try reassociating with /dev/null, first (bug 174993)
00810         */
00811        if (!freopen("/dev/null", "r", stdin))
00812            close(0);
00813        ndesc = getdtablesize();
00814        for (fd = PR_MIN(65536, ndesc); --fd > 2; close(fd));
00815 
00816        /* clean up environment in the child process */
00817        putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc:/usr/etc");
00818        putenv("SHELL=/bin/sh");
00819        putenv("IFS= \t");
00820 
00821        /*
00822         * The caller may have passed us a string that is in text
00823         * space. It may be illegal to modify the string
00824         */
00825        cmd = strdup(cmd);
00826        /* format argv */
00827        argv[0] = strtok(cmd, blank);
00828        argc = 1;
00829        while ((argv[argc] = strtok(0, blank)) != 0) {
00830            if (++argc == SAFE_POPEN_MAXARGS) {
00831               argv[argc] = 0;
00832               break;
00833            }
00834        }
00835 
00836        /* and away we go */
00837        execvp(argv[0], argv);
00838        exit(127);
00839        break;
00840 
00841       default:
00842        close(p[1]);
00843        break;
00844     }
00845 
00846     /* non-zero means there's a cmd running */
00847     safe_popen_pid = pid;
00848     return fp;
00849 }
00850 
00851 static int
00852 safe_pclose(FILE *fp)
00853 {
00854     pid_t pid;
00855     int status = -1, rv;
00856 
00857     if ((pid = safe_popen_pid) == 0)
00858        return -1;
00859     safe_popen_pid = 0;
00860 
00861     fclose(fp);
00862 
00863     /* yield the processor so the child gets some time to exit normally */
00864     PR_Sleep(PR_INTERVAL_NO_WAIT);
00865 
00866     /* if the child hasn't exited, kill it -- we're done with its output */
00867     while ((rv = waitpid(pid, &status, WNOHANG)) == -1 && errno == EINTR)
00868        ;
00869     if (rv == 0 && kill(pid, SIGKILL) == 0) {
00870        while ((rv = waitpid(pid, &status, 0)) == -1 && errno == EINTR)
00871            ;
00872     }
00873 
00874     /* Reset SIGCHLD signal hander before returning */
00875     sigaction(SIGCHLD, &oldact, NULL);
00876 
00877     return status;
00878 }
00879 
00880 
00881 #if !defined(VMS)
00882 
00883 #ifdef DARWIN
00884 #include <crt_externs.h>
00885 #endif
00886 
00887 /* Fork netstat to collect its output by default. Do not unset this unless
00888  * another source of entropy is available
00889  */
00890 #define DO_NETSTAT 1
00891 
00892 void RNG_SystemInfoForRNG(void)
00893 {
00894     FILE *fp;
00895     char buf[BUFSIZ];
00896     size_t bytes;
00897     const char * const *cp;
00898     char *randfile;
00899 #ifdef DARWIN
00900     char **environ = *_NSGetEnviron();
00901 #else
00902     extern char **environ;
00903 #endif
00904 #ifdef BEOS
00905     static const char * const files[] = {
00906        "/boot/var/swap",
00907        "/boot/var/log/syslog",
00908        "/boot/var/tmp",
00909        "/boot/home/config/settings",
00910        "/boot/home",
00911        0
00912     };
00913 #else
00914     static const char * const files[] = {
00915        "/etc/passwd",
00916        "/etc/utmp",
00917        "/tmp",
00918        "/var/tmp",
00919        "/usr/tmp",
00920        0
00921     };
00922 #endif
00923 
00924 #ifdef DO_PS
00925 For now it is considered that it is too expensive to run the ps command
00926 for the small amount of entropy it provides.
00927 #if defined(__sun) && (!defined(__svr4) && !defined(SVR4)) || defined(bsdi) || defined(LINUX)
00928     static char ps_cmd[] = "ps aux";
00929 #else
00930     static char ps_cmd[] = "ps -el";
00931 #endif
00932 #endif /* DO_PS */
00933 #if defined(BSDI)
00934     static char netstat_ni_cmd[] = "netstat -nis";
00935 #else
00936     static char netstat_ni_cmd[] = "netstat -ni";
00937 #endif
00938 
00939     GiveSystemInfo();
00940 
00941     bytes = RNG_GetNoise(buf, sizeof(buf));
00942     RNG_RandomUpdate(buf, bytes);
00943 
00944     /*
00945      * Pass the C environment and the addresses of the pointers to the
00946      * hash function. This makes the random number function depend on the
00947      * execution environment of the user and on the platform the program
00948      * is running on.
00949      */
00950     if (environ != NULL) {
00951         cp = (const char * const *) environ;
00952         while (*cp) {
00953            RNG_RandomUpdate(*cp, strlen(*cp));
00954            cp++;
00955         }
00956         RNG_RandomUpdate(environ, (char*)cp - (char*)environ);
00957     }
00958 
00959     /* Give in system information */
00960     if (gethostname(buf, sizeof(buf)) == 0) {
00961        RNG_RandomUpdate(buf, strlen(buf));
00962     }
00963     GiveSystemInfo();
00964 
00965     /* grab some data from system's PRNG before any other files. */
00966     bytes = RNG_FileUpdate("/dev/urandom", SYSTEM_RNG_SEED_COUNT);
00967 
00968     /* If the user points us to a random file, pass it through the rng */
00969     randfile = getenv("NSRANDFILE");
00970     if ( ( randfile != NULL ) && ( randfile[0] != '\0') ) {
00971        RNG_FileForRNG(randfile);
00972     }
00973 
00974     /* pass other files through */
00975     for (cp = files; *cp; cp++)
00976        RNG_FileForRNG(*cp);
00977 
00978 /*
00979  * Bug 100447: On BSD/OS 4.2 and 4.3, we have problem calling safe_popen
00980  * in a pthreads environment.  Therefore, we call safe_popen last and on
00981  * BSD/OS we do not call safe_popen when we succeeded in getting data
00982  * from /dev/urandom.
00983  */
00984 
00985 #ifdef BSDI
00986     if (bytes)
00987         return;
00988 #endif
00989 
00990 #ifdef SOLARIS
00991 
00992 /*
00993  * On Solaris, NSS may be initialized automatically from libldap in
00994  * applications that are unaware of the use of NSS. safe_popen forks, and
00995  * sometimes creates issues with some applications' pthread_atfork handlers.
00996  * We always have /dev/urandom on Solaris 9 and above as an entropy source,
00997  * and for Solaris 8 we have the libkstat interface, so we don't need to
00998  * fork netstat.
00999  */
01000 
01001 #undef DO_NETSTAT
01002     if (!bytes) {
01003         /* On Solaris 8, /dev/urandom isn't available, so we use libkstat. */
01004         PRUint32 kstat_bytes = 0;
01005         if (SECSuccess != RNG_kstat(&kstat_bytes)) {
01006             PORT_Assert(0);
01007         }
01008         bytes += kstat_bytes;
01009         PORT_Assert(bytes);
01010     }
01011 #endif
01012 
01013 #ifdef DO_PS
01014     fp = safe_popen(ps_cmd);
01015     if (fp != NULL) {
01016        while ((bytes = fread(buf, 1, sizeof(buf), fp)) > 0)
01017            RNG_RandomUpdate(buf, bytes);
01018        safe_pclose(fp);
01019     }
01020 #endif
01021 
01022 #ifdef DO_NETSTAT
01023     fp = safe_popen(netstat_ni_cmd);
01024     if (fp != NULL) {
01025        while ((bytes = fread(buf, 1, sizeof(buf), fp)) > 0)
01026            RNG_RandomUpdate(buf, bytes);
01027        safe_pclose(fp);
01028     }
01029 #endif
01030 
01031 }
01032 #else
01033 void RNG_SystemInfoForRNG(void)
01034 {
01035     FILE *fp;
01036     char buf[BUFSIZ];
01037     size_t bytes;
01038     int extra;
01039     char **cp;
01040     extern char **environ;
01041     char *randfile;
01042  
01043     GiveSystemInfo();
01044  
01045     bytes = RNG_GetNoise(buf, sizeof(buf));
01046     RNG_RandomUpdate(buf, bytes);
01047  
01048     /*
01049      * Pass the C environment and the addresses of the pointers to the
01050      * hash function. This makes the random number function depend on the
01051      * execution environment of the user and on the platform the program
01052      * is running on.
01053      */
01054     cp = environ;
01055     while (*cp) {
01056        RNG_RandomUpdate(*cp, strlen(*cp));
01057        cp++;
01058     }
01059     RNG_RandomUpdate(environ, (char*)cp - (char*)environ);
01060  
01061     /* Give in system information */
01062     if (gethostname(buf, sizeof(buf)) > 0) {
01063        RNG_RandomUpdate(buf, strlen(buf));
01064     }
01065     GiveSystemInfo();
01066  
01067     /* If the user points us to a random file, pass it through the rng */
01068     randfile = getenv("NSRANDFILE");
01069     if ( ( randfile != NULL ) && ( randfile[0] != '\0') ) {
01070        RNG_FileForRNG(randfile);
01071     }
01072 
01073     /*
01074     ** We need to generate at least 1024 bytes of seed data. Since we don't
01075     ** do the file stuff for VMS, and because the environ list is so short
01076     ** on VMS, we need to make sure we generate enough. So do another 1000
01077     ** bytes to be sure.
01078     */
01079     extra = 1000;
01080     while (extra > 0) {
01081         cp = environ;
01082         while (*cp) {
01083            int n = strlen(*cp);
01084            RNG_RandomUpdate(*cp, n);
01085            extra -= n;
01086            cp++;
01087         }
01088     }
01089 }
01090 #endif
01091 
01092 #define TOTAL_FILE_LIMIT 1000000   /* one million */
01093 
01094 size_t RNG_FileUpdate(const char *fileName, size_t limit)
01095 {
01096     FILE *        file;
01097     size_t        bytes;
01098     size_t        fileBytes = 0;
01099     struct stat   stat_buf;
01100     unsigned char buffer[BUFSIZ];
01101     static size_t totalFileBytes = 0;
01102     
01103     /* suppress valgrind warnings due to holes in struct stat */
01104     memset(&stat_buf, 0, sizeof(stat_buf));
01105 
01106     if (stat((char *)fileName, &stat_buf) < 0)
01107        return fileBytes;
01108     RNG_RandomUpdate(&stat_buf, sizeof(stat_buf));
01109     
01110     file = fopen((char *)fileName, "r");
01111     if (file != NULL) {
01112        while (limit > fileBytes) {
01113            bytes = PR_MIN(sizeof buffer, limit - fileBytes);
01114            bytes = fread(buffer, 1, bytes, file);
01115            if (bytes == 0) 
01116               break;
01117            RNG_RandomUpdate(buffer, bytes);
01118            fileBytes      += bytes;
01119            totalFileBytes += bytes;
01120            /* after TOTAL_FILE_LIMIT has been reached, only read in first
01121            ** buffer of data from each subsequent file.
01122            */
01123            if (totalFileBytes > TOTAL_FILE_LIMIT) 
01124               break;
01125        }
01126        fclose(file);
01127     }
01128     /*
01129      * Pass yet another snapshot of our highest resolution clock into
01130      * the hash function.
01131      */
01132     bytes = RNG_GetNoise(buffer, sizeof(buffer));
01133     RNG_RandomUpdate(buffer, bytes);
01134     return fileBytes;
01135 }
01136 
01137 void RNG_FileForRNG(const char *fileName)
01138 {
01139     RNG_FileUpdate(fileName, TOTAL_FILE_LIMIT);
01140 }
01141 
01142 size_t RNG_SystemRNG(void *dest, size_t maxLen)
01143 {
01144     FILE *file;
01145     size_t bytes;
01146     size_t fileBytes = 0;
01147     unsigned char *buffer = dest;
01148 
01149     file = fopen("/dev/urandom", "r");
01150     if (file == NULL) {
01151        PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
01152        return fileBytes;
01153     }
01154     while (maxLen > fileBytes) {
01155        bytes = maxLen - fileBytes;
01156        bytes = fread(buffer, 1, bytes, file);
01157        if (bytes == 0) 
01158            break;
01159        fileBytes += bytes;
01160        buffer += bytes;
01161     }
01162     fclose(file);
01163     if (fileBytes != maxLen) {
01164        PORT_SetError(SEC_ERROR_NEED_RANDOM);  /* system RNG failed */
01165        fileBytes = 0;
01166     }
01167     return fileBytes;
01168 }