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 shm->segment = INVALID_HANDLE_VALUE;
711 }
712 UnmapViewOfFile(shm->descriptor);
713 shm->descriptor = NULL;
714 return -1;
715 }
716
717 return key;
718 }/*}}}*/
719
shmat(int key,const void * shmaddr,int flags)720 TSRM_API void *shmat(int key, const void *shmaddr, int flags)
721 {/*{{{*/
722 shm_pair *shm = shm_get(key, NULL);
723
724 if (!shm || !shm->segment) {
725 return (void*)-1;
726 }
727
728 shm->addr = shm->descriptor + sizeof(shm->descriptor);
729 shm->descriptor->shm_atime = time(NULL);
730 shm->descriptor->shm_lpid = getpid();
731 shm->descriptor->shm_nattch++;
732
733 return shm->addr;
734 }/*}}}*/
735
shmdt(const void * shmaddr)736 TSRM_API int shmdt(const void *shmaddr)
737 {/*{{{*/
738 shm_pair *shm = shm_get(0, (void*)shmaddr);
739 int ret;
740
741 if (!shm || !shm->segment) {
742 return -1;
743 }
744
745 shm->descriptor->shm_dtime = time(NULL);
746 shm->descriptor->shm_lpid = getpid();
747 shm->descriptor->shm_nattch--;
748
749 ret = 0;
750 if (shm->descriptor->shm_nattch <= 0) {
751 ret = UnmapViewOfFile(shm->descriptor) ? 0 : -1;
752 shm->descriptor = NULL;
753 }
754 return ret;
755 }/*}}}*/
756
shmctl(int key,int cmd,struct shmid_ds * buf)757 TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf)
758 {/*{{{*/
759 shm_pair *shm = shm_get(key, NULL);
760
761 if (!shm || !shm->segment) {
762 return -1;
763 }
764
765 switch (cmd) {
766 case IPC_STAT:
767 memcpy(buf, shm->descriptor, sizeof(struct shmid_ds));
768 return 0;
769
770 case IPC_SET:
771 shm->descriptor->shm_ctime = time(NULL);
772 shm->descriptor->shm_perm.uid = buf->shm_perm.uid;
773 shm->descriptor->shm_perm.gid = buf->shm_perm.gid;
774 shm->descriptor->shm_perm.mode = buf->shm_perm.mode;
775 return 0;
776
777 case IPC_RMID:
778 if (shm->descriptor->shm_nattch < 1) {
779 shm->descriptor->shm_perm.key = -1;
780 }
781 return 0;
782
783 default:
784 return -1;
785 }
786 }/*}}}*/
787
UnixTimeToFileTime(time_t t,LPFILETIME pft)788 static zend_always_inline void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */
789 {
790 // Note that LONGLONG is a 64-bit value
791 LONGLONG ll;
792
793 ll = t * 10000000LL + 116444736000000000LL;
794 pft->dwLowDateTime = (DWORD)ll;
795 pft->dwHighDateTime = ll >> 32;
796 }
797 /* }}} */
798
win32_utime(const char * filename,struct utimbuf * buf)799 TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */
800 {
801 FILETIME mtime, atime;
802 HANDLE hFile;
803 PHP_WIN32_IOUTIL_INIT_W(filename)
804
805 if (!pathw) {
806 return -1;
807 }
808
809 hFile = CreateFileW(pathw, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL,
810 OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
811
812 PHP_WIN32_IOUTIL_CLEANUP_W()
813
814 /* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but
815 the CreateFile operation succeeds */
816 if (GetLastError() == ERROR_ALREADY_EXISTS) {
817 SetLastError(0);
818 }
819
820 if ( hFile == INVALID_HANDLE_VALUE ) {
821 return -1;
822 }
823
824 if (!buf) {
825 SYSTEMTIME st;
826 GetSystemTime(&st);
827 SystemTimeToFileTime(&st, &mtime);
828 atime = mtime;
829 } else {
830 UnixTimeToFileTime(buf->modtime, &mtime);
831 UnixTimeToFileTime(buf->actime, &atime);
832 }
833 if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
834 CloseHandle(hFile);
835 return -1;
836 }
837 CloseHandle(hFile);
838 return 1;
839 }
840 /* }}} */
841 #endif
842