com php-src: Add support for getrandom(2), add type check on file descriptor: ext/standard/config.m4 ext/standard/random.c
ext/standard/tests/random/random_bytes.phpt
| From: | Anatol Belski | Date: | Tue, 29 Sep 2015 07:14:28 +0000 |
| Subject: | com php-src: Add support for getrandom(2), add type check on file descriptor: ext/standard/config.m4 ext/standard/random.c ext/standard/tests/random/random_bytes.phpt |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-89202@lists.php.net to get a copy of this message | ||
Commit: 6554f721f770c99037f07d465a7d610568576ce4
Author: Scott <scott@paragonie.com> Wed, 9 Sep 2015 06:50:06 -0400
Committer: Anatol Belski <ab@php.net> Tue, 29 Sep 2015 09:14:28 +0200
Parents: 208ed0a01059a32af38be5c3c5481c200c8ec4a2
Branches: PHP-7.0 master
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=6554f721f770c99037f07d465a7d610568576ce4
Log:
Add support for getrandom(2), add type check on file descriptor
Fix to_read, throw exception if syscall fails
Fixes thanks to feedback from sarnold at ##crypto on freenode
Correction on error conditions
Remove dead code (thanks @defuse)
It turns out getrandom can take >256, getentropy refuses.
Better semantics
Thanks @defuse for catching my silly mistake here
Cast to size_t to be explicit
Let's simplify the logic a bit
Let's be consistent; define everything before we do any logic
Continuously check that the file descriptor is still a valid one
Add device type check on fd initialization
Changed paths:
M ext/standard/config.m4
M ext/standard/random.c
M ext/standard/tests/random/random_bytes.phpt
Diff:
diff --git a/ext/standard/config.m4 b/ext/standard/config.m4
index c75b141..c435f96 100644
--- a/ext/standard/config.m4
+++ b/ext/standard/config.m4
@@ -598,6 +598,11 @@ dnl
AC_CHECK_DECLS([arc4random_buf])
dnl
+dnl Check for getrandom on newer Linux kernels
+dnl
+AC_CHECK_DECLS([getrandom])
+
+dnl
dnl Setup extension sources
dnl
PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32.c crypt.c \
diff --git a/ext/standard/random.c b/ext/standard/random.c
index e2e4895..6bee9d3 100644
--- a/ext/standard/random.c
+++ b/ext/standard/random.c
@@ -30,6 +30,9 @@
#if PHP_WIN32
# include "win32/winutil.h"
#endif
+#ifdef __linux__
+# include <linux/random.h>
+#endif
#ifdef ZTS
int random_globals_id;
@@ -75,6 +78,7 @@ PHP_MSHUTDOWN_FUNCTION(random)
/* }}} */
/* {{{ */
+
static int php_random_bytes(void *bytes, size_t size)
{
#if PHP_WIN32
@@ -85,26 +89,75 @@ static int php_random_bytes(void *bytes, size_t size)
}
#elif HAVE_DECL_ARC4RANDOM_BUF
arc4random_buf(bytes, size);
+#elif HAVE_DECL_GETRANDOM
+ /* Linux getrandom(2) syscall */
+ size_t read_bytes = 0;
+ size_t amount_to_read = 0;
+ ssize_t n;
+
+ /* Keep reading until we get enough entropy */
+ do {
+ amount_to_read = size - read_bytes;
+ /* Below, (bytes + read_bytes) is pointer arithmetic.
+
+ bytes read_bytes size
+ | | |
+ [#######=============] (we're going to write over the = region)
+ \\\\\\\\\\\\\
+ amount_to_read
+
+ */
+ n = getrandom(bytes + read_bytes, amount_to_read, 0);
+
+ if (n == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ /* Try again */
+ continue;
+ }
+ /*
+ If the syscall fails, we are doomed. The loop that calls
+ php_random_bytes should be terminated by the exception instead
+ of proceeding to demand more entropy.
+ */
+ zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data",
errno);
+ return FAILURE;
+ }
+
+ read_bytes += (size_t) n;
+ } while (read_bytes < size);
#else
int fd = RANDOM_G(fd);
+ struct stat st;
size_t read_bytes = 0;
+ ssize_t n;
if (fd < 0) {
-#if HAVE_DEV_ARANDOM
- fd = open("/dev/arandom", O_RDONLY);
-#elif HAVE_DEV_URANDOM
+#if HAVE_DEV_URANDOM
fd = open("/dev/urandom", O_RDONLY);
#endif
if (fd < 0) {
zend_throw_exception(zend_ce_exception, "Cannot open source device", 0);
return FAILURE;
}
-
+ /* Does the file exist and is it a character device? */
+ if (fstat(fd, &st) != 0 || !S_ISCHR(st.st_mode)) {
+ close(fd);
+ zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
+ return FAILURE;
+ }
+#ifdef __linux__
+ // Make sure that /dev/urandom is the proper urandom device on Linux
+ if (st.st_rdev != makedev(1, 9)) {
+ close(fd);
+ zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
+ return FAILURE;
+ }
+#endif
RANDOM_G(fd) = fd;
}
while (read_bytes < size) {
- ssize_t n = read(fd, bytes + read_bytes, size - read_bytes);
+ n = read(fd, bytes + read_bytes, size - read_bytes);
if (n <= 0) {
break;
}
diff --git a/ext/standard/tests/random/random_bytes.phpt
b/ext/standard/tests/random/random_bytes.phpt
index 8639138..6a7d438 100644
--- a/ext/standard/tests/random/random_bytes.phpt
+++ b/ext/standard/tests/random/random_bytes.phpt
@@ -8,7 +8,10 @@ var_dump(strlen(bin2hex(random_bytes(16))));
var_dump(is_string(random_bytes(10)));
+var_dump(is_string(random_bytes(257)));
+
?>
--EXPECT--
int(32)
bool(true)
+bool(true)