summaryrefslogtreecommitdiff
path: root/util/nvmutil/lib
diff options
context:
space:
mode:
Diffstat (limited to 'util/nvmutil/lib')
-rw-r--r--util/nvmutil/lib/num.c122
1 files changed, 105 insertions, 17 deletions
diff --git a/util/nvmutil/lib/num.c b/util/nvmutil/lib/num.c
index 62a3b286..48c8dee1 100644
--- a/util/nvmutil/lib/num.c
+++ b/util/nvmutil/lib/num.c
@@ -17,6 +17,7 @@
#endif
#include <limits.h>
#include <stddef.h>
+#include <string.h>
#include <unistd.h>
#include "../include/common.h"
@@ -57,36 +58,123 @@ rlong(void)
return rval;
#else
- int fd;
+ static int fd = -1;
+ static long nr = -1;
+ static unsigned long off = 0;
+#if defined (BUFSIZ)
+ static char rbuf[BUFSIZ];
+#else
+#ifndef PORTABLE
+ static char rbuf[4096];
+#elif ((PORTABLE) > 0)
+ static char rbuf[256]; /* scarce memory on old systems */
+#else
+ static char rbuf[4096]; /* typical 32-bit BUFSIZ */
+#endif
+#endif
+ unsigned long rval;
+ long new_nr;
- long nr;
+ /* reading from urandom is inherently
+ * unreliable on old systems, even if
+ * newer systems make it more reliable
+ *
+ * modern linux/bsd make it safe, but
+ * we have to assume that someone is
+ * compiling this on linux from 1999
+ *
+ * this logic therefore applies various
+ * tricks to mitigate possible os bugs
+ */
- unsigned long rval;
+ int retries = 0;
+ int max_retries = 100;
- fd = open("/dev/urandom", O_RDONLY | O_BINARY);
+retry_urandom_read:
+
+ if (++retries > max_retries)
+ goto rlong_next;
+
+ if (nr < 0 || nr < (long)sizeof(unsigned long)) {
+
+ if (fd < 0) {
+
+ fd = open("/dev/urandom",
+ O_RDONLY | O_BINARY | O_NOFOLLOW |
+ O_CLOEXEC);
#ifdef __OpenBSD__
- if (fd < 0) /* old openbsd */
- fd = open("/dev/arandom", O_RDONLY | O_BINARY);
+ if (fd < 0) /* old openbsd */
+ fd = open("/dev/arandom",
+ O_RDONLY | O_BINARY | O_NOFOLLOW |
+ O_CLOEXEC);
+#endif
+
+#ifdef PORTABLE_RAND_DEV
+#if (PORTABLE_RAND_DEV) > 0
+ /* WARNING:
+ * /dev/random may block
+ * forever and does **NOT**
+ * guarantee better entropy
+ * on old systems
+ *
+ * only use it if needed
+ */
+
+ if (fd < 0)
+ fd = open("/dev/random",
+ O_RDONLY | O_BINARY | O_NOFOLLOW |
+ O_CLOEXEC);
+#endif
#endif
- if (fd < 0)
- fd = open("/dev/random", O_RDONLY | O_BINARY);
+ if (fd < 0)
+ goto retry_urandom_read;
+
+ retries = 0;
+ }
+
+ new_nr = rw_file_exact(fd, (unsigned char *)rbuf,
+ sizeof(rbuf), 0, IO_READ, LOOP_EAGAIN,
+ LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR);
+
+ if (new_nr < 0 || new_nr < (long)sizeof(rbuf))
+ goto retry_urandom_read;
+
+ /* only reset buffer after successful refill */
+ nr = new_nr;
+ off = 0;
- if (fd < 0)
- err(errno, "can't open random device");
+ /* to mitigate file descriptor
+ * injection, we do not re-use
+ * the same descriptor each time
+ */
+ (void) close_on_eintr(fd);
+ fd = -1;
+ }
- nr = rw_file_exact(fd, (unsigned char *)&rval,
- sizeof(unsigned long), 0, IO_READ, LOOP_EAGAIN,
- LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR);
+ fd = -1;
+ retries = 0;
- if (close_on_eintr(fd) < 0)
- err(errno, "Can't close randomness fd");
+ memcpy(&rval, rbuf + off, sizeof(unsigned long));
- if (nr != sizeof(unsigned long))
- err(errno, "Incomplete read from random device");
+ nr -= (long)sizeof(unsigned long);
+ off += sizeof(unsigned long);
return rval;
+
+rlong_next:
+
+ fd = -1;
+ off = 0;
+ nr = -1;
+
+/* TODO: will re-add timer-based fallback here #if defined(PORTABLE) */
+
+ err(EIO, "Can't read from /dev/[ua]random");
+ return 0;
+
+ /* add portable timers here */
#endif
}