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 | Authors: Daniel Beulshausen <daniel@php4win.de> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include <stdio.h>
18 #include <fcntl.h>
19 #include <io.h>
20 #include <process.h>
21 #include <time.h>
22 #include <errno.h>
23
24 #define TSRM_INCLUDE_FULL_WINDOWS_HEADERS
25 #include "SAPI.h"
26 #include "TSRM.h"
27
28 #ifdef TSRM_WIN32
29 #include <Sddl.h>
30 #include "tsrm_win32.h"
31 #include "zend_virtual_cwd.h"
32 #include "win32/ioutil.h"
33 #include "win32/winutil.h"
34
35 #ifdef ZTS
36 static ts_rsrc_id win32_globals_id;
37 #else
38 static tsrm_win32_globals win32_globals;
39 #endif
40
tsrm_win32_ctor(tsrm_win32_globals * globals)41 static void tsrm_win32_ctor(tsrm_win32_globals *globals)
42 {/*{{{*/
43 #ifdef ZTS
44 TSRMLS_CACHE_UPDATE();
45 #endif
46 globals->process = NULL;
47 globals->shm = NULL;
48 globals->process_size = 0;
49 globals->shm_size = 0;
50 globals->comspec = _strdup("cmd.exe");
51
52 /* Set it to INVALID_HANDLE_VALUE
53 * It will be initialized correctly in tsrm_win32_access or set to
54 * NULL if no impersonation has been done.
55 * the impersonated token can't be set here as the impersonation
56 * will happen later, in fcgi_accept_request (or whatever is the
57 * SAPI being used).
58 */
59 globals->impersonation_token = INVALID_HANDLE_VALUE;
60 globals->impersonation_token_sid = NULL;
61 }/*}}}*/
62
tsrm_win32_dtor(tsrm_win32_globals * globals)63 static void tsrm_win32_dtor(tsrm_win32_globals *globals)
64 {/*{{{*/
65 shm_pair *ptr;
66
67 if (globals->process) {
68 free(globals->process);
69 }
70
71 if (globals->shm) {
72 for (ptr = globals->shm; ptr < (globals->shm + globals->shm_size); ptr++) {
73 UnmapViewOfFile(ptr->addr);
74 CloseHandle(ptr->segment);
75 UnmapViewOfFile(ptr->descriptor);
76 CloseHandle(ptr->info);
77 }
78 free(globals->shm);
79 }
80
81 free(globals->comspec);
82
83 if (globals->impersonation_token && globals->impersonation_token != INVALID_HANDLE_VALUE ) {
84 CloseHandle(globals->impersonation_token);
85 }
86 if (globals->impersonation_token_sid) {
87 free(globals->impersonation_token_sid);
88 }
89 }/*}}}*/
90
tsrm_win32_startup(void)91 TSRM_API void tsrm_win32_startup(void)
92 {/*{{{*/
93 #ifdef ZTS
94 ts_allocate_id(&win32_globals_id, sizeof(tsrm_win32_globals), (ts_allocate_ctor)tsrm_win32_ctor, (ts_allocate_dtor)tsrm_win32_dtor);
95 #else
96 tsrm_win32_ctor(&win32_globals);
97 #endif
98 }/*}}}*/
99
tsrm_win32_shutdown(void)100 TSRM_API void tsrm_win32_shutdown(void)
101 {/*{{{*/
102 #ifndef ZTS
103 tsrm_win32_dtor(&win32_globals);
104 #endif
105 }/*}}}*/
106
tsrm_win32_get_path_sid_key(const char * pathname,size_t pathname_len,size_t * key_len)107 const char * tsrm_win32_get_path_sid_key(const char *pathname, size_t pathname_len, size_t *key_len)
108 {/*{{{*/
109 PSID pSid = TWG(impersonation_token_sid);
110 char *ptcSid = NULL;
111 char *bucket_key = NULL;
112 size_t ptc_sid_len;
113
114 if (!pSid) {
115 *key_len = pathname_len;
116 return pathname;
117 }
118
119 if (!ConvertSidToStringSid(pSid, &ptcSid)) {
120 *key_len = 0;
121 return NULL;
122 }
123
124
125 ptc_sid_len = strlen(ptcSid);
126 *key_len = pathname_len + ptc_sid_len;
127 bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *key_len + 1);
128 if (!bucket_key) {
129 LocalFree(ptcSid);
130 *key_len = 0;
131 return NULL;
132 }
133
134 memcpy(bucket_key, ptcSid, ptc_sid_len);
135 memcpy(bucket_key + ptc_sid_len, pathname, pathname_len + 1);
136
137 LocalFree(ptcSid);
138 return bucket_key;
139 }/*}}}*/
140
141
tsrm_win32_get_token_sid(HANDLE hToken)142 PSID tsrm_win32_get_token_sid(HANDLE hToken)
143 {/*{{{*/
144 DWORD dwLength = 0;
145 PTOKEN_USER pTokenUser = NULL;
146 DWORD sid_len;
147 PSID pResultSid = NULL;
148
149 /* Get the actual size of the TokenUser structure */
150 if (!GetTokenInformation(
151 hToken, TokenUser, (LPVOID) pTokenUser, 0, &dwLength)) {
152 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
153 goto Finished;
154 }
155
156 pTokenUser = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
157 if (pTokenUser == NULL) {
158 goto Finished;
159 }
160 }
161
162 /* and fetch it now */
163 if (!GetTokenInformation(
164 hToken, TokenUser, (LPVOID) pTokenUser, dwLength, &dwLength)) {
165 goto Finished;
166 }
167
168 sid_len = GetLengthSid(pTokenUser->User.Sid);
169
170 /* ConvertSidToStringSid(pTokenUser->User.Sid, &ptcSidOwner); */
171 pResultSid = malloc(sid_len);
172 if (!pResultSid) {
173 goto Finished;
174 }
175 if (!CopySid(sid_len, pResultSid, pTokenUser->User.Sid)) {
176 goto Finished;
177 }
178 HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser);
179 return pResultSid;
180
181 Finished:
182 if (pResultSid) {
183 free(pResultSid);
184 }
185 /* Free the buffer for the token groups. */
186 if (pTokenUser != NULL) {
187 HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser);
188 }
189 return NULL;
190 }/*}}}*/
191
tsrm_win32_access(const char * pathname,int mode)192 TSRM_API int tsrm_win32_access(const char *pathname, int mode)
193 {/*{{{*/
194 time_t t;
195 HANDLE thread_token = NULL;
196 PSID token_sid;
197 SECURITY_INFORMATION sec_info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
198 GENERIC_MAPPING gen_map = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS };
199 DWORD priv_set_length = sizeof(PRIVILEGE_SET);
200
201 PRIVILEGE_SET privilege_set = {0};
202 DWORD sec_desc_length = 0, desired_access = 0, granted_access = 0;
203 BYTE * psec_desc = NULL;
204 BOOL fAccess = FALSE;
205
206 realpath_cache_bucket * bucket = NULL;
207 char real_path[MAXPATHLEN] = {0};
208
209 if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
210 if(tsrm_realpath(pathname, real_path) == NULL) {
211 SET_ERRNO_FROM_WIN32_CODE(ERROR_FILE_NOT_FOUND);
212 return -1;
213 }
214 pathname = real_path;
215 }
216
217 PHP_WIN32_IOUTIL_INIT_W(pathname)
218 if (!pathw) {
219 return -1;
220 }
221
222 /* Either access call failed, or the mode was asking for a specific check.*/
223 int ret = php_win32_ioutil_access_w(pathw, mode);
224 if (0 > ret || X_OK == mode || F_OK == mode) {
225 PHP_WIN32_IOUTIL_CLEANUP_W()
226 return ret;
227 }
228
229 /* Only in NTS when impersonate==1 (aka FastCGI) */
230
231 /*
232 AccessCheck() requires an impersonation token. We first get a primary
233 token and then create a duplicate impersonation token. The
234 impersonation token is not actually assigned to the thread, but is
235 used in the call to AccessCheck. Thus, this function itself never
236 impersonates, but does use the identity of the thread. If the thread
237 was impersonating already, this function uses that impersonation context.
238 */
239 if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
240 if (GetLastError() == ERROR_NO_TOKEN) {
241 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) {
242 TWG(impersonation_token) = NULL;
243 goto Finished;
244 }
245 }
246 }
247
248 /* token_sid will be freed in tsrmwin32_dtor */
249 token_sid = tsrm_win32_get_token_sid(thread_token);
250 if (!token_sid) {
251 if (TWG(impersonation_token_sid)) {
252 free(TWG(impersonation_token_sid));
253 }
254 TWG(impersonation_token_sid) = NULL;
255 goto Finished;
256 }
257
258 /* Different identity, we need a new impersonated token as well */
259 if (!TWG(impersonation_token_sid) || !EqualSid(token_sid, TWG(impersonation_token_sid))) {
260 if (TWG(impersonation_token_sid)) {
261 free(TWG(impersonation_token_sid));
262 }
263 TWG(impersonation_token_sid) = token_sid;
264
265 /* Duplicate the token as impersonated token */
266 if (!DuplicateToken(thread_token, SecurityImpersonation, &TWG(impersonation_token))) {
267 goto Finished;
268 }
269 } else {
270 /* we already have it, free it then */
271 free(token_sid);
272 }
273
274 if (CWDG(realpath_cache_size_limit)) {
275 t = time(0);
276 bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
277 if(bucket == NULL && !real_path[0]) {
278 /* We used the pathname directly. Call tsrm_realpath */
279 /* so that entry is created in realpath cache */
280 if(tsrm_realpath(pathname, real_path) != NULL) {
281 pathname = real_path;
282 bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
283 PHP_WIN32_IOUTIL_REINIT_W(pathname);
284 }
285 }
286 }
287
288 /* Do a full access check because access() will only check read-only attribute */
289 if(mode == 0 || mode > 6) {
290 if(bucket != NULL && bucket->is_rvalid) {
291 fAccess = bucket->is_readable;
292 goto Finished;
293 }
294 desired_access = FILE_GENERIC_READ;
295 } else if(mode <= 2) {
296 if(bucket != NULL && bucket->is_wvalid) {
297 fAccess = bucket->is_writable;
298 goto Finished;
299 }
300 desired_access = FILE_GENERIC_WRITE;
301 } else if(mode <= 4) {
302 if(bucket != NULL && bucket->is_rvalid) {
303 fAccess = bucket->is_readable;
304 goto Finished;
305 }
306 desired_access = FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS;
307 } else { // if(mode <= 6)
308 if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
309 fAccess = bucket->is_readable & bucket->is_writable;
310 goto Finished;
311 }
312 desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
313 }
314
315 if(TWG(impersonation_token) == NULL) {
316 goto Finished;
317 }
318
319 /* Get size of security buffer. Call is expected to fail */
320 if(GetFileSecurityW(pathw, sec_info, NULL, 0, &sec_desc_length)) {
321 goto Finished;
322 }
323
324 psec_desc = (BYTE *)malloc(sec_desc_length);
325 if(psec_desc == NULL ||
326 !GetFileSecurityW(pathw, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) {
327 goto Finished;
328 }
329
330 MapGenericMask(&desired_access, &gen_map);
331
332 if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
333 goto Finished_Impersonate;
334 }
335
336 /* Keep the result in realpath_cache */
337 if(bucket != NULL) {
338 if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) {
339 bucket->is_rvalid = 1;
340 bucket->is_readable = fAccess;
341 }
342 else if(desired_access == FILE_GENERIC_WRITE) {
343 bucket->is_wvalid = 1;
344 bucket->is_writable = fAccess;
345 } else if (desired_access == (FILE_GENERIC_READ | FILE_GENERIC_WRITE)) {
346 bucket->is_rvalid = 1;
347 bucket->is_readable = fAccess;
348 bucket->is_wvalid = 1;
349 bucket->is_writable = fAccess;
350 }
351 }
352
353 Finished_Impersonate:
354 if(psec_desc != NULL) {
355 free(psec_desc);
356 psec_desc = NULL;
357 }
358
359 Finished:
360 if(thread_token != NULL) {
361 CloseHandle(thread_token);
362 }
363
364 PHP_WIN32_IOUTIL_CLEANUP_W()
365 if(fAccess == FALSE) {
366 errno = EACCES;
367 return errno;
368 } else {
369 return 0;
370 }
371 }/*}}}*/
372
373
process_get(FILE * stream)374 static process_pair *process_get(FILE *stream)
375 {/*{{{*/
376 process_pair *ptr;
377 process_pair *newptr;
378
379 for (ptr = TWG(process); ptr < (TWG(process) + TWG(process_size)); ptr++) {
380 if (ptr->stream == stream) {
381 break;
382 }
383 }
384
385 if (ptr < (TWG(process) + TWG(process_size))) {
386 return ptr;
387 }
388
389 newptr = (process_pair*)realloc((void*)TWG(process), (TWG(process_size)+1)*sizeof(process_pair));
390 if (newptr == NULL) {
391 return NULL;
392 }
393
394 TWG(process) = newptr;
395 ptr = newptr + TWG(process_size);
396 TWG(process_size)++;
397 return ptr;
398 }/*}}}*/
399
shm_get(key_t key,void * addr)400 static shm_pair *shm_get(key_t key, void *addr)
401 {/*{{{*/
402 shm_pair *ptr;
403 shm_pair *newptr;
404
405 for (ptr = TWG(shm); ptr < (TWG(shm) + TWG(shm_size)); ptr++) {
406 if (!ptr->descriptor) {
407 continue;
408 }
409 if (!addr && ptr->descriptor->shm_perm.key == key) {
410 break;
411 } else if (ptr->addr == addr) {
412 break;
413 }
414 }
415
416 if (ptr < (TWG(shm) + TWG(shm_size))) {
417 return ptr;
418 }
419
420 newptr = (shm_pair*)realloc((void*)TWG(shm), (TWG(shm_size)+1)*sizeof(shm_pair));
421 if (newptr == NULL) {
422 return NULL;
423 }
424
425 TWG(shm) = newptr;
426 ptr = newptr + TWG(shm_size);
427 TWG(shm_size)++;
428 memset(ptr, 0, sizeof(*ptr));
429 return ptr;
430 }/*}}}*/
431
dupHandle(HANDLE fh,BOOL inherit)432 static HANDLE dupHandle(HANDLE fh, BOOL inherit)
433 {/*{{{*/
434 HANDLE copy, self = GetCurrentProcess();
435 if (!DuplicateHandle(self, fh, self, ©, 0, inherit, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) {
436 return NULL;
437 }
438 return copy;
439 }/*}}}*/
440
popen(const char * command,const char * type)441 TSRM_API FILE *popen(const char *command, const char *type)
442 {/*{{{*/
443
444 return popen_ex(command, type, NULL, NULL);
445 }/*}}}*/
446
popen_ex(const char * command,const char * type,const char * cwd,const char * env)447 TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, const char *env)
448 {/*{{{*/
449 FILE *stream = NULL;
450 int fno, type_len, read, mode;
451 STARTUPINFOW startup;
452 PROCESS_INFORMATION process;
453 SECURITY_ATTRIBUTES security;
454 HANDLE in, out;
455 DWORD dwCreateFlags = 0;
456 BOOL res;
457 process_pair *proc;
458 char *cmd = NULL;
459 wchar_t *cmdw = NULL, *cwdw = NULL, *envw = NULL;
460 char *ptype = (char *)type;
461 HANDLE thread_token = NULL;
462 HANDLE token_user = NULL;
463 BOOL asuser = TRUE;
464
465 if (!type) {
466 return NULL;
467 }
468
469 type_len = (int)strlen(type);
470 if (type_len < 1 || type_len > 2) {
471 return NULL;
472 }
473
474 if (ptype[0] != 'r' && ptype[0] != 'w') {
475 return NULL;
476 }
477
478 if (type_len > 1 && (ptype[1] != 'b' && ptype[1] != 't')) {
479 return NULL;
480 }
481
482 cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /s /c ")+2);
483 if (!cmd) {
484 return NULL;
485 }
486
487 sprintf(cmd, "%s /s /c \"%s\"", TWG(comspec), command);
488 cmdw = php_win32_cp_any_to_w(cmd);
489 if (!cmdw) {
490 free(cmd);
491 return NULL;
492 }
493
494 if (cwd) {
495 cwdw = php_win32_ioutil_any_to_w(cwd);
496 if (!cwdw) {
497 free(cmd);
498 free(cmdw);
499 return NULL;
500 }
501 }
502
503 security.nLength = sizeof(SECURITY_ATTRIBUTES);
504 security.bInheritHandle = TRUE;
505 security.lpSecurityDescriptor = NULL;
506
507 if (!type_len || !CreatePipe(&in, &out, &security, 2048L)) {
508 free(cmdw);
509 free(cwdw);
510 free(cmd);
511 return NULL;
512 }
513
514 memset(&startup, 0, sizeof(STARTUPINFOW));
515 memset(&process, 0, sizeof(PROCESS_INFORMATION));
516
517 startup.cb = sizeof(STARTUPINFOW);
518 startup.dwFlags = STARTF_USESTDHANDLES;
519 startup.hStdError = GetStdHandle(STD_ERROR_HANDLE);
520
521 read = (type[0] == 'r') ? TRUE : FALSE;
522 mode = ((type_len == 2) && (type[1] == 'b')) ? O_BINARY : O_TEXT;
523
524 if (read) {
525 in = dupHandle(in, FALSE);
526 startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
527 startup.hStdOutput = out;
528 } else {
529 out = dupHandle(out, FALSE);
530 startup.hStdInput = in;
531 startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
532 }
533
534 dwCreateFlags = NORMAL_PRIORITY_CLASS;
535 if (strcmp(sapi_module.name, "cli") != 0) {
536 dwCreateFlags |= CREATE_NO_WINDOW;
537 }
538
539 /* Get a token with the impersonated user. */
540 if(OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
541 DuplicateTokenEx(thread_token, MAXIMUM_ALLOWED, &security, SecurityImpersonation, TokenPrimary, &token_user);
542 } else {
543 DWORD err = GetLastError();
544 if (err == ERROR_NO_TOKEN) {
545 asuser = FALSE;
546 }
547 }
548
549 envw = php_win32_cp_env_any_to_w(env);
550 if (envw) {
551 dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
552 } else {
553 if (env) {
554 free(cmd);
555 free(cmdw);
556 free(cwdw);
557 return NULL;
558 }
559 }
560
561 if (asuser) {
562 res = CreateProcessAsUserW(token_user, NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
563 CloseHandle(token_user);
564 } else {
565 res = CreateProcessW(NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
566 }
567 free(cmd);
568 free(cmdw);
569 free(cwdw);
570 free(envw);
571
572 if (!res) {
573 return NULL;
574 }
575
576 CloseHandle(process.hThread);
577 proc = process_get(NULL);
578
579 if (read) {
580 fno = _open_osfhandle((tsrm_intptr_t)in, _O_RDONLY | mode);
581 CloseHandle(out);
582 } else {
583 fno = _open_osfhandle((tsrm_intptr_t)out, _O_WRONLY | mode);
584 CloseHandle(in);
585 }
586
587 stream = _fdopen(fno, type);
588 proc->prochnd = process.hProcess;
589 proc->stream = stream;
590 return stream;
591 }/*}}}*/
592
pclose(FILE * stream)593 TSRM_API int pclose(FILE *stream)
594 {/*{{{*/
595 DWORD termstat = 0;
596 process_pair *process;
597
598 if ((process = process_get(stream)) == NULL) {
599 return 0;
600 }
601
602 fflush(process->stream);
603 fclose(process->stream);
604
605 WaitForSingleObject(process->prochnd, INFINITE);
606 GetExitCodeProcess(process->prochnd, &termstat);
607 process->stream = NULL;
608 CloseHandle(process->prochnd);
609
610 return termstat;
611 }/*}}}*/
612
613 #define SEGMENT_PREFIX "TSRM_SHM_SEGMENT:"
614 #define DESCRIPTOR_PREFIX "TSRM_SHM_DESCRIPTOR:"
615 #define INT_MIN_AS_STRING "-2147483648"
616
617
618 #define TSRM_BASE_SHM_KEY_ADDRESS 0x20000000
619 /* Returns a number between 0x2000_0000 and 0x3fff_ffff. On Windows, key_t is int. */
tsrm_choose_random_shm_key(key_t prev_key)620 static key_t tsrm_choose_random_shm_key(key_t prev_key) {
621 unsigned char buf[4];
622 if (php_win32_get_random_bytes(buf, 4) != SUCCESS) {
623 return prev_key + 2;
624 }
625 uint32_t n =
626 ((uint32_t)(buf[0]) << 24) |
627 (((uint32_t)buf[1]) << 16) |
628 (((uint32_t)buf[2]) << 8) |
629 (((uint32_t)buf[3]));
630 return (n & 0x1fffffff) + TSRM_BASE_SHM_KEY_ADDRESS;
631 }
632
shmget(key_t key,size_t size,int flags)633 TSRM_API int shmget(key_t key, size_t size, int flags)
634 {/*{{{*/
635 shm_pair *shm;
636 char shm_segment[sizeof(SEGMENT_PREFIX INT_MIN_AS_STRING)], shm_info[sizeof(DESCRIPTOR_PREFIX INT_MIN_AS_STRING)];
637 HANDLE shm_handle = NULL, info_handle = NULL;
638 BOOL created = FALSE;
639
640 if (key != IPC_PRIVATE) {
641 snprintf(shm_segment, sizeof(shm_segment), SEGMENT_PREFIX "%d", key);
642 snprintf(shm_info, sizeof(shm_info), DESCRIPTOR_PREFIX "%d", key);
643
644 shm_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_segment);
645 info_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_info);
646 } else {
647 /* IPC_PRIVATE always creates a new segment even if IPC_CREAT flag isn't passed. */
648 flags |= IPC_CREAT;
649 }
650
651 if (!shm_handle && !info_handle) {
652 if (flags & IPC_CREAT) {
653 #if SIZEOF_SIZE_T == 8
654 DWORD high = size >> 32;
655 DWORD low = (DWORD)size;
656 #else
657 DWORD high = 0;
658 DWORD low = size;
659 #endif
660 shm_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, high, low, key == IPC_PRIVATE ? NULL : shm_segment);
661 info_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(shm->descriptor), key == IPC_PRIVATE ? NULL : shm_info);
662 created = TRUE;
663 }
664 if (!shm_handle || !info_handle) {
665 if (shm_handle) {
666 CloseHandle(shm_handle);
667 }
668 if (info_handle) {
669 CloseHandle(info_handle);
670 }
671 return -1;
672 }
673 } else {
674 if (flags & IPC_EXCL) {
675 if (shm_handle) {
676 CloseHandle(shm_handle);
677 }
678 if (info_handle) {
679 CloseHandle(info_handle);
680 }
681 return -1;
682 }
683 }
684
685 if (key == IPC_PRIVATE) {
686 /* This should call shm_get with a brand new key id that isn't used yet. See https://man7.org/linux/man-pages/man2/shmget.2.html
687 * Because extensions such as shmop/sysvshm can be used in userland to attach to shared memory segments, use unpredictable high positive numbers to avoid accidentally conflicting with userland. */
688 key = tsrm_choose_random_shm_key(TSRM_BASE_SHM_KEY_ADDRESS);
689 for (shm_pair *ptr = TWG(shm); ptr < (TWG(shm) + TWG(shm_size)); ptr++) {
690 if (ptr->descriptor && ptr->descriptor->shm_perm.key == key) {
691 key = tsrm_choose_random_shm_key(key);
692 ptr = TWG(shm);
693 continue;
694 }
695 }
696 }
697
698 shm = shm_get(key, NULL);
699 if (!shm) {
700 CloseHandle(shm_handle);
701 CloseHandle(info_handle);
702 return -1;
703 }
704 shm->segment = shm_handle;
705 shm->info = info_handle;
706 shm->descriptor = MapViewOfFileEx(shm->info, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
707
708 if (NULL != shm->descriptor && created) {
709 shm->descriptor->shm_perm.key = key;
710 shm->descriptor->shm_segsz = size;
711 shm->descriptor->shm_ctime = time(NULL);
712 shm->descriptor->shm_cpid = getpid();
713 shm->descriptor->shm_perm.mode = flags;
714
715 shm->descriptor->shm_perm.cuid = shm->descriptor->shm_perm.cgid= 0;
716 shm->descriptor->shm_perm.gid = shm->descriptor->shm_perm.uid = 0;
717 shm->descriptor->shm_atime = shm->descriptor->shm_dtime = 0;
718 shm->descriptor->shm_lpid = shm->descriptor->shm_nattch = 0;
719 shm->descriptor->shm_perm.mode = shm->descriptor->shm_perm.seq = 0;
720 }
721
722 if (NULL != shm->descriptor && (shm->descriptor->shm_perm.key != key || size > shm->descriptor->shm_segsz)) {
723 if (NULL != shm->segment) {
724 CloseHandle(shm->segment);
725 }
726 UnmapViewOfFile(shm->descriptor);
727 CloseHandle(shm->info);
728 return -1;
729 }
730
731 return key;
732 }/*}}}*/
733
shmat(int key,const void * shmaddr,int flags)734 TSRM_API void *shmat(int key, const void *shmaddr, int flags)
735 {/*{{{*/
736 shm_pair *shm = shm_get(key, NULL);
737
738 if (!shm || !shm->segment) {
739 return (void*)-1;
740 }
741
742 shm->addr = MapViewOfFileEx(shm->segment, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
743
744 if (NULL == shm->addr) {
745 int err = GetLastError();
746 SET_ERRNO_FROM_WIN32_CODE(err);
747 return (void*)-1;
748 }
749
750 shm->descriptor->shm_atime = time(NULL);
751 shm->descriptor->shm_lpid = getpid();
752 shm->descriptor->shm_nattch++;
753
754 return shm->addr;
755 }/*}}}*/
756
shmdt(const void * shmaddr)757 TSRM_API int shmdt(const void *shmaddr)
758 {/*{{{*/
759 shm_pair *shm = shm_get(0, (void*)shmaddr);
760 int ret;
761
762 if (!shm || !shm->segment) {
763 return -1;
764 }
765
766 shm->descriptor->shm_dtime = time(NULL);
767 shm->descriptor->shm_lpid = getpid();
768 shm->descriptor->shm_nattch--;
769
770 ret = UnmapViewOfFile(shm->addr) ? 0 : -1;
771 if (!ret && shm->descriptor->shm_nattch <= 0) {
772 ret = UnmapViewOfFile(shm->descriptor) ? 0 : -1;
773 shm->descriptor = NULL;
774 }
775 return ret;
776 }/*}}}*/
777
shmctl(int key,int cmd,struct shmid_ds * buf)778 TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf)
779 {/*{{{*/
780 shm_pair *shm = shm_get(key, NULL);
781
782 if (!shm || !shm->segment) {
783 return -1;
784 }
785
786 switch (cmd) {
787 case IPC_STAT:
788 memcpy(buf, shm->descriptor, sizeof(struct shmid_ds));
789 return 0;
790
791 case IPC_SET:
792 shm->descriptor->shm_ctime = time(NULL);
793 shm->descriptor->shm_perm.uid = buf->shm_perm.uid;
794 shm->descriptor->shm_perm.gid = buf->shm_perm.gid;
795 shm->descriptor->shm_perm.mode = buf->shm_perm.mode;
796 return 0;
797
798 case IPC_RMID:
799 if (shm->descriptor->shm_nattch < 1) {
800 shm->descriptor->shm_perm.key = -1;
801 }
802 return 0;
803
804 default:
805 return -1;
806 }
807 }/*}}}*/
808
UnixTimeToFileTime(time_t t,LPFILETIME pft)809 static zend_always_inline void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */
810 {
811 // Note that LONGLONG is a 64-bit value
812 LONGLONG ll;
813
814 ll = t * 10000000LL + 116444736000000000LL;
815 pft->dwLowDateTime = (DWORD)ll;
816 pft->dwHighDateTime = ll >> 32;
817 }
818 /* }}} */
819
win32_utime(const char * filename,struct utimbuf * buf)820 TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */
821 {
822 FILETIME mtime, atime;
823 HANDLE hFile;
824 PHP_WIN32_IOUTIL_INIT_W(filename)
825
826 if (!pathw) {
827 return -1;
828 }
829
830 hFile = CreateFileW(pathw, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL,
831 OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
832
833 PHP_WIN32_IOUTIL_CLEANUP_W()
834
835 /* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but
836 the CreateFile operation succeeds */
837 if (GetLastError() == ERROR_ALREADY_EXISTS) {
838 SetLastError(0);
839 }
840
841 if ( hFile == INVALID_HANDLE_VALUE ) {
842 return -1;
843 }
844
845 if (!buf) {
846 SYSTEMTIME st;
847 GetSystemTime(&st);
848 SystemTimeToFileTime(&st, &mtime);
849 atime = mtime;
850 } else {
851 UnixTimeToFileTime(buf->modtime, &mtime);
852 UnixTimeToFileTime(buf->actime, &atime);
853 }
854 if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
855 CloseHandle(hFile);
856 return -1;
857 }
858 CloseHandle(hFile);
859 return 1;
860 }
861 /* }}} */
862 #endif
863