Bug 1346735 - drop netstat from unix_rand; add new urandom only entropy source, r=mt,ttaubert
authorFranziskus Kiefer <franziskuskiefer@gmail.com>
Wed, 18 Jan 2017 11:30:03 +0100
changeset 13217 eab257efdbf6b6d5ef4358e2bb197f9281ac2001
parent 13216 2e7133b0da822f9bed3b07d21c6a479cfdd82dbc
child 13218 11185e87f8e5c755848b9f98d6919cdcf94f97d2
push id2087
push userfranziskuskiefer@gmail.com
push dateThu, 16 Mar 2017 11:40:54 +0000
reviewersmt, ttaubert
bugs1346735
Bug 1346735 - drop netstat from unix_rand; add new urandom only entropy source, r=mt,ttaubert Differential Revision: https://nss-review.dev.mozaws.net/D45
coreconf/config.gypi
lib/freebl/freebl_base.gypi
lib/freebl/sysrand.c
lib/freebl/unix_rand.c
lib/freebl/unix_urandom.c
--- a/coreconf/config.gypi
+++ b/coreconf/config.gypi
@@ -103,16 +103,17 @@
     'fuzz%': 0,
     'fuzz_tls%': 0,
     'fuzz_oss%': 0,
     'sign_libs%': 1,
     'use_pprof%': 0,
     'ct_verif%': 0,
     'nss_public_dist_dir%': '<(nss_dist_dir)/public',
     'nss_private_dist_dir%': '<(nss_dist_dir)/private',
+    'only_dev_random%': 1,
   },
   'target_defaults': {
     # Settings specific to targets should go here.
     # This is mostly for linking to libraries.
     'variables': {
       'mapfile%': '',
       'test_build%': 0,
       'debug_optimization_level%': '0',
--- a/lib/freebl/freebl_base.gypi
+++ b/lib/freebl/freebl_base.gypi
@@ -172,16 +172,21 @@
         'UNSAFE_FUZZER_MODE',
       ],
     }],
     [ 'ct_verif==1', {
       'defines': [
         'CT_VERIF',
       ],
     }],
+    [ 'only_dev_random==1', {
+      'defines': [
+        'SEED_ONLY_DEV_URANDOM',
+      ]
+    }],
     [ 'OS=="mac"', {
       'conditions': [
         [ 'target_arch=="ia32"', {
           'sources': [
             'mpi/mpi_sse2.s',
           ],
           'defines': [
             'MP_USE_UINT_DIGIT',
--- a/lib/freebl/sysrand.c
+++ b/lib/freebl/sysrand.c
@@ -3,14 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef FREEBL_NO_DEPEND
 #include "stubs.h"
 #endif
 
 #include "seccomon.h"
 
-#if defined(XP_UNIX) || defined(XP_BEOS)
+#if (defined(XP_UNIX) || defined(XP_BEOS)) && defined(SEED_ONLY_DEV_URANDOM)
+#include "unix_urandom.c"
+#elif defined(XP_UNIX) || defined(XP_BEOS)
 #include "unix_rand.c"
 #endif
 #ifdef XP_WIN
 #include "win_rand.c"
 #endif
--- a/lib/freebl/unix_rand.c
+++ b/lib/freebl/unix_rand.c
@@ -677,160 +677,26 @@ RNG_GetNoise(void *buf, size_t maxbytes)
     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;
@@ -855,22 +721,16 @@ RNG_SystemInfoForRNG(void)
         "/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
@@ -885,17 +745,16 @@ RNG_SystemInfoForRNG(void)
         }
         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 (!bytes) {
         PORT_SetError(SEC_ERROR_NEED_RANDOM);
     }
 
     /* If the user points us to a random file, pass it through the rng */
@@ -909,62 +768,32 @@ RNG_SystemInfoForRNG(void)
             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)
         return;
 #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;
new file mode 100644
--- /dev/null
+++ b/lib/freebl/unix_urandom.c
@@ -0,0 +1,50 @@
+/* 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 <fcntl.h>
+#include <unistd.h>
+#include "secerr.h"
+#include "secrng.h"
+#include "prprf.h"
+
+void
+RNG_SystemInfoForRNG(void)
+{
+    PRUint8 bytes[SYSTEM_RNG_SEED_COUNT];
+    size_t numBytes = RNG_SystemRNG(bytes, SYSTEM_RNG_SEED_COUNT);
+    if (!numBytes) {
+        /* error is set */
+        return;
+    }
+    RNG_RandomUpdate(bytes, numBytes);
+}
+
+size_t
+RNG_SystemRNG(void *dest, size_t maxLen)
+{
+    int fd;
+    int bytes;
+    size_t fileBytes = 0;
+    unsigned char *buffer = dest;
+
+    fd = open("/dev/urandom", O_RDONLY);
+    if (fd < 0) {
+        PORT_SetError(SEC_ERROR_NEED_RANDOM);
+        return 0;
+    }
+    while (fileBytes < maxLen) {
+        bytes = read(fd, buffer, maxLen - fileBytes);
+        if (bytes <= 0) {
+            break;
+        }
+        fileBytes += bytes;
+        buffer += bytes;
+    }
+    (void)close(fd);
+    if (fileBytes != maxLen) {
+        PORT_SetError(SEC_ERROR_NEED_RANDOM);
+        return 0;
+    }
+    return fileBytes;
+}