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