This source file includes following definitions.
- random_globals_ctor
- random_globals_dtor
- PHP_MINIT_FUNCTION
- PHP_MSHUTDOWN_FUNCTION
- php_random_bytes
- PHP_FUNCTION
- PHP_FUNCTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 #include <stdlib.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <math.h>
25
26 #include "php.h"
27 #include "zend_exceptions.h"
28 #include "php_random.h"
29
30 #if PHP_WIN32
31 # include "win32/winutil.h"
32 #endif
33 #ifdef __linux__
34 # include <sys/syscall.h>
35 #endif
36 #if defined(__OpenBSD__) || defined(__NetBSD__)
37 # include <sys/param.h>
38 #endif
39
40 #ifdef ZTS
41 int random_globals_id;
42 #else
43 php_random_globals random_globals;
44 #endif
45
46 static void random_globals_ctor(php_random_globals *random_globals_p)
47 {
48 random_globals_p->fd = -1;
49 }
50
51 static void random_globals_dtor(php_random_globals *random_globals_p)
52 {
53 if (random_globals_p->fd > 0) {
54 close(random_globals_p->fd);
55 random_globals_p->fd = -1;
56 }
57 }
58
59
60 PHP_MINIT_FUNCTION(random)
61 {
62 #ifdef ZTS
63 ts_allocate_id(&random_globals_id, sizeof(php_random_globals), (ts_allocate_ctor)random_globals_ctor, (ts_allocate_dtor)random_globals_dtor);
64 #else
65 random_globals_ctor(&random_globals);
66 #endif
67
68 return SUCCESS;
69 }
70
71
72
73 PHP_MSHUTDOWN_FUNCTION(random)
74 {
75 #ifndef ZTS
76 random_globals_dtor(&random_globals);
77 #endif
78
79 return SUCCESS;
80 }
81
82
83
84
85 PHPAPI int php_random_bytes(void *bytes, size_t size, zend_bool should_throw)
86 {
87 #if PHP_WIN32
88
89 if (php_win32_get_random_bytes(bytes, size) == FAILURE) {
90 if (should_throw) {
91 zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0);
92 }
93 return FAILURE;
94 }
95 #elif HAVE_DECL_ARC4RANDOM_BUF && ((defined(__OpenBSD__) && OpenBSD >= 201405) || (defined(__NetBSD__) && __NetBSD_Version__ >= 700000001))
96 arc4random_buf(bytes, size);
97 #elif HAVE_DECL_GETRANDOM
98
99 size_t read_bytes = 0;
100 size_t amount_to_read = 0;
101 ssize_t n;
102
103
104 do {
105
106
107
108
109
110
111
112
113
114 amount_to_read = size - read_bytes;
115 n = syscall(SYS_getrandom, bytes + read_bytes, amount_to_read, 0);
116
117 if (n == -1) {
118 if (errno == EINTR || errno == EAGAIN) {
119
120 continue;
121 }
122
123
124
125
126
127 if (should_throw) {
128 zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", errno);
129 }
130 return FAILURE;
131 }
132
133 read_bytes += (size_t) n;
134 } while (read_bytes < size);
135 #else
136 int fd = RANDOM_G(fd);
137 struct stat st;
138 size_t read_bytes = 0;
139 ssize_t n;
140
141 if (fd < 0) {
142 #if HAVE_DEV_URANDOM
143 fd = open("/dev/urandom", O_RDONLY);
144 #endif
145 if (fd < 0) {
146 if (should_throw) {
147 zend_throw_exception(zend_ce_exception, "Cannot open source device", 0);
148 }
149 return FAILURE;
150 }
151
152 if (fstat(fd, &st) != 0 ||
153 # ifdef S_ISNAM
154 !(S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode))
155 # else
156 !S_ISCHR(st.st_mode)
157 # endif
158 ) {
159 close(fd);
160 if (should_throw) {
161 zend_throw_exception(zend_ce_exception, "Error reading from source device", 0);
162 }
163 return FAILURE;
164 }
165 RANDOM_G(fd) = fd;
166 }
167
168 while (read_bytes < size) {
169 n = read(fd, bytes + read_bytes, size - read_bytes);
170 if (n <= 0) {
171 break;
172 }
173 read_bytes += n;
174 }
175
176 if (read_bytes < size) {
177 if (should_throw) {
178 zend_throw_exception(zend_ce_exception, "Could not gather sufficient random data", 0);
179 }
180 return FAILURE;
181 }
182 #endif
183
184 return SUCCESS;
185 }
186
187
188
189
190 PHP_FUNCTION(random_bytes)
191 {
192 zend_long size;
193 zend_string *bytes;
194
195 if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "l", &size) == FAILURE) {
196 return;
197 }
198
199 if (size < 1) {
200 zend_throw_exception(zend_ce_error, "Length must be greater than 0", 0);
201 return;
202 }
203
204 bytes = zend_string_alloc(size, 0);
205
206 if (php_random_bytes_throw(ZSTR_VAL(bytes), size) == FAILURE) {
207 zend_string_release(bytes);
208 return;
209 }
210
211 ZSTR_VAL(bytes)[size] = '\0';
212
213 RETURN_STR(bytes);
214 }
215
216
217
218
219 PHP_FUNCTION(random_int)
220 {
221 zend_long min;
222 zend_long max;
223 zend_ulong umax;
224 zend_ulong result;
225
226 if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "ll", &min, &max) == FAILURE) {
227 return;
228 }
229
230 if (min > max) {
231 zend_throw_exception(zend_ce_error, "Minimum value must be less than or equal to the maximum value", 0);
232 return;
233 }
234
235 if (min == max) {
236 RETURN_LONG(min);
237 }
238
239 umax = max - min;
240
241 if (php_random_bytes_throw(&result, sizeof(result)) == FAILURE) {
242 return;
243 }
244
245
246 if (umax == ZEND_ULONG_MAX) {
247 RETURN_LONG((zend_long)result);
248 }
249
250
251 umax++;
252
253
254 if ((umax & (umax - 1)) != 0) {
255
256 zend_ulong limit = ZEND_ULONG_MAX - (ZEND_ULONG_MAX % umax) - 1;
257
258
259 while (result > limit) {
260 if (php_random_bytes_throw(&result, sizeof(result)) == FAILURE) {
261 return;
262 }
263 }
264 }
265
266 RETURN_LONG((zend_long)((result % umax) + min));
267 }
268
269
270
271
272
273
274
275
276
277