/* {{{ */ static int php_random_bytes(void *bytes, size_t size) { #if PHP_WIN32 /* Defer to CryptGenRandom on Windows */ if (php_win32_get_random_bytes(bytes, size) == FAILURE) { zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0); return FAILURE; } #elif HAVE_DECL_ARC4RANDOM_BUF arc4random_buf(bytes, size); #else int fd = RANDOM_G(fd); size_t read_bytes = 0; if (fd < 0) { #if HAVE_DEV_ARANDOM fd = open("/dev/arandom", O_RDONLY); #elif 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; } RANDOM_G(fd) = fd; } while (read_bytes < size) { ssize_t n = read(fd, bytes + read_bytes, size - read_bytes); if (n <= 0) { break; } read_bytes += n; } if (read_bytes < size) { zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0); return FAILURE; } #endif return SUCCESS; }
static int php_password_make_salt(size_t length, char *ret) /* {{{ */ { int buffer_valid = 0; size_t i, raw_length; char *buffer; char *result; if (length > (INT_MAX / 3)) { php_error_docref(NULL, E_WARNING, "Length is too large to safely generate"); return FAILURE; } raw_length = length * 3 / 4 + 1; buffer = (char *) safe_emalloc(raw_length, 1, 1); #if PHP_WIN32 { BYTE *iv_b = (BYTE *) buffer; if (php_win32_get_random_bytes(iv_b, raw_length) == SUCCESS) { buffer_valid = 1; } } #else { int fd, n; size_t read_bytes = 0; fd = open("/dev/urandom", O_RDONLY); if (fd >= 0) { while (read_bytes < raw_length) { n = read(fd, buffer + read_bytes, raw_length - read_bytes); if (n < 0) { break; } read_bytes += (size_t) n; } close(fd); } if (read_bytes >= raw_length) { buffer_valid = 1; } } #endif if (!buffer_valid) { for (i = 0; i < raw_length; i++) { buffer[i] ^= (char) (255.0 * php_rand() / RAND_MAX); } } result = safe_emalloc(length, 1, 1); if (php_password_salt_to64(buffer, raw_length, length, result) == FAILURE) { php_error_docref(NULL, E_WARNING, "Generated salt too short"); efree(buffer); efree(result); return FAILURE; } memcpy(ret, result, length); efree(result); efree(buffer); ret[length] = 0; return SUCCESS; }
static int php_random_bytes(void *bytes, size_t size) { #if PHP_WIN32 /* Defer to CryptGenRandom on Windows */ if (php_win32_get_random_bytes(bytes, size) == FAILURE) { zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0); return FAILURE; } #elif HAVE_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001)) 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 = syscall(SYS_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_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 || # ifdef S_ISNAM !(S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode)) # else !S_ISCHR(st.st_mode) # endif ) { close(fd); zend_throw_exception(zend_ce_exception, "Error reading from source device", 0); return FAILURE; } RANDOM_G(fd) = fd; } while (read_bytes < size) { n = read(fd, bytes + read_bytes, size - read_bytes); if (n <= 0) { break; } read_bytes += n; } if (read_bytes < size) { zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0); return FAILURE; } #endif return SUCCESS; }
/* {{{ */ PHPAPI int php_random_bytes(void *bytes, size_t size, zend_bool should_throw) { #ifdef PHP_WIN32 /* Defer to CryptGenRandom on Windows */ if (php_win32_get_random_bytes(bytes, size) == FAILURE) { if (should_throw) { zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0); } return FAILURE; } #elif HAVE_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001)) arc4random_buf(bytes, size); #else size_t read_bytes = 0; ssize_t n; #if defined(__linux__) && defined(SYS_getrandom) /* Linux getrandom(2) syscall */ /* Keep reading until we get enough entropy */ while (read_bytes < size) { /* Below, (bytes + read_bytes) is pointer arithmetic. bytes read_bytes size | | | [#######=============] (we're going to write over the = region) \\\\\\\\\\\\\ amount_to_read */ size_t amount_to_read = size - read_bytes; n = syscall(SYS_getrandom, bytes + read_bytes, amount_to_read, 0); if (n == -1) { if (errno == ENOSYS) { /* This can happen if PHP was compiled against a newer kernel where getrandom() * is available, but then runs on an older kernel without getrandom(). If this * happens we simply fall back to reading from /dev/urandom. */ ZEND_ASSERT(read_bytes == 0); break; } else if (errno == EINTR || errno == EAGAIN) { /* Try again */ continue; } else { /* If the syscall fails, fall back to reading from /dev/urandom */ break; } } read_bytes += (size_t) n; } #endif if (read_bytes < size) { int fd = RANDOM_G(fd); struct stat st; if (fd < 0) { #if HAVE_DEV_URANDOM fd = open("/dev/urandom", O_RDONLY); #endif if (fd < 0) { if (should_throw) { 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 || # ifdef S_ISNAM !(S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode)) # else !S_ISCHR(st.st_mode) # endif ) { close(fd); if (should_throw) { zend_throw_exception(zend_ce_exception, "Error reading from source device", 0); } return FAILURE; } RANDOM_G(fd) = fd; } for (read_bytes = 0; read_bytes < size; read_bytes += (size_t) n) { n = read(fd, bytes + read_bytes, size - read_bytes); if (n <= 0) { break; } } if (read_bytes < size) { if (should_throw) { zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0); } return FAILURE; } } #endif return SUCCESS; }