| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include <limits.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <sys/time.h> |
| #include <sys/wait.h> |
| #include <sys/stat.h> |
| #include "secrng.h" |
| #include "secerr.h" |
| #include "prerror.h" |
| #include "prthread.h" |
| #include "prprf.h" |
| #include "prenv.h" |
| |
| size_t RNG_FileUpdate(const char *fileName, size_t limit); |
| |
| /* |
| * 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 { |
| PRInt32 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; |
| } |
| |
| #ifdef SOLARIS |
| |
| #include <kstat.h> |
| |
| static const PRUint32 entropy_buf_len = 4096; /* buffer up to 4 KB */ |
| |
| /* Buffer entropy data, and feed it to the RNG, entropy_buf_len bytes at a time. |
| * Returns error if RNG_RandomUpdate fails. Also increments *total_fed |
| * by the number of bytes successfully buffered. |
| */ |
| static SECStatus BufferEntropy(char* inbuf, PRUint32 inlen, |
| char* entropy_buf, PRUint32* entropy_buffered, |
| PRUint32* total_fed) |
| { |
| PRUint32 tocopy = 0; |
| PRUint32 avail = 0; |
| SECStatus rv = SECSuccess; |
| |
| while (inlen) { |
| avail = entropy_buf_len - *entropy_buffered; |
| if (!avail) { |
| /* Buffer is full, time to feed it to the RNG. */ |
| rv = RNG_RandomUpdate(entropy_buf, entropy_buf_len); |
| if (SECSuccess != rv) { |
| break; |
| } |
| *entropy_buffered = 0; |
| avail = entropy_buf_len; |
| } |
| tocopy = PR_MIN(avail, inlen); |
| memcpy(entropy_buf + *entropy_buffered, inbuf, tocopy); |
| *entropy_buffered += tocopy; |
| inlen -= tocopy; |
| inbuf += tocopy; |
| *total_fed += tocopy; |
| } |
| return rv; |
| } |
| |
| /* Feed kernel statistics structures and ks_data field to the RNG. |
| * Returns status as well as the number of bytes successfully fed to the RNG. |
| */ |
| static SECStatus RNG_kstat(PRUint32* fed) |
| { |
| kstat_ctl_t* kc = NULL; |
| kstat_t* ksp = NULL; |
| PRUint32 entropy_buffered = 0; |
| char* entropy_buf = NULL; |
| SECStatus rv = SECSuccess; |
| |
| PORT_Assert(fed); |
| if (!fed) { |
| return SECFailure; |
| } |
| *fed = 0; |
| |
| kc = kstat_open(); |
| PORT_Assert(kc); |
| if (!kc) { |
| return SECFailure; |
| } |
| entropy_buf = (char*) PORT_Alloc(entropy_buf_len); |
| PORT_Assert(entropy_buf); |
| if (entropy_buf) { |
| for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { |
| if (-1 == kstat_read(kc, ksp, NULL)) { |
| /* missing data from a single kstat shouldn't be fatal */ |
| continue; |
| } |
| rv = BufferEntropy((char*)ksp, sizeof(kstat_t), |
| entropy_buf, &entropy_buffered, |
| fed); |
| if (SECSuccess != rv) { |
| break; |
| } |
| |
| if (ksp->ks_data && ksp->ks_data_size>0 && ksp->ks_ndata>0) { |
| rv = BufferEntropy((char*)ksp->ks_data, ksp->ks_data_size, |
| entropy_buf, &entropy_buffered, |
| fed); |
| if (SECSuccess != rv) { |
| break; |
| } |
| } |
| } |
| if (SECSuccess == rv && entropy_buffered) { |
| /* Buffer is not empty, time to feed it to the RNG */ |
| rv = RNG_RandomUpdate(entropy_buf, entropy_buffered); |
| } |
| PORT_Free(entropy_buf); |
| } else { |
| rv = SECFailure; |
| } |
| if (kstat_close(kc)) { |
| PORT_Assert(0); |
| rv = SECFailure; |
| } |
| return rv; |
| } |
| |
| #endif |
| |
| #if defined(SCO) || defined(UNIXWARE) || defined(BSDI) || defined(FREEBSD) \ |
| || defined(NETBSD) || defined(DARWIN) || defined(OPENBSD) \ |
| || defined(NTO) || defined(__riscos__) |
| #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> |
| |
| #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) */ |
| |
| extern long sysconf(int name); |
| |
| 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> |
| |
| #define getdtablesize() sysconf(_SC_OPEN_MAX) |
| |
| #if defined(__ia64) |
| #include <ia64/sys/inline.h> |
| |
| static size_t |
| GetHighResClock(void *buf, size_t maxbytes) |
| { |
| PRUint64 t; |
| |
| t = _Asm_mov_from_ar(_AREG44); |
| return CopyLowBits(buf, maxbytes, &t, sizeof(t)); |
| } |
| #else |
| 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)); |
| } |
| #endif |
| |
| 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(OSF1) |
| #include <sys/types.h> |
| #include <sys/sysinfo.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 <sys/sysinfo.h> |
| |
| static size_t |
| GetHighResClock(void *buf, size_t maxbytes) |
| { |
| return 0; |
| } |
| |
| static void |
| GiveSystemInfo(void) |
| { |
| #ifndef NO_SYSINFO |
| struct sysinfo si; |
| 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 <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 */ |
| |
| |
| #ifdef BEOS |
| #include <be/kernel/OS.h> |
| |
| static size_t |
| GetHighResClock(void *buf, size_t maxbytes) |
| { |
| bigtime_t bigtime; /* Actually a int64 */ |
| |
| bigtime = real_time_clock_usecs(); |
| return CopyLowBits(buf, maxbytes, &bigtime, sizeof(bigtime)); |
| } |
| |
| static void |
| GiveSystemInfo(void) |
| { |
| system_info *info = NULL; |
| PRInt32 val; |
| get_system_info(info); |
| if (info) { |
| val = info->boot_time; |
| RNG_RandomUpdate(&val, sizeof(val)); |
| val = info->used_pages; |
| RNG_RandomUpdate(&val, sizeof(val)); |
| val = info->used_ports; |
| RNG_RandomUpdate(&val, sizeof(val)); |
| val = info->used_threads; |
| RNG_RandomUpdate(&val, sizeof(val)); |
| val = info->used_teams; |
| RNG_RandomUpdate(&val, sizeof(val)); |
| } |
| } |
| #endif /* BEOS */ |
| |
| #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; |
| |
| (void)gettimeofday(&tv, 0); |
| 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; |
| |
| fp = fdopen(p[0], "r"); |
| if (fp == 0) { |
| close(p[0]); |
| close(p[1]); |
| 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) { |
| int ndesc; |
| |
| case -1: |
| fclose(fp); /* this closes p[0], the fd associated with fp */ |
| 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 the other file descriptors, except stdin which we |
| * try reassociating with /dev/null, first (bug 174993) |
| */ |
| if (!freopen("/dev/null", "r", stdin)) |
| close(0); |
| ndesc = getdtablesize(); |
| for (fd = PR_MIN(65536, ndesc); --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]); |
| 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 status = -1, rv; |
| |
| if ((pid = safe_popen_pid) == 0) |
| return -1; |
| safe_popen_pid = 0; |
| |
| fclose(fp); |
| |
| /* yield the processor so the child gets some time to exit normally */ |
| PR_Sleep(PR_INTERVAL_NO_WAIT); |
| |
| /* if the child hasn't exited, kill it -- we're done with its output */ |
| while ((rv = waitpid(pid, &status, WNOHANG)) == -1 && errno == EINTR) |
| ; |
| if (rv == 0) { |
| kill(pid, SIGKILL); |
| while ((rv = waitpid(pid, &status, 0)) == -1 && errno == EINTR) |
| ; |
| } |
| |
| /* Reset SIGCHLD signal hander before returning */ |
| sigaction(SIGCHLD, &oldact, NULL); |
| |
| return status; |
| } |
| |
| #ifdef DARWIN |
| #include <TargetConditionals.h> |
| #if !TARGET_OS_IPHONE |
| #include <crt_externs.h> |
| #endif |
| #endif |
| |
| /* Fork netstat to collect its output by default. Do not unset this unless |
| * another source of entropy is available |
| */ |
| #define DO_NETSTAT 1 |
| |
| void RNG_SystemInfoForRNG(void) |
| { |
| FILE *fp; |
| char buf[BUFSIZ]; |
| size_t bytes; |
| const char * const *cp; |
| char *randfile; |
| #ifdef DARWIN |
| #if TARGET_OS_IPHONE |
| /* iOS does not expose a way to access environ. */ |
| char **environ = NULL; |
| #else |
| char **environ = *_NSGetEnviron(); |
| #endif |
| #else |
| extern char **environ; |
| #endif |
| #ifdef BEOS |
| static const char * const files[] = { |
| "/boot/var/swap", |
| "/boot/var/log/syslog", |
| "/boot/var/tmp", |
| "/boot/home/config/settings", |
| "/boot/home", |
| 0 |
| }; |
| #else |
| static const char * const files[] = { |
| "/etc/passwd", |
| "/etc/utmp", |
| "/tmp", |
| "/var/tmp", |
| "/usr/tmp", |
| 0 |
| }; |
| #endif |
| |
| #if defined(BSDI) |
| static char netstat_ni_cmd[] = "netstat -nis"; |
| #else |
| static char netstat_ni_cmd[] = "netstat -ni"; |
| #endif |
| |
| GiveSystemInfo(); |
| |
| bytes = RNG_GetNoise(buf, sizeof(buf)); |
| RNG_RandomUpdate(buf, bytes); |
| |
| /* |
| * 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. |
| */ |
| if (environ != NULL) { |
| cp = (const char * const *) 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(); |
| |
| /* grab some data from system's PRNG before any other files. */ |
| bytes = RNG_FileUpdate("/dev/urandom", SYSTEM_RNG_SEED_COUNT); |
| |
| /* If the user points us to a random file, pass it through the rng */ |
| randfile = PR_GetEnvSecure("NSRANDFILE"); |
| if ( ( randfile != NULL ) && ( randfile[0] != '\0') ) { |
| char *randCountString = PR_GetEnvSecure("NSRANDCOUNT"); |
| int randCount = randCountString ? atoi(randCountString) : 0; |
| if (randCount != 0) { |
| RNG_FileUpdate(randfile, randCount); |
| } else { |
| RNG_FileForRNG(randfile); |
| } |
| } |
| |
| /* pass other files through */ |
| for (cp = files; *cp; cp++) |
| RNG_FileForRNG(*cp); |
| |
| /* |
| * Bug 100447: On BSD/OS 4.2 and 4.3, we have problem calling safe_popen |
| * in a pthreads environment. Therefore, we call safe_popen last and on |
| * BSD/OS we do not call safe_popen when we succeeded in getting data |
| * from /dev/urandom. |
| * |
| * Bug 174993: On platforms providing /dev/urandom, don't fork netstat |
| * either, if data has been gathered successfully. |
| */ |
| |
| #if defined(BSDI) || defined(FREEBSD) || defined(NETBSD) \ |
| || defined(OPENBSD) || defined(DARWIN) || defined(LINUX) \ |
| || defined(HPUX) |
| if (bytes == SYSTEM_RNG_SEED_COUNT) |
| return; |
| |
| /* |
| * Modified to abort the process if it failed to read from /dev/urandom. |
| * |
| * See crbug.com/244661 for details. |
| */ |
| fprintf(stderr, "[ERROR:%s(%d)] NSS read %zu bytes (expected %d bytes) " |
| "from /dev/urandom. Abort process.\n", __FILE__, __LINE__, |
| bytes, SYSTEM_RNG_SEED_COUNT); |
| fflush(stderr); |
| abort(); |
| #endif |
| |
| #ifdef SOLARIS |
| |
| /* |
| * On Solaris, NSS may be initialized automatically from libldap in |
| * applications that are unaware of the use of NSS. safe_popen forks, and |
| * sometimes creates issues with some applications' pthread_atfork handlers. |
| * We always have /dev/urandom on Solaris 9 and above as an entropy source, |
| * and for Solaris 8 we have the libkstat interface, so we don't need to |
| * fork netstat. |
| */ |
| |
| #undef DO_NETSTAT |
| if (!bytes) { |
| /* On Solaris 8, /dev/urandom isn't available, so we use libkstat. */ |
| PRUint32 kstat_bytes = 0; |
| if (SECSuccess != RNG_kstat(&kstat_bytes)) { |
| PORT_Assert(0); |
| } |
| bytes += kstat_bytes; |
| PORT_Assert(bytes); |
| } |
| #endif |
| |
| #ifdef DO_NETSTAT |
| 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); |
| } |
| #endif |
| |
| } |
| |
| #define TOTAL_FILE_LIMIT 1000000 /* one million */ |
| |
| size_t RNG_FileUpdate(const char *fileName, size_t limit) |
| { |
| FILE * file; |
| int fd; |
| int bytes; |
| size_t fileBytes = 0; |
| struct stat stat_buf; |
| unsigned char buffer[BUFSIZ]; |
| static size_t totalFileBytes = 0; |
| |
| /* suppress valgrind warnings due to holes in struct stat */ |
| memset(&stat_buf, 0, sizeof(stat_buf)); |
| |
| if (stat((char *)fileName, &stat_buf) < 0) |
| return fileBytes; |
| RNG_RandomUpdate(&stat_buf, sizeof(stat_buf)); |
| |
| file = fopen(fileName, "r"); |
| if (file != NULL) { |
| /* Read from the underlying file descriptor directly to bypass stdio |
| * buffering and avoid reading more bytes than we need from |
| * /dev/urandom. NOTE: we can't use fread with unbuffered I/O because |
| * fread may return EOF in unbuffered I/O mode on Android. |
| * |
| * Moreover, we read into a buffer of size BUFSIZ, so buffered I/O |
| * has no performance advantage. */ |
| fd = fileno(file); |
| /* 'file' was just opened, so this should not fail. */ |
| PORT_Assert(fd != -1); |
| while (limit > fileBytes) { |
| bytes = PR_MIN(sizeof buffer, limit - fileBytes); |
| bytes = read(fd, buffer, bytes); |
| if (bytes <= 0) |
| break; |
| RNG_RandomUpdate(buffer, bytes); |
| fileBytes += bytes; |
| totalFileBytes += bytes; |
| /* after TOTAL_FILE_LIMIT has been reached, only read in first |
| ** buffer of data from each subsequent file. |
| */ |
| if (totalFileBytes > TOTAL_FILE_LIMIT) |
| 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); |
| return fileBytes; |
| } |
| |
| void RNG_FileForRNG(const char *fileName) |
| { |
| RNG_FileUpdate(fileName, TOTAL_FILE_LIMIT); |
| } |
| |
| void ReadSingleFile(const char *fileName) |
| { |
| FILE * file; |
| unsigned char buffer[BUFSIZ]; |
| |
| file = fopen(fileName, "rb"); |
| if (file != NULL) { |
| while (fread(buffer, 1, sizeof(buffer), file) > 0) |
| ; |
| fclose(file); |
| } |
| } |
| |
| #define _POSIX_PTHREAD_SEMANTICS |
| #include <dirent.h> |
| |
| PRBool |
| ReadFileOK(char *dir, char *file) |
| { |
| struct stat stat_buf; |
| char filename[PATH_MAX]; |
| int count = snprintf(filename, sizeof filename, "%s/%s",dir, file); |
| |
| if (count <= 0) { |
| return PR_FALSE; /* name too long, can't read it anyway */ |
| } |
| |
| if (stat(filename, &stat_buf) < 0) |
| return PR_FALSE; /* can't stat, probably can't read it then as well */ |
| return S_ISREG(stat_buf.st_mode) ? PR_TRUE : PR_FALSE; |
| } |
| |
| /* |
| * read one file out of either /etc or the user's home directory. |
| * fileToRead tells which file to read. |
| * |
| * return 1 if it's time to reset the fileToRead (no more files to read). |
| */ |
| int ReadOneFile(int fileToRead) |
| { |
| char *dir = "/etc"; |
| DIR *fd = opendir(dir); |
| int resetCount = 0; |
| #ifdef SOLARIS |
| /* grumble, Solaris does not define struct dirent to be the full length */ |
| typedef union { |
| unsigned char space[sizeof(struct dirent) + MAXNAMELEN]; |
| struct dirent dir; |
| } dirent_hack; |
| dirent_hack entry, firstEntry; |
| |
| #define entry_dir entry.dir |
| #else |
| struct dirent entry, firstEntry; |
| #define entry_dir entry |
| #endif |
| |
| int i, error = -1; |
| |
| if (fd == NULL) { |
| dir = PR_GetEnvSecure("HOME"); |
| if (dir) { |
| fd = opendir(dir); |
| } |
| } |
| if (fd == NULL) { |
| return 1; |
| } |
| |
| for (i=0; i <= fileToRead; i++) { |
| struct dirent *result = NULL; |
| do { |
| error = readdir_r(fd, &entry_dir, &result); |
| } while (error == 0 && result != NULL && |
| !ReadFileOK(dir,&result->d_name[0])); |
| if (error != 0 || result == NULL) { |
| resetCount = 1; /* read to the end, start again at the beginning */ |
| if (i != 0) { |
| /* ran out of entries in the directory, use the first one */ |
| entry = firstEntry; |
| error = 0; |
| break; |
| } |
| /* if i== 0, there were no readable entries in the directory */ |
| break; |
| } |
| if (i==0) { |
| /* save the first entry in case we run out of entries */ |
| firstEntry = entry; |
| } |
| } |
| |
| if (error == 0) { |
| char filename[PATH_MAX]; |
| int count = snprintf(filename, sizeof filename, |
| "%s/%s",dir, &entry_dir.d_name[0]); |
| if (count >= 1) { |
| ReadSingleFile(filename); |
| } |
| } |
| |
| closedir(fd); |
| return resetCount; |
| } |
| |
| /* |
| * do something to try to introduce more noise into the 'GetNoise' call |
| */ |
| static void rng_systemJitter(void) |
| { |
| static int fileToRead = 1; |
| |
| if (ReadOneFile(fileToRead)) { |
| fileToRead = 1; |
| } else { |
| fileToRead++; |
| } |
| } |
| |
| /* |
| * Modified to abort the process if it failed to read from /dev/urandom. |
| * |
| * See crbug.com/244661 for details. |
| */ |
| size_t RNG_SystemRNG(void *dest, size_t maxLen) |
| { |
| FILE *file; |
| int fd; |
| int bytes; |
| size_t fileBytes = 0; |
| unsigned char *buffer = dest; |
| |
| file = fopen("/dev/urandom", "r"); |
| if (file == NULL) { |
| fprintf(stderr, "[ERROR:%s(%d)] NSS failed to read from /dev/urandom. " |
| "Abort process.\n", __FILE__, __LINE__); |
| fflush(stderr); |
| abort(); |
| } |
| /* Read from the underlying file descriptor directly to bypass stdio |
| * buffering and avoid reading more bytes than we need from /dev/urandom. |
| * NOTE: we can't use fread with unbuffered I/O because fread may return |
| * EOF in unbuffered I/O mode on Android. |
| */ |
| fd = fileno(file); |
| /* 'file' was just opened, so this should not fail. */ |
| PORT_Assert(fd != -1); |
| while (maxLen > fileBytes) { |
| bytes = maxLen - fileBytes; |
| bytes = read(fd, buffer, bytes); |
| if (bytes <= 0) |
| break; |
| fileBytes += bytes; |
| buffer += bytes; |
| } |
| fclose(file); |
| if (fileBytes != maxLen) { |
| fprintf(stderr, "[ERROR:%s(%d)] NSS failed to read from /dev/urandom. " |
| "Abort process.\n", __FILE__, __LINE__); |
| fflush(stderr); |
| abort(); |
| } |
| return fileBytes; |
| } |