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