1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Zeev Suraski <zeev@php.net> |
16 * Pierre Joye <pierre@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include "php.h"
21 #include "winutil.h"
22 #include "codepage.h"
23 #include <bcrypt.h>
24 #include <lmcons.h>
25 #include <imagehlp.h>
26
27
php_win32_error_to_msg(HRESULT error)28 PHP_WINUTIL_API char *php_win32_error_to_msg(HRESULT error)
29 {/*{{{*/
30 wchar_t *bufw = NULL;
31 char *buf;
32
33 DWORD ret = FormatMessageW(
34 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
35 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&bufw, 0, NULL
36 );
37
38 if (!ret || !bufw) {
39 return "";
40 }
41
42 buf = php_win32_cp_conv_w_to_any(bufw, ret, PHP_WIN32_CP_IGNORE_LEN_P);
43
44 LocalFree(bufw);
45
46 return (buf ? buf : "");
47 }/*}}}*/
48
php_win32_error_msg_free(char * msg)49 PHP_WINUTIL_API void php_win32_error_msg_free(char *msg)
50 {/*{{{*/
51 if (msg && msg[0]) {
52 free(msg);
53 }
54 }/*}}}*/
55
php_win32_check_trailing_space(const char * path,const size_t path_len)56 int php_win32_check_trailing_space(const char * path, const size_t path_len)
57 {/*{{{*/
58 if (path_len > MAXPATHLEN - 1) {
59 return 1;
60 }
61 if (path) {
62 if (path[0] == ' ' || path[path_len - 1] == ' ') {
63 return 0;
64 } else {
65 return 1;
66 }
67 } else {
68 return 0;
69 }
70 }/*}}}*/
71
72 static BCRYPT_ALG_HANDLE bcrypt_algo;
73 static BOOL has_bcrypt_algo = 0;
74
75 #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
76
77 #ifdef PHP_EXPORTS
php_win32_shutdown_random_bytes(void)78 BOOL php_win32_shutdown_random_bytes(void)
79 {/*{{{*/
80 BOOL ret = TRUE;
81
82 if (has_bcrypt_algo) {
83 ret = NT_SUCCESS(BCryptCloseAlgorithmProvider(bcrypt_algo, 0));
84 has_bcrypt_algo = 0;
85 }
86
87 return ret;
88 }/*}}}*/
89
php_win32_init_random_bytes(void)90 BOOL php_win32_init_random_bytes(void)
91 {/*{{{*/
92 if (has_bcrypt_algo) {
93 return TRUE;
94 }
95
96 has_bcrypt_algo = NT_SUCCESS(BCryptOpenAlgorithmProvider(&bcrypt_algo, BCRYPT_RNG_ALGORITHM, NULL, 0));
97
98 return has_bcrypt_algo;
99 }/*}}}*/
100 #endif
101
php_win32_get_random_bytes(unsigned char * buf,size_t size)102 PHP_WINUTIL_API int php_win32_get_random_bytes(unsigned char *buf, size_t size)
103 { /* {{{ */
104
105 BOOL ret;
106
107 #if 0
108 /* Currently we fail on startup, with CNG API it shows no regressions so far and is secure.
109 Should switch on and try to reinit, if it fails too often on startup. This means also
110 bringing locks back. */
111 if (has_bcrypt_algo == 0) {
112 return FAILURE;
113 }
114 #endif
115
116 /* No sense to loop here, the limit is huge enough. */
117 ret = NT_SUCCESS(BCryptGenRandom(bcrypt_algo, buf, (ULONG)size, 0));
118
119 return ret ? SUCCESS : FAILURE;
120 }
121 /* }}} */
122
123
124 /*
125 * This functions based on the code from the UNIXem project under
126 * the BSD like license. Modified for PHP by ab@php.net
127 *
128 * Home: http://synesis.com.au/software/
129 *
130 * Copyright (c) 2005-2010, Matthew Wilson and Synesis Software
131 */
132
php_win32_code_to_errno(unsigned long w32Err)133 PHP_WINUTIL_API int php_win32_code_to_errno(unsigned long w32Err)
134 {/*{{{*/
135 size_t i;
136
137 struct code_to_errno_map
138 {
139 unsigned long w32Err;
140 int eerrno;
141 };
142
143 static const struct code_to_errno_map errmap[] =
144 {
145 /* 1 */ { ERROR_INVALID_FUNCTION , EINVAL }
146 /* 2 */ , { ERROR_FILE_NOT_FOUND , ENOENT }
147 /* 3 */ , { ERROR_PATH_NOT_FOUND , ENOENT }
148 /* 4 */ , { ERROR_TOO_MANY_OPEN_FILES , EMFILE }
149 /* 5 */ , { ERROR_ACCESS_DENIED , EACCES }
150 /* 6 */ , { ERROR_INVALID_HANDLE , EBADF }
151 /* 7 */ , { ERROR_ARENA_TRASHED , ENOMEM }
152 /* 8 */ , { ERROR_NOT_ENOUGH_MEMORY , ENOMEM }
153 /* 9 */ , { ERROR_INVALID_BLOCK , ENOMEM }
154 /* 10 */ , { ERROR_BAD_ENVIRONMENT , E2BIG }
155 /* 11 */ , { ERROR_BAD_FORMAT , ENOEXEC }
156 /* 12 */ , { ERROR_INVALID_ACCESS , EINVAL }
157 /* 13 */ , { ERROR_INVALID_DATA , EINVAL }
158 /* 14 */ , { ERROR_OUTOFMEMORY , ENOMEM }
159 /* 15 */ , { ERROR_INVALID_DRIVE , ENOENT }
160 /* 16 */ , { ERROR_CURRENT_DIRECTORY , ECURDIR }
161 /* 17 */ , { ERROR_NOT_SAME_DEVICE , EXDEV }
162 /* 18 */ , { ERROR_NO_MORE_FILES , ENOENT }
163 /* 19 */ , { ERROR_WRITE_PROTECT , EROFS }
164 /* 20 */ , { ERROR_BAD_UNIT , ENXIO }
165 /* 21 */ , { ERROR_NOT_READY , EBUSY }
166 /* 22 */ , { ERROR_BAD_COMMAND , EIO }
167 /* 23 */ , { ERROR_CRC , EIO }
168 /* 24 */ , { ERROR_BAD_LENGTH , EIO }
169 /* 25 */ , { ERROR_SEEK , EIO }
170 /* 26 */ , { ERROR_NOT_DOS_DISK , EIO }
171 /* 27 */ , { ERROR_SECTOR_NOT_FOUND , ENXIO }
172 /* 28 */ , { ERROR_OUT_OF_PAPER , EBUSY }
173 /* 29 */ , { ERROR_WRITE_FAULT , EIO }
174 /* 30 */ , { ERROR_READ_FAULT , EIO }
175 /* 31 */ , { ERROR_GEN_FAILURE , EIO }
176 /* 32 */ , { ERROR_SHARING_VIOLATION , EAGAIN }
177 /* 33 */ , { ERROR_LOCK_VIOLATION , EACCES }
178 /* 34 */ , { ERROR_WRONG_DISK , ENXIO }
179 /* 35 */ , { 35 , ENFILE }
180 /* 36 */ , { ERROR_SHARING_BUFFER_EXCEEDED , ENFILE }
181 /* 37 */ , { ERROR_HANDLE_EOF , EINVAL }
182 /* 38 */ , { ERROR_HANDLE_DISK_FULL , ENOSPC }
183 #if 0
184 /* 39 */ , { 0 , 0 }
185 /* 40 */ , { 0 , 0 }
186 /* 41 */ , { 0 , 0 }
187 /* 42 */ , { 0 , 0 }
188 /* 43 */ , { 0 , 0 }
189 /* 44 */ , { 0 , 0 }
190 /* 45 */ , { 0 , 0 }
191 /* 46 */ , { 0 , 0 }
192 /* 47 */ , { 0 , 0 }
193 /* 48 */ , { 0 , 0 }
194 /* 49 */ , { 0 , 0 }
195 #endif
196 /* 50 */ , { ERROR_NOT_SUPPORTED , ENOSYS }
197 #if 0
198 /* 51 */ , { 0 , 0 }
199 /* 52 */ , { 0 , 0 }
200 #endif
201 /* 53 */ , { ERROR_BAD_NETPATH , ENOENT }
202 #if 0
203 /* 54 */ , { 0 , 0 }
204 /* 55 */ , { 0 , 0 }
205 /* 56 */ , { 0 , 0 }
206 /* 57 */ , { 0 , 0 }
207 /* 58 */ , { 0 , 0 }
208 /* 59 */ , { 0 , 0 }
209 /* 60 */ , { 0 , 0 }
210 /* 61 */ , { 0 , 0 }
211 /* 62 */ , { 0 , 0 }
212 /* 63 */ , { 0 , 0 }
213 /* 64 */ , { 0 , 0 }
214 #endif
215 /* 65 */ , { ERROR_NETWORK_ACCESS_DENIED , EACCES }
216 #if 0
217 /* 66 */ , { 0 , 0 }
218 #endif
219 /* 67 */ , { ERROR_BAD_NET_NAME , ENOENT }
220 #if 0
221 /* 68 */ , { 0 , 0 }
222 /* 69 */ , { 0 , 0 }
223 /* 70 */ , { 0 , 0 }
224 /* 71 */ , { 0 , 0 }
225 /* 72 */ , { 0 , 0 }
226 /* 73 */ , { 0 , 0 }
227 /* 74 */ , { 0 , 0 }
228 /* 75 */ , { 0 , 0 }
229 /* 76 */ , { 0 , 0 }
230 /* 77 */ , { 0 , 0 }
231 /* 78 */ , { 0 , 0 }
232 /* 79 */ , { 0 , 0 }
233 #endif
234 /* 80 */ , { ERROR_FILE_EXISTS , EEXIST }
235 #if 0
236 /* 81 */ , { 0 , 0 }
237 #endif
238 /* 82 */ , { ERROR_CANNOT_MAKE , EACCES }
239 /* 83 */ , { ERROR_FAIL_I24 , EACCES }
240 #if 0
241 /* 84 */ , { 0 , 0 }
242 /* 85 */ , { 0 , 0 }
243 /* 86 */ , { 0 , 0 }
244 #endif
245 /* 87 */ , { ERROR_INVALID_PARAMETER , EINVAL }
246 #if 0
247 /* 88 */ , { 0 , 0 }
248 #endif
249 /* 89 */ , { ERROR_NO_PROC_SLOTS , EAGAIN }
250 #if 0
251 /* 90 */ , { 0 , 0 }
252 /* 91 */ , { 0 , 0 }
253 /* 92 */ , { 0 , 0 }
254 /* 93 */ , { 0 , 0 }
255 /* 94 */ , { 0 , 0 }
256 /* 95 */ , { 0 , 0 }
257 /* 96 */ , { 0 , 0 }
258 /* 97 */ , { 0 , 0 }
259 /* 98 */ , { 0 , 0 }
260 /* 99 */ , { 0 , 0 }
261 /* 100 */ , { 0 , 0 }
262 /* 101 */ , { 0 , 0 }
263 /* 102 */ , { 0 , 0 }
264 /* 103 */ , { 0 , 0 }
265 /* 104 */ , { 0 , 0 }
266 /* 105 */ , { 0 , 0 }
267 /* 106 */ , { 0 , 0 }
268 /* 107 */ , { 0 , 0 }
269 #endif
270 /* 108 */ , { ERROR_DRIVE_LOCKED , EACCES }
271 /* 109 */ , { ERROR_BROKEN_PIPE , EPIPE }
272 #if 0
273 /* 110 */ , { 0 , 0 }
274 #endif
275 /* 111 */ , { ERROR_BUFFER_OVERFLOW , ENAMETOOLONG }
276 /* 112 */ , { ERROR_DISK_FULL , ENOSPC }
277 #if 0
278 /* 113 */ , { 0 , 0 }
279 #endif
280 /* 114 */ , { ERROR_INVALID_TARGET_HANDLE , EBADF }
281 #if 0
282 /* 115 */ , { 0 , 0 }
283 /* 116 */ , { 0 , 0 }
284 /* 117 */ , { 0 , 0 }
285 /* 118 */ , { 0 , 0 }
286 /* 119 */ , { 0 , 0 }
287 /* 120 */ , { 0 , 0 }
288 /* 121 */ , { 0 , 0 }
289 #endif
290 /* 122 */ , { ERROR_INSUFFICIENT_BUFFER , ERANGE }
291 /* 123 */ , { ERROR_INVALID_NAME , ENOENT }
292 /* 124 */ , { ERROR_INVALID_HANDLE , EINVAL }
293 #if 0
294 /* 125 */ , { 0 , 0 }
295 #endif
296 /* 126 */ , { ERROR_MOD_NOT_FOUND , ENOENT }
297 /* 127 */ , { ERROR_PROC_NOT_FOUND , ENOENT }
298 /* 128 */ , { ERROR_WAIT_NO_CHILDREN , ECHILD }
299 /* 129 */ , { ERROR_CHILD_NOT_COMPLETE , ECHILD }
300 /* 130 */ , { ERROR_DIRECT_ACCESS_HANDLE , EBADF }
301 /* 131 */ , { ERROR_NEGATIVE_SEEK , EINVAL }
302 /* 132 */ , { ERROR_SEEK_ON_DEVICE , EACCES }
303 #if 0
304 /* 133 */ , { 0 , 0 }
305 /* 134 */ , { 0 , 0 }
306 /* 135 */ , { 0 , 0 }
307 /* 136 */ , { 0 , 0 }
308 /* 137 */ , { 0 , 0 }
309 /* 138 */ , { 0 , 0 }
310 /* 139 */ , { 0 , 0 }
311 /* 140 */ , { 0 , 0 }
312 /* 141 */ , { 0 , 0 }
313 /* 142 */ , { 0 , 0 }
314 /* 143 */ , { 0 , 0 }
315 /* 144 */ , { 0 , 0 }
316 #endif
317 /* 145 */ , { ERROR_DIR_NOT_EMPTY , ENOTEMPTY }
318 #if 0
319 /* 146 */ , { 0 , 0 }
320 /* 147 */ , { 0 , 0 }
321 /* 148 */ , { 0 , 0 }
322 /* 149 */ , { 0 , 0 }
323 /* 150 */ , { 0 , 0 }
324 /* 151 */ , { 0 , 0 }
325 /* 152 */ , { 0 , 0 }
326 /* 153 */ , { 0 , 0 }
327 /* 154 */ , { 0 , 0 }
328 /* 155 */ , { 0 , 0 }
329 /* 156 */ , { 0 , 0 }
330 /* 157 */ , { 0 , 0 }
331 #endif
332 /* 158 */ , { ERROR_NOT_LOCKED , EACCES }
333 #if 0
334 /* 159 */ , { 0 , 0 }
335 /* 160 */ , { 0 , 0 }
336 #endif
337 /* 161 */ , { ERROR_BAD_PATHNAME , ENOENT }
338 #if 0
339 /* 162 */ , { 0 , 0 }
340 /* 163 */ , { 0 , 0 }
341 #endif
342 /* 164 */ , { ERROR_MAX_THRDS_REACHED , EAGAIN }
343 #if 0
344 /* 165 */ , { 0 , 0 }
345 /* 166 */ , { 0 , 0 }
346 #endif
347 /* 167 */ , { ERROR_LOCK_FAILED , EACCES }
348 #if 0
349 /* 168 */ , { 0 , 0 }
350 /* 169 */ , { 0 , 0 }
351 /* 170 */ , { 0 , 0 }
352 /* 171 */ , { 0 , 0 }
353 /* 172 */ , { 0 , 0 }
354 /* 173 */ , { 0 , 0 }
355 /* 174 */ , { 0 , 0 }
356 /* 175 */ , { 0 , 0 }
357 /* 176 */ , { 0 , 0 }
358 /* 177 */ , { 0 , 0 }
359 /* 178 */ , { 0 , 0 }
360 /* 179 */ , { 0 , 0 }
361 /* 180 */ , { 0 , 0 }
362 /* 181 */ , { 0 , 0 }
363 /* 182 */ , { 0 , 0 }
364 #endif
365 /* 183 */ , { ERROR_ALREADY_EXISTS , EEXIST }
366 #if 0
367 /* 184 */ , { 0 , 0 }
368 /* 185 */ , { 0 , 0 }
369 /* 186 */ , { 0 , 0 }
370 /* 187 */ , { 0 , 0 }
371 /* 188 */ , { 0 , 0 }
372 /* 189 */ , { 0 , 0 }
373 /* 190 */ , { 0 , 0 }
374 /* 191 */ , { 0 , 0 }
375 /* 192 */ , { 0 , 0 }
376 /* 193 */ , { 0 , 0 }
377 /* 194 */ , { 0 , 0 }
378 /* 195 */ , { 0 , 0 }
379 /* 196 */ , { 0 , 0 }
380 /* 197 */ , { 0 , 0 }
381 /* 198 */ , { 0 , 0 }
382 /* 199 */ , { 0 , 0 }
383 #endif
384
385 /* 206 */ , { ERROR_FILENAME_EXCED_RANGE , ENAMETOOLONG }
386
387 /* 215 */ , { ERROR_NESTING_NOT_ALLOWED , EAGAIN }
388 /* 258 */ , { WAIT_TIMEOUT, ETIME}
389
390 /* 267 */ , { ERROR_DIRECTORY , ENOTDIR }
391 /* 336 */ , { ERROR_DIRECTORY_NOT_SUPPORTED , EISDIR }
392
393 /* 996 */ , { ERROR_IO_INCOMPLETE , EAGAIN }
394 /* 997 */ , { ERROR_IO_PENDING , EAGAIN }
395
396 /* 1004 */ , { ERROR_INVALID_FLAGS , EINVAL }
397 /* 1113 */ , { ERROR_NO_UNICODE_TRANSLATION , EINVAL }
398 /* 1168 */ , { ERROR_NOT_FOUND , ENOENT }
399 /* 1224 */ , { ERROR_USER_MAPPED_FILE , EACCES }
400 /* 1314 */ , { ERROR_PRIVILEGE_NOT_HELD , EACCES }
401 /* 1816 */ , { ERROR_NOT_ENOUGH_QUOTA , ENOMEM }
402 , { ERROR_ABANDONED_WAIT_0 , EIO }
403 /* 1464 */ , { ERROR_SYMLINK_NOT_SUPPORTED , EINVAL }
404 /* 4390 */ , { ERROR_NOT_A_REPARSE_POINT , EINVAL }
405 };
406
407 for(i = 0; i < sizeof(errmap)/sizeof(struct code_to_errno_map); ++i)
408 {
409 if(w32Err == errmap[i].w32Err)
410 {
411 return errmap[i].eerrno;
412 }
413 }
414
415 assert(!"Unrecognised value");
416
417 return EINVAL;
418 }/*}}}*/
419
php_win32_get_username(void)420 PHP_WINUTIL_API char *php_win32_get_username(void)
421 {/*{{{*/
422 wchar_t unamew[UNLEN + 1];
423 size_t uname_len;
424 char *uname;
425 DWORD unsize = UNLEN;
426
427 GetUserNameW(unamew, &unsize);
428 uname = php_win32_cp_conv_w_to_any(unamew, unsize - 1, &uname_len);
429 if (!uname) {
430 return NULL;
431 }
432
433 /* Ensure the length doesn't overflow. */
434 if (uname_len > UNLEN) {
435 uname[uname_len] = '\0';
436 }
437
438 return uname;
439 }/*}}}*/
440
is_compatible(const char * name,BOOL is_smaller,char * format,char ** err)441 static zend_always_inline BOOL is_compatible(const char *name, BOOL is_smaller, char *format, char **err)
442 {/*{{{*/
443 /* work around ImageLoad() issue */
444 char *name_stripped = name;
445 if (name[0] == '.' && IS_SLASH(name[1])) {
446 name_stripped += 2;
447 }
448
449 PLOADED_IMAGE img = ImageLoad(name_stripped, NULL);
450
451 if (!img) {
452 DWORD _err = GetLastError();
453 char *err_txt = php_win32_error_to_msg(_err);
454 spprintf(err, 0, "Failed to load %s, %s", name, err_txt);
455 free(err_txt);
456 return FALSE;
457 }
458
459 DWORD major = img->FileHeader->OptionalHeader.MajorLinkerVersion;
460 DWORD minor = img->FileHeader->OptionalHeader.MinorLinkerVersion;
461
462 #if PHP_LINKER_MAJOR == 14
463 /* VS 2015, 2017 and 2019 are binary compatible, but only forward compatible.
464 It should be fine, if we load a module linked with an older one into
465 the core linked with the newer one, but not the otherway round.
466 Analogously, it should be fine, if a PHP build linked with an older version
467 is used with a newer CRT, but not the other way round.
468 Otherwise, if the linker major version is not same, it is an error, as
469 per the current knowledge.
470
471 This check is to be extended as new VS versions come out. */
472 DWORD core_minor = (DWORD)(PHP_LINKER_MINOR/10);
473 DWORD comp_minor = (DWORD)(minor/10);
474 if (14 == major && (is_smaller ? core_minor < comp_minor : core_minor > comp_minor) || PHP_LINKER_MAJOR != major)
475 #else
476 if (PHP_LINKER_MAJOR != major)
477 #endif
478 {
479 spprintf(err, 0, format, name, major, minor, PHP_LINKER_MAJOR, PHP_LINKER_MINOR);
480 ImageUnload(img);
481 return FALSE;
482 }
483 ImageUnload(img);
484
485 return TRUE;
486 }/*}}}*/
487
php_win32_image_compatible(const char * name,char ** err)488 PHP_WINUTIL_API BOOL php_win32_image_compatible(const char *name, char **err)
489 {/*{{{*/
490 return is_compatible(name, TRUE, "Can't load module '%s' as it's linked with %u.%u, but the core is linked with %d.%d", err);
491 }/*}}}*/
492
493 /* Expect a CRT name DLL. */
php_win32_crt_compatible(const char * name,char ** err)494 PHP_WINUTIL_API BOOL php_win32_crt_compatible(const char *name, char **err)
495 {/*{{{*/
496 return is_compatible(name, FALSE, "'%s' %u.%u is not compatible with this PHP build linked with %d.%d", err);
497 }/*}}}*/
498
499