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