Left: | ||
Right: |
OLD | NEW |
---|---|
1 #include "Python.h" | 1 #include "Python.h" |
2 #ifdef MS_WINDOWS | 2 #ifdef MS_WINDOWS |
3 #include <windows.h> | 3 #include <windows.h> |
4 #else | 4 #else |
5 #include <fcntl.h> | 5 #include <fcntl.h> |
6 #endif | 6 #endif |
7 | 7 |
8 #ifdef Py_DEBUG | 8 #ifdef Py_DEBUG |
9 int _Py_HashSecret_Initialized = 0; | 9 int _Py_HashSecret_Initialized = 0; |
10 #else | 10 #else |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
83 "secret using RAND_pseudo_bytes"); | 83 "secret using RAND_pseudo_bytes"); |
84 } | 84 } |
85 return -1; | 85 return -1; |
86 } | 86 } |
87 return 0; | 87 return 0; |
88 } | 88 } |
89 #endif /* __VMS */ | 89 #endif /* __VMS */ |
90 | 90 |
91 | 91 |
92 #if !defined(MS_WINDOWS) && !defined(__VMS) | 92 #if !defined(MS_WINDOWS) && !defined(__VMS) |
93 static int urandom_fd = -1; | |
94 static int urandom_errno = 0; | |
93 | 95 |
94 /* Read size bytes from /dev/urandom into buffer. | 96 /* Read size bytes from /dev/urandom into buffer. |
95 Call Py_FatalError() on error. */ | 97 Call Py_FatalError() on error. */ |
96 static void | 98 static void |
97 dev_urandom_noraise(char *buffer, Py_ssize_t size) | 99 dev_urandom_noraise(char *buffer, Py_ssize_t size) |
98 { | 100 { |
99 int fd; | 101 int fd = urandom_fd; |
100 Py_ssize_t n; | 102 Py_ssize_t n; |
101 | 103 |
102 assert (0 < size); | |
103 | |
104 fd = open("/dev/urandom", O_RDONLY); | |
105 if (fd < 0) | 104 if (fd < 0) |
106 Py_FatalError("Failed to open /dev/urandom"); | 105 Py_FatalError("Failed to open /dev/urandom"); |
106 assert (0 < size); | |
107 | 107 |
108 while (0 < size) | 108 while (0 < size) |
109 { | 109 { |
110 do { | 110 do { |
111 n = read(fd, buffer, (size_t)size); | 111 n = read(fd, buffer, (size_t)size); |
112 } while (n < 0 && errno == EINTR); | 112 } while (n < 0 && errno == EINTR); |
113 if (n <= 0) | 113 if (n <= 0) |
114 { | 114 { |
115 /* stop on error or if read(size) returned 0 */ | 115 /* stop on error or if read(size) returned 0 */ |
116 Py_FatalError("Failed to read bytes from /dev/urandom"); | 116 Py_FatalError("Failed to read bytes from /dev/urandom"); |
117 break; | 117 break; |
118 } | 118 } |
119 buffer += n; | 119 buffer += n; |
120 size -= (Py_ssize_t)n; | 120 size -= (Py_ssize_t)n; |
121 } | 121 } |
122 close(fd); | |
123 } | 122 } |
124 | 123 |
125 /* Read size bytes from /dev/urandom into buffer. | 124 /* Read size bytes from /dev/urandom into buffer. |
126 Return 0 on success, raise an exception and return -1 on error. */ | 125 Return 0 on success, raise an exception and return -1 on error. */ |
127 static int | 126 static int |
128 dev_urandom_python(char *buffer, Py_ssize_t size) | 127 dev_urandom_python(char *buffer, Py_ssize_t size) |
129 { | 128 { |
130 int fd; | 129 int fd = urandom_fd; |
131 Py_ssize_t n; | 130 Py_ssize_t n; |
132 | 131 |
133 if (size <= 0) | 132 if (size <= 0) |
134 return 0; | 133 return 0; |
135 | 134 |
136 Py_BEGIN_ALLOW_THREADS | 135 if (fd < 0) { |
137 fd = open("/dev/urandom", O_RDONLY); | 136 assert(urandom_errno != 0); |
138 Py_END_ALLOW_THREADS | 137 errno = urandom_errno; |
139 if (fd < 0) | |
140 { | |
141 if (errno == ENOENT || errno == ENXIO || | 138 if (errno == ENOENT || errno == ENXIO || |
142 errno == ENODEV || errno == EACCES) | 139 errno == ENODEV || errno == EACCES) |
143 PyErr_SetString(PyExc_NotImplementedError, | 140 PyErr_SetString(PyExc_NotImplementedError, |
144 "/dev/urandom (or equivalent) not found"); | 141 "/dev/urandom (or equivalent) not found"); |
145 else | 142 else |
146 PyErr_SetFromErrno(PyExc_OSError); | 143 PyErr_SetFromErrno(PyExc_OSError); |
147 return -1; | 144 return -1; |
148 } | 145 } |
149 | 146 |
150 Py_BEGIN_ALLOW_THREADS | 147 Py_BEGIN_ALLOW_THREADS |
151 do { | 148 do { |
152 do { | 149 do { |
153 n = read(fd, buffer, (size_t)size); | 150 n = read(fd, buffer, (size_t)size); |
154 } while (n < 0 && errno == EINTR); | 151 } while (n < 0 && errno == EINTR); |
155 if (n <= 0) | 152 if (n <= 0) |
156 break; | 153 break; |
157 buffer += n; | 154 buffer += n; |
158 size -= (Py_ssize_t)n; | 155 size -= (Py_ssize_t)n; |
159 } while (0 < size); | 156 } while (0 < size); |
160 Py_END_ALLOW_THREADS | 157 Py_END_ALLOW_THREADS |
161 | 158 |
162 if (n <= 0) | 159 if (n <= 0) |
163 { | 160 { |
164 /* stop on error or if read(size) returned 0 */ | 161 /* stop on error or if read(size) returned 0 */ |
165 if (n < 0) | 162 if (n < 0) { |
166 PyErr_SetFromErrno(PyExc_OSError); | 163 PyErr_SetFromErrno(PyExc_OSError); |
164 } | |
167 else | 165 else |
168 PyErr_Format(PyExc_RuntimeError, | 166 PyErr_Format(PyExc_RuntimeError, |
169 "Failed to read %zi bytes from /dev/urandom", | 167 "Failed to read %zi bytes from /dev/urandom", |
170 size); | 168 size); |
171 close(fd); | |
172 return -1; | 169 return -1; |
173 } | 170 } |
174 close(fd); | |
175 return 0; | 171 return 0; |
176 } | 172 } |
173 | |
174 static void | |
175 dev_urandom_init(void) | |
176 { | |
177 assert(urandom_fd < 0); | |
178 #ifdef O_CLOEXEC | |
179 urandom_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); | |
180 #else | |
181 urandom_fd = open("/dev/urandom", O_RDONLY); | |
182 #endif | |
183 if (urandom_fd < 0) | |
184 goto error; | |
185 else { | |
Benjamin Peterson
2013/08/24 21:54:05
Kill this else.
| |
186 urandom_errno = 0; | |
187 if (urandom_fd <= 2) { | |
188 /* Ok, Python was launched with at least a missing standard | |
189 stream, and our random fd took its place. Unfortunately, | |
190 now other parts of the interpreter (as well as, possibly, | |
191 some user code) will mistake the random fd for a proper | |
192 standard stream. So we'll try to reallocate the fd to | |
193 something else. */ | |
Charles-François Natali
2013/08/23 23:07:39
Holy crap :-)
But AFAICT this issue isn't specifi
AntoinePitrou
2013/08/23 23:14:41
Hash randomization must be initialized before ever
| |
194 int i = 0, dups[3], new_fd; | |
195 errno = 0; | |
196 while (1) { | |
197 new_fd = dup(urandom_fd); | |
198 if (new_fd < 0 || new_fd > 2) | |
199 break; | |
200 /* On the third dup() we *must* get either an error or | |
201 something greater than 2. */ | |
202 assert(i < 2); | |
203 dups[i++] = new_fd; | |
204 } | |
205 assert(i < 3); | |
206 close(urandom_fd); | |
207 while (--i >= 0) | |
208 close(dups[i]); | |
209 if (new_fd < 0) | |
210 goto error; | |
211 urandom_fd = new_fd; | |
212 } | |
213 } | |
214 return; | |
215 error: | |
216 /* If something failed, we don't report an error here, as it must still | |
217 be possible to launch Python if PYTHONHASHSEED is set to some fixed | |
218 value. Therefore, just record the error status for later. */ | |
219 urandom_fd = -1; | |
220 urandom_errno = errno; | |
221 } | |
222 | |
223 static void | |
224 dev_urandom_close(void) | |
225 { | |
226 if (urandom_fd >= 0) { | |
227 close(urandom_fd); | |
228 urandom_fd = -1; | |
229 } | |
230 } | |
231 | |
177 #endif /* !defined(MS_WINDOWS) && !defined(__VMS) */ | 232 #endif /* !defined(MS_WINDOWS) && !defined(__VMS) */ |
178 | 233 |
179 /* Fill buffer with pseudo-random bytes generated by a linear congruent | 234 /* Fill buffer with pseudo-random bytes generated by a linear congruent |
180 generator (LCG): | 235 generator (LCG): |
181 | 236 |
182 x(n+1) = (x(n) * 214013 + 2531011) % 2^32 | 237 x(n+1) = (x(n) * 214013 + 2531011) % 2^32 |
183 | 238 |
184 Use bits 23..16 of x(n) to generate a byte. */ | 239 Use bits 23..16 of x(n) to generate a byte. */ |
185 static void | 240 static void |
186 lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size) | 241 lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size) |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
222 # endif | 277 # endif |
223 #endif | 278 #endif |
224 } | 279 } |
225 | 280 |
226 void | 281 void |
227 _PyRandom_Init(void) | 282 _PyRandom_Init(void) |
228 { | 283 { |
229 char *env; | 284 char *env; |
230 void *secret = &_Py_HashSecret; | 285 void *secret = &_Py_HashSecret; |
231 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t); | 286 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t); |
287 | |
288 /* _PyRandom_Init() can be called twice (first by Python's main(), | |
289 second by Py_Initialize()). */ | |
232 | 290 |
233 if (_Py_HashSecret_Initialized) | 291 if (_Py_HashSecret_Initialized) |
234 return; | 292 return; |
235 _Py_HashSecret_Initialized = 1; | 293 _Py_HashSecret_Initialized = 1; |
236 | 294 |
295 #if !defined(MS_WINDOWS) && !defined(__VMS) | |
296 dev_urandom_init(); | |
297 #endif | |
298 | |
237 /* | 299 /* |
238 Hash randomization is enabled. Generate a per-process secret, | 300 Hash randomization is enabled. Generate a per-process secret, |
239 using PYTHONHASHSEED if provided. | 301 using PYTHONHASHSEED if provided. |
240 */ | 302 */ |
241 | 303 |
242 env = Py_GETENV("PYTHONHASHSEED"); | 304 env = Py_GETENV("PYTHONHASHSEED"); |
243 if (env && *env != '\0' && strcmp(env, "random") != 0) { | 305 if (env && *env != '\0' && strcmp(env, "random") != 0) { |
244 char *endptr = env; | 306 char *endptr = env; |
245 unsigned long seed; | 307 unsigned long seed; |
246 seed = strtoul(env, &endptr, 10); | 308 seed = strtoul(env, &endptr, 10); |
(...skipping 17 matching lines...) Expand all Loading... | |
264 (void)win32_urandom((unsigned char *)secret, secret_size, 0); | 326 (void)win32_urandom((unsigned char *)secret, secret_size, 0); |
265 #else /* #ifdef MS_WINDOWS */ | 327 #else /* #ifdef MS_WINDOWS */ |
266 # ifdef __VMS | 328 # ifdef __VMS |
267 vms_urandom((unsigned char *)secret, secret_size, 0); | 329 vms_urandom((unsigned char *)secret, secret_size, 0); |
268 # else | 330 # else |
269 dev_urandom_noraise((char*)secret, secret_size); | 331 dev_urandom_noraise((char*)secret, secret_size); |
270 # endif | 332 # endif |
271 #endif | 333 #endif |
272 } | 334 } |
273 } | 335 } |
336 | |
337 void | |
338 _PyRandom_Fini(void) | |
339 { | |
340 #if !defined(MS_WINDOWS) && !defined(__VMS) | |
341 dev_urandom_close(); | |
342 #endif | |
343 } | |
OLD | NEW |