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 size_t cmd_buffer_size = strlen(command) + strlen(TWG(comspec)) + sizeof(" /s /c ") + 2;
481 cmd = malloc(cmd_buffer_size);
482 if (!cmd) {
483 return NULL;
484 }
485
486 snprintf(cmd, cmd_buffer_size, "%s /s /c \"%s\"", TWG(comspec), command);
487 cmdw = php_win32_cp_any_to_w(cmd);
488 if (!cmdw) {
489 free(cmd);
490 return NULL;
491 }
492
493 if (cwd) {
494 cwdw = php_win32_ioutil_any_to_w(cwd);
495 if (!cwdw) {
496 free(cmd);
497 free(cmdw);
498 return NULL;
499 }
500 }
501
502 security.nLength = sizeof(SECURITY_ATTRIBUTES);
503 security.bInheritHandle = TRUE;
504 security.lpSecurityDescriptor = NULL;
505
506 if (!type_len || !CreatePipe(&in, &out, &security, 2048L)) {
507 free(cmdw);
508 free(cwdw);
509 free(cmd);
510 return NULL;
511 }
512
513 memset(&startup, 0, sizeof(STARTUPINFOW));
514 memset(&process, 0, sizeof(PROCESS_INFORMATION));
515
516 startup.cb = sizeof(STARTUPINFOW);
517 startup.dwFlags = STARTF_USESTDHANDLES;
518 startup.hStdError = GetStdHandle(STD_ERROR_HANDLE);
519
520 read = (type[0] == 'r') ? TRUE : FALSE;
521 mode = ((type_len == 2) && (type[1] == 'b')) ? O_BINARY : O_TEXT;
522
523 if (read) {
524 in = dupHandle(in, FALSE);
525 startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
526 startup.hStdOutput = out;
527 } else {
528 out = dupHandle(out, FALSE);
529 startup.hStdInput = in;
530 startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
531 }
532
533 dwCreateFlags = NORMAL_PRIORITY_CLASS;
534 if (strcmp(sapi_module.name, "cli") != 0) {
535 dwCreateFlags |= CREATE_NO_WINDOW;
536 }
537
538 /* Get a token with the impersonated user. */
539 if(OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
540 DuplicateTokenEx(thread_token, MAXIMUM_ALLOWED, &security, SecurityImpersonation, TokenPrimary, &token_user);
541 } else {
542 DWORD err = GetLastError();
543 if (err == ERROR_NO_TOKEN) {
544 asuser = FALSE;
545 }
546 }
547
548 envw = php_win32_cp_env_any_to_w(env);
549 if (envw) {
550 dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
551 } else {
552 if (env) {
553 free(cmd);
554 free(cmdw);
555 free(cwdw);
556 return NULL;
557 }
558 }
559
560 if (asuser) {
561 res = CreateProcessAsUserW(token_user, NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
562 CloseHandle(token_user);
563 } else {
564 res = CreateProcessW(NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
565 }
566 free(cmd);
567 free(cmdw);
568 free(cwdw);
569 free(envw);
570
571 if (!res) {
572 return NULL;
573 }
574
575 CloseHandle(process.hThread);
576 proc = process_get(NULL);
577
578 if (read) {
579 fno = _open_osfhandle((tsrm_intptr_t)in, _O_RDONLY | mode);
580 CloseHandle(out);
581 } else {
582 fno = _open_osfhandle((tsrm_intptr_t)out, _O_WRONLY | mode);
583 CloseHandle(in);
584 }
585
586 stream = _fdopen(fno, type);
587 proc->prochnd = process.hProcess;
588 proc->stream = stream;
589 return stream;
590 }/*}}}*/
591
pclose(FILE * stream)592 TSRM_API int pclose(FILE *stream)
593 {/*{{{*/
594 DWORD termstat = 0;
595 process_pair *process;
596
597 if ((process = process_get(stream)) == NULL) {
598 return 0;
599 }
600
601 fflush(process->stream);
602 fclose(process->stream);
603
604 WaitForSingleObject(process->prochnd, INFINITE);
605 GetExitCodeProcess(process->prochnd, &termstat);
606 process->stream = NULL;
607 CloseHandle(process->prochnd);
608
609 return termstat;
610 }/*}}}*/
611
612 #define SEGMENT_PREFIX "TSRM_SHM_SEGMENT:"
613 #define INT_MIN_AS_STRING "-2147483648"
614
615
616 #define TSRM_BASE_SHM_KEY_ADDRESS 0x20000000
617 /* Returns a number between 0x2000_0000 and 0x3fff_ffff. On Windows, key_t is int. */
tsrm_choose_random_shm_key(key_t prev_key)618 static key_t tsrm_choose_random_shm_key(key_t prev_key) {
619 unsigned char buf[4];
620 if (php_win32_get_random_bytes(buf, 4) != SUCCESS) {
621 return prev_key + 2;
622 }
623 uint32_t n =
624 ((uint32_t)(buf[0]) << 24) |
625 (((uint32_t)buf[1]) << 16) |
626 (((uint32_t)buf[2]) << 8) |
627 (((uint32_t)buf[3]));
628 return (n & 0x1fffffff) + TSRM_BASE_SHM_KEY_ADDRESS;
629 }
630
shmget(key_t key,size_t size,int flags)631 TSRM_API int shmget(key_t key, size_t size, int flags)
632 {/*{{{*/
633 shm_pair *shm;
634 char shm_segment[sizeof(SEGMENT_PREFIX INT_MIN_AS_STRING)];
635 HANDLE shm_handle = NULL, info_handle = NULL;
636 BOOL created = FALSE;
637
638 if (key != IPC_PRIVATE) {
639 snprintf(shm_segment, sizeof(shm_segment), SEGMENT_PREFIX "%d", key);
640
641 shm_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_segment);
642 } else {
643 /* IPC_PRIVATE always creates a new segment even if IPC_CREAT flag isn't passed. */
644 flags |= IPC_CREAT;
645 }
646
647 if (!shm_handle) {
648 if (flags & IPC_CREAT) {
649 if (size == 0 || size > SIZE_MAX - sizeof(shm->descriptor)) {
650 return -1;
651 }
652 size += sizeof(shm->descriptor);
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 created = TRUE;
662 }
663 if (!shm_handle) {
664 return -1;
665 }
666 } else {
667 if (flags & IPC_EXCL) {
668 CloseHandle(shm_handle);
669 return -1;
670 }
671 }
672
673 if (key == IPC_PRIVATE) {
674 /* 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
675 * 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. */
676 key = tsrm_choose_random_shm_key(TSRM_BASE_SHM_KEY_ADDRESS);
677 for (shm_pair *ptr = TWG(shm); ptr < (TWG(shm) + TWG(shm_size)); ptr++) {
678 if (ptr->descriptor && ptr->descriptor->shm_perm.key == key) {
679 key = tsrm_choose_random_shm_key(key);
680 ptr = TWG(shm);
681 continue;
682 }
683 }
684 }
685
686 shm = shm_get(key, NULL);
687 if (!shm) {
688 CloseHandle(shm_handle);
689 return -1;
690 }
691 shm->segment = shm_handle;
692 shm->descriptor = MapViewOfFileEx(shm->segment, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
693
694 if (NULL != shm->descriptor && created) {
695 shm->descriptor->shm_perm.key = key;
696 shm->descriptor->shm_segsz = size;
697 shm->descriptor->shm_ctime = time(NULL);
698 shm->descriptor->shm_cpid = getpid();
699 shm->descriptor->shm_perm.mode = flags;
700
701 shm->descriptor->shm_perm.cuid = shm->descriptor->shm_perm.cgid= 0;
702 shm->descriptor->shm_perm.gid = shm->descriptor->shm_perm.uid = 0;
703 shm->descriptor->shm_atime = shm->descriptor->shm_dtime = 0;
704 shm->descriptor->shm_lpid = shm->descriptor->shm_nattch = 0;
705 shm->descriptor->shm_perm.mode = shm->descriptor->shm_perm.seq = 0;
706 }
707
708 if (NULL != shm->descriptor && (shm->descriptor->shm_perm.key != key || size > shm->descriptor->shm_segsz)) {
709 if (NULL != shm->segment) {
710 CloseHandle(shm->segment);
711 shm->segment = INVALID_HANDLE_VALUE;
712 }
713 UnmapViewOfFile(shm->descriptor);
714 shm->descriptor = NULL;
715 return -1;
716 }
717
718 return key;
719 }/*}}}*/
720
shmat(int key,const void * shmaddr,int flags)721 TSRM_API void *shmat(int key, const void *shmaddr, int flags)
722 {/*{{{*/
723 shm_pair *shm = shm_get(key, NULL);
724
725 if (!shm || !shm->segment) {
726 return (void*)-1;
727 }
728
729 shm->addr = shm->descriptor + sizeof(shm->descriptor);
730 shm->descriptor->shm_atime = time(NULL);
731 shm->descriptor->shm_lpid = getpid();
732 shm->descriptor->shm_nattch++;
733
734 return shm->addr;
735 }/*}}}*/
736
shmdt(const void * shmaddr)737 TSRM_API int shmdt(const void *shmaddr)
738 {/*{{{*/
739 shm_pair *shm = shm_get(0, (void*)shmaddr);
740 int ret;
741
742 if (!shm || !shm->segment) {
743 return -1;
744 }
745
746 shm->descriptor->shm_dtime = time(NULL);
747 shm->descriptor->shm_lpid = getpid();
748 shm->descriptor->shm_nattch--;
749
750 ret = 0;
751 if (shm->descriptor->shm_nattch <= 0) {
752 ret = UnmapViewOfFile(shm->descriptor) ? 0 : -1;
753 shm->descriptor = NULL;
754 }
755 return ret;
756 }/*}}}*/
757
shmctl(int key,int cmd,struct shmid_ds * buf)758 TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf)
759 {/*{{{*/
760 shm_pair *shm = shm_get(key, NULL);
761
762 if (!shm || !shm->segment) {
763 return -1;
764 }
765
766 switch (cmd) {
767 case IPC_STAT:
768 memcpy(buf, shm->descriptor, sizeof(struct shmid_ds));
769 return 0;
770
771 case IPC_SET:
772 shm->descriptor->shm_ctime = time(NULL);
773 shm->descriptor->shm_perm.uid = buf->shm_perm.uid;
774 shm->descriptor->shm_perm.gid = buf->shm_perm.gid;
775 shm->descriptor->shm_perm.mode = buf->shm_perm.mode;
776 return 0;
777
778 case IPC_RMID:
779 if (shm->descriptor->shm_nattch < 1) {
780 shm->descriptor->shm_perm.key = -1;
781 }
782 return 0;
783
784 default:
785 return -1;
786 }
787 }/*}}}*/
788
UnixTimeToFileTime(time_t t,LPFILETIME pft)789 static zend_always_inline void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */
790 {
791 // Note that LONGLONG is a 64-bit value
792 LONGLONG ll;
793
794 ll = t * 10000000LL + 116444736000000000LL;
795 pft->dwLowDateTime = (DWORD)ll;
796 pft->dwHighDateTime = ll >> 32;
797 }
798 /* }}} */
799
win32_utime(const char * filename,struct utimbuf * buf)800 TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */
801 {
802 FILETIME mtime, atime;
803 HANDLE hFile;
804 PHP_WIN32_IOUTIL_INIT_W(filename)
805
806 if (!pathw) {
807 return -1;
808 }
809
810 hFile = CreateFileW(pathw, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL,
811 OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
812
813 PHP_WIN32_IOUTIL_CLEANUP_W()
814
815 /* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but
816 the CreateFile operation succeeds */
817 if (GetLastError() == ERROR_ALREADY_EXISTS) {
818 SetLastError(0);
819 }
820
821 if ( hFile == INVALID_HANDLE_VALUE ) {
822 return -1;
823 }
824
825 if (!buf) {
826 SYSTEMTIME st;
827 GetSystemTime(&st);
828 SystemTimeToFileTime(&st, &mtime);
829 atime = mtime;
830 } else {
831 UnixTimeToFileTime(buf->modtime, &mtime);
832 UnixTimeToFileTime(buf->actime, &atime);
833 }
834 if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
835 CloseHandle(hFile);
836 return -1;
837 }
838 CloseHandle(hFile);
839 return 1;
840 }
841 /* }}} */
842 #endif
843