/*
*
* LICENSE AGREEMENT NON-COMMERCIAL USE (Random Seed Generation)
*
* Netscape Communications Corporation ("Netscape") hereby grants you a
* non-exclusive, non-transferable license to use the source code version
* of the accompanying random seed generation software (the "Software")
* subject to the following terms:
*
* 1. You may use, modify and/or incorporate the Software, in whole or in
* part, into other software programs (a "Derivative Work") solely in
* order to evaluate the Software's features, functions and capabilities.
*
* 2. You may not use the Software or Derivative Works for
* revenue-generating purposes. You may not:
*
* (a) license or distribute the Software or any Derivative Work
* in any manner that generates license fees, royalties,
* maintenance fees, upgrade fees or any other form of income.
*
* (b) use the Software or a Derivative Work to provide services
* to others for which you are compensated in any manner.
*
* (c) distribute the Software or a Derivative Work without
* written agreement from the end user to abide by the terms of
* Sections 1 and 2 of this Agreement.
*
* 3. This license is granted free of charge.
*
* 4. Any modification of the Software or Derivative Work must
* prominently state in the code and associated documentation that it has
* been modified, the date modified and the identity of the person(s) who
* made the modifications.
*
* 5. Licensee will promptly notify Netscape of any proposed or
* implemented improvement in the Software and grants Netscape the
* perpetual right to incorporate any such improvements into the Software
* and into Netscape's products which incorporate the Software (which may
* be distributed to third parties). Licensee will also promptly notify
* Netscape of any alleged deficiency in the Software. If Netscape
* requests, Licensee will provide Netscape with a copy of each Derivative
* Work Licensee creates.
*
* 6. Any copy of the Software or Derivative Work shall include a copy of
* this Agreement, Netscape's copyright notices and the disclaimer of
* warranty and limitation of liability.
*
* 7. Title, ownership rights, and intellectual property rights in and to
* the Software shall remain in Netscape and/or its suppliers. You may
* use the Software only as provided in this Agreement. Netscape shall
* have no obligation to provide maintenance, support, upgrades or new
* releases to you or any person to whom you distribute the Software or a
* Derivative Work.
*
* 8. Netscape may use Licensee's name in publicity materials as a
* licensee of the Software.
*
* 9. Disclaimer of Warranty. THE SOFTWARE IS LICENSED "AS IS" WITHOUT
* WARRANTY OF ANY KIND, INCLUDING WITHOUT LIMITATION, PERFORMANCE,
* MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. THE ENTIRE RISK
* AS TO THE RESULTS AND PERFORMANCE OF THE SOFTWARE IS ASSUMED BY YOU.
*
* 10. Limitation of Liability. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL
* THEORY SHALL NETSCAPE OR ITS SUPPLIERS BE LIABLE TO YOU OR ANY OTHER
* PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* OF ANY CHARACTER INCLUDING WITHOUT LIMITATION ANY COMMERCIAL DAMAGES OR
* LOSSES , EVEN IF NETSCAPE HAS BEEN INFORMED OF THE POSSIBILITY OF SUCH
* DAMAGES.
*
* 11. You may not download or otherwise export or reexport the Software
* or any underlying information or technology except in full compliance
* with all United States and other applicable laws and regulations.
*
* 10. Either party may terminate this Agreement immediately in the event
* of default by the other party. You may also terminate this Agreement
* at any time by destroying the Software and all copies thereof.
*
* 11. Use, duplication or disclosure by the United States Government is
* subject to restrictions set forth in subparagraphs (a) through (d) of
* the Commercial Computer-Restricted Rights clause at FAR 52.227-19 when
* applicable, or in subparagraph (c)(1)(ii) of the Rights in Technical
* Data and Computer Program clause at DFARS 252.227-7013, and in similar
* clauses in the NASA FAR Supplement. Contractor/manufacturer is Netscape
* Communications Corporation, 501 East Middlefield Road, Mountain View,
* CA 94043.
*
* 12. This Agreement shall be governed by and construed under California
* law as such law applies to agreements between California residents
* entered into and to be performed entirely within California, except as
* governed by Federal law.
*
*/
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <assert.h>
#include "secrng.h"
extern RNGContext *sec_base_rng;
/*
* When copying data to the buffer we want the least signicant bytes
* from the input since those bits are changing the fastest. The address
* of least significant byte depends upon whether we are running on
* a big-endian or little-endian machine.
*
* Does this mean the least signicant bytes are the most significant
* to us? :-)
*/
static size_t CopyLowBits(void *dst, size_t dstlen, void *src, size_t srclen)
{
union endianness {
int32 i;
char c[4];
} u;
if (srclen <= dstlen) {
memcpy(dst, src, srclen);
return srclen;
}
u.i = 0x01020304;
if (u.c[0] == 0x01) {
/* big-endian case */
memcpy(dst, (char*)src + (srclen - dstlen), dstlen);
} else {
/* little-endian case */
memcpy(dst, src, dstlen);
}
return dstlen;
}
#if defined(SCO) || defined(UNIXWARE) || defined(BSDI)
#include <unistd.h>
#include <sys/times.h>
#define getdtablesize() sysconf(_SC_OPEN_MAX)
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
int ticks;
struct tms buffer;
ticks=times(&buffer);
return CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks));
}
static void
GiveSystemInfo(void)
{
long si;
/*
* Is this really necessary? Why not use rand48 or something?
*/
si = sysconf(_SC_CHILD_MAX);
RNG_RandomUpdate(&si, sizeof(si));
si = sysconf(_SC_STREAM_MAX);
RNG_RandomUpdate(&si, sizeof(si));
si = sysconf(_SC_OPEN_MAX);
RNG_RandomUpdate(&si, sizeof(si));
}
#endif
#if defined(__sun)
#if defined(__svr4) || defined(SVR4)
#include <sys/systeminfo.h>
#include <sys/times.h>
#include <wait.h>
int gettimeofday(struct timeval *);
int gethostname(char *, int);
#define getdtablesize() sysconf(_SC_OPEN_MAX)
static void
GiveSystemInfo(void)
{
int rv;
char buf[2000];
rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
}
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
hrtime_t t;
t = gethrtime();
if (t) {
return CopyLowBits(buf, maxbytes, &t, sizeof(t));
}
return 0;
}
#else /* SunOS (Sun, but not SVR4) */
#include <sys/wait.h>
#include "sunos4.h"
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
return 0;
}
static void
GiveSystemInfo(void)
{
long si;
/* This is not very good */
si = sysconf(_SC_CHILD_MAX);
RNG_RandomUpdate(&si, sizeof(si));
}
#endif
#endif /* Sun */
#if defined(__hpux)
#include <sys/unistd.h>
#include <sys/wait.h>
#define getdtablesize() sysconf(_SC_OPEN_MAX)
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
extern int ret_cr16();
int cr16val;
cr16val = ret_cr16();
return CopyLowBits(buf, maxbytes, &cr16val, sizeof(cr16val));
}
static void
GiveSystemInfo(void)
{
long si;
/* This is not very good */
si = sysconf(_AES_OS_VERSION);
RNG_RandomUpdate(&si, sizeof(si));
si = sysconf(_SC_CPU_VERSION);
RNG_RandomUpdate(&si, sizeof(si));
}
#endif /* HPUX */
#if defined(__alpha)
#include <sys/types.h>
#include <sys/sysinfo.h>
#include <sys/wait.h>
#include <sys/systeminfo.h>
#include <c_asm.h>
static void
GiveSystemInfo(void)
{
char buf[BUFSIZ];
int rv;
int off = 0;
rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
}
/*
* Use the "get the cycle counter" instruction on the alpha.
* The low 32 bits completely turn over in less than a minute.
* The high 32 bits are some non-counter gunk that changes sometimes.
*/
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
unsigned long t;
t = asm("rpcc %v0");
return CopyLowBits(buf, maxbytes, &t, sizeof(t));
}
#endif /* Alpha */
#if defined(_IBMR2)
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
return 0;
}
static void
GiveSystemInfo(void)
{
/* XXX haven't found any yet! */
}
#endif /* IBM R2 */
#if defined(__linux)
#include <linux/kernel.h>
int putenv(const char *);
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
return 0;
}
static void
GiveSystemInfo(void)
{
/* XXX sysinfo() does not seem be implemented anywhwere */
#if 0
struct sysinfo si;
char hn[2000];
if (sysinfo(&si) == 0) {
RNG_RandomUpdate(&si, sizeof(si));
}
#endif
}
#endif /* __linux */
#if defined(NCR)
#include <sys/utsname.h>
#include <sys/systeminfo.h>
#define getdtablesize() sysconf(_SC_OPEN_MAX)
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
return 0;
}
static void
GiveSystemInfo(void)
{
int rv;
char buf[2000];
rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
}
#endif /* NCR */
#if defined(sgi)
#include <fcntl.h>
#undef PRIVATE
#include <sys/mman.h>
#include <sys/syssgi.h>
#include <sys/immu.h>
#include <sys/systeminfo.h>
#include <sys/utsname.h>
#include <wait.h>
static void
GiveSystemInfo(void)
{
int rv;
char buf[4096];
rv = syssgi(SGI_SYSID, &buf[0]);
if (rv > 0) {
RNG_RandomUpdate(buf, MAXSYSIDSIZE);
}
#ifdef SGI_RDUBLK
rv = syssgi(SGI_RDUBLK, getpid(), &buf[0], sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, sizeof(buf));
}
#endif /* SGI_RDUBLK */
rv = syssgi(SGI_INVENT, SGI_INV_READ, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, sizeof(buf));
}
rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
}
static size_t GetHighResClock(void *buf, size_t maxbuf)
{
unsigned phys_addr, raddr, cycleval;
static volatile unsigned *iotimer_addr = NULL;
static int tries = 0;
static int cntr_size;
int mfd;
long s0[2];
struct timeval tv;
#ifndef SGI_CYCLECNTR_SIZE
#define SGI_CYCLECNTR_SIZE 165 /* Size user needs to use to read CC */
#endif
if (iotimer_addr == NULL) {
if (tries++ > 1) {
/* Don't keep trying if it didn't work */
return 0;
}
/*
** For SGI machines we can use the cycle counter, if it has one,
** to generate some truly random numbers
*/
phys_addr = syssgi(SGI_QUERY_CYCLECNTR, &cycleval);
if (phys_addr) {
int pgsz = getpagesize();
int pgoffmask = pgsz - 1;
raddr = phys_addr & ~pgoffmask;
mfd = open("/dev/mmem", O_RDONLY);
if (mfd < 0) {
return 0;
}
iotimer_addr = (unsigned *)
mmap(0, pgoffmask, PROT_READ, MAP_PRIVATE, mfd, (int)raddr);
if (iotimer_addr == (void*)-1) {
close(mfd);
iotimer_addr = NULL;
return 0;
}
iotimer_addr = (unsigned*)
((__psint_t)iotimer_addr | (phys_addr & pgoffmask));
/*
* The file 'mfd' is purposefully not closed.
*/
cntr_size = syssgi(SGI_CYCLECNTR_SIZE);
if (cntr_size < 0) {
struct utsname utsinfo;
/*
* We must be executing on a 6.0 or earlier system, since the
* SGI_CYCLECNTR_SIZE call is not supported.
*
* The only pre-6.1 platforms with 64-bit counters are
* IP19 and IP21 (Challenge, PowerChallenge, Onyx).
*/
uname(&utsinfo);
if (!strncmp(utsinfo.machine, "IP19", 4) ||
!strncmp(utsinfo.machine, "IP21", 4))
cntr_size = 64;
else
cntr_size = 32;
}
cntr_size /= 8; /* Convert from bits to bytes */
}
}
s0[0] = *iotimer_addr;
if (cntr_size > 4)
s0[1] = *(iotimer_addr + 1);
memcpy(buf, (char *)&s0[0], cntr_size);
return CopyLowBits(buf, maxbuf, &s0, cntr_size);
}
#endif
#if defined(sony)
#include <sys/systeminfo.h>
#define getdtablesize() sysconf(_SC_OPEN_MAX)
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
return 0;
}
static void
GiveSystemInfo(void)
{
int rv;
char buf[2000];
rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
}
#endif /* sony */
#if defined(sinix)
#include <unistd.h>
#include <sys/systeminfo.h>
#include <sys/times.h>
int gettimeofday(struct timeval *, struct timezone *);
int gethostname(char *, int);
#define getdtablesize() sysconf(_SC_OPEN_MAX)
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
int ticks;
struct tms buffer;
ticks=times(&buffer);
return CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks));
}
static void
GiveSystemInfo(void)
{
int rv;
char buf[2000];
rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
}
#endif /* sinix */
#if defined(nec_ews)
#include <sys/systeminfo.h>
#define getdtablesize() sysconf(_SC_OPEN_MAX)
static size_t
GetHighResClock(void *buf, size_t maxbytes)
{
return 0;
}
static void
GiveSystemInfo(void)
{
int rv;
char buf[2000];
rv = sysinfo(SI_MACHINE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_RELEASE, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
rv = sysinfo(SI_HW_SERIAL, buf, sizeof(buf));
if (rv > 0) {
RNG_RandomUpdate(buf, rv);
}
}
#endif /* nec_ews */
size_t RNG_GetNoise(void *buf, size_t maxbytes)
{
struct timeval tv;
int n = 0;
int c;
n = GetHighResClock(buf, maxbytes);
maxbytes -= n;
#if defined(__sun) && (defined(_svr4) || defined(SVR4)) || defined(sony)
(void)gettimeofday(&tv);
#else
(void)gettimeofday(&tv, 0);
#endif
c = CopyLowBits((char*)buf+n, maxbytes, &tv.tv_usec, sizeof(tv.tv_usec));
n += c;
maxbytes -= c;
c = CopyLowBits((char*)buf+n, maxbytes, &tv.tv_sec, sizeof(tv.tv_sec));
n += c;
return n;
}
#define SAFE_POPEN_MAXARGS 10 /* must be at least 2 */
/*
* safe_popen is static to this module and we know what arguments it is
* called with. Note that this version only supports a single open child
* process at any time.
*/
static pid_t safe_popen_pid;
static struct sigaction oldact;
static FILE *
safe_popen(char *cmd)
{
int p[2], fd, argc;
pid_t pid;
char *argv[SAFE_POPEN_MAXARGS + 1];
FILE *fp;
static char blank[] = " \t";
static struct sigaction newact;
if (pipe(p) < 0)
return 0;
/* Setup signals so that SIGCHLD is ignored as we want to do waitpid */
newact.sa_handler = SIG_DFL;
newact.sa_flags = 0;
sigfillset(&newact.sa_mask);
sigaction (SIGCHLD, &newact, &oldact);
pid = fork();
switch (pid) {
case -1:
close(p[0]);
close(p[1]);
sigaction (SIGCHLD, &oldact, NULL);
return 0;
case 0:
/* dup write-side of pipe to stderr and stdout */
if (p[1] != 1) dup2(p[1], 1);
if (p[1] != 2) dup2(p[1], 2);
close(0);
for (fd = getdtablesize(); --fd > 2; close(fd))
;
/* clean up environment in the child process */
putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc:/usr/etc");
putenv("SHELL=/bin/sh");
putenv("IFS= \t");
/*
* The caller may have passed us a string that is in text
* space. It may be illegal to modify the string
*/
cmd = strdup(cmd);
/* format argv */
argv[0] = strtok(cmd, blank);
argc = 1;
while ((argv[argc] = strtok(0, blank)) != 0) {
if (++argc == SAFE_POPEN_MAXARGS) {
argv[argc] = 0;
break;
}
}
/* and away we go */
execvp(argv[0], argv);
exit(127);
break;
default:
close(p[1]);
fp = fdopen(p[0], "r");
if (fp == 0) {
close(p[0]);
sigaction (SIGCHLD, &oldact, NULL);
return 0;
}
break;
}
/* non-zero means there's a cmd running */
safe_popen_pid = pid;
return fp;
}
static int
safe_pclose(FILE *fp)
{
pid_t pid;
int count, status;
if ((pid = safe_popen_pid) == 0)
return -1;
safe_popen_pid = 0;
/* if the child hasn't exited, kill it -- we're done with its output */
count = 0;
while (waitpid(pid, &status, WNOHANG) == 0) {
if (kill(pid, SIGKILL) < 0 && errno == ESRCH)
break;
if (++count == 1000)
break;
}
/* Reset SIGCHLD signal hander before returning */
sigaction(SIGCHLD, &oldact, NULL);
fclose(fp);
return status;
}
void RNG_SystemInfoForRNG(void)
{
FILE *fp;
char buf[BUFSIZ];
size_t bytes;
extern char **environ;
char **cp;
char *randfile;
char *files[] = {
"/etc/passwd",
"/etc/utmp",
"/tmp",
"/var/tmp",
"/usr/tmp",
0
};
#ifdef DO_PS
For now it is considered that it is too expensive to run the ps command
for the small amount of entropy it provides.
#if defined(__sun) && (!defined(__svr4) && !defined(SVR4)) || defined(bsdi) || defined(__linux)
static char ps_cmd[] = "ps aux";
#else
static char ps_cmd[] = "ps -el";
#endif
#endif /* DO_PS */
static char netstat_ni_cmd[] = "netstat -ni";
GiveSystemInfo();
bytes = RNG_GetNoise(buf, sizeof(buf));
RNG_RandomUpdate(buf, bytes);
#ifdef DO_PS
fp = safe_popen(ps_cmd);
if (fp != NULL) {
while ((bytes = fread(buf, 1, sizeof(buf), fp)) > 0)
RNG_RandomUpdate(buf, bytes);
safe_pclose(fp);
}
#endif
fp = safe_popen(netstat_ni_cmd);
if (fp != NULL) {
while ((bytes = fread(buf, 1, sizeof(buf), fp)) > 0)
RNG_RandomUpdate(buf, bytes);
safe_pclose(fp);
}
/*
* Pass the C environment and the addresses of the pointers to the
* hash function. This makes the random number function depend on the
* execution environment of the user and on the platform the program
* is running on.
*/
cp = environ;
while (*cp) {
RNG_RandomUpdate(*cp, strlen(*cp));
cp++;
}
RNG_RandomUpdate(environ, (char*)cp - (char*)environ);
/* Give in system information */
if (gethostname(buf, sizeof(buf)) > 0) {
RNG_RandomUpdate(buf, strlen(buf));
}
GiveSystemInfo();
/* If the user points us to a random file, pass it through the rng */
randfile = getenv("NSRANDFILE");
if ( ( randfile != NULL ) && ( randfile[0] != '\0') ) {
RNG_FileForRNG(randfile);
}
/* pass other files through */
for (cp = files; *cp; cp++)
RNG_FileForRNG(*cp);
}
void RNG_FileForRNG(char *fileName)
{
struct stat stat_buf;
unsigned char buffer[BUFSIZ];
size_t bytes;
FILE *file;
static size_t totalFileBytes = 0;
if (stat((char *)fileName, &stat_buf) < 0)
return;
RNG_RandomUpdate(&stat_buf, sizeof(stat_buf));
file = fopen((char *)fileName, "r");
if (file != NULL) {
for (;;) {
bytes = fread(buffer, 1, sizeof(buffer), file);
if (bytes == 0) break;
RNG_RandomUpdate(buffer, bytes);
totalFileBytes += bytes;
if (totalFileBytes > 1024*1024) break;
}
fclose(file);
}
/*
* Pass yet another snapshot of our highest resolution clock into
* the hash function.
*/
bytes = RNG_GetNoise(buffer, sizeof(buffer));
RNG_RandomUpdate(buffer, bytes);
}