Busybox: Upgrade to 1.21.1 (stable). lsof active.
[tomato.git] / release / src / router / php / TSRM / tsrm_win32.c
blob0ced6db451bfe1227f131700ce46d0bbb9e0510d
1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Daniel Beulshausen <daniel@php4win.de> |
16 +----------------------------------------------------------------------+
19 /* $Id$ */
21 #include <stdio.h>
22 #include <fcntl.h>
23 #include <io.h>
24 #include <process.h>
25 #include <time.h>
26 #include <errno.h>
28 #define TSRM_INCLUDE_FULL_WINDOWS_HEADERS
29 #include "SAPI.h"
30 #include "TSRM.h"
32 #ifdef TSRM_WIN32
33 #include <Sddl.h>
34 #include "tsrm_win32.h"
35 #include "tsrm_virtual_cwd.h"
37 #ifdef ZTS
38 static ts_rsrc_id win32_globals_id;
39 #else
40 static tsrm_win32_globals win32_globals;
41 #endif
43 static void tsrm_win32_ctor(tsrm_win32_globals *globals TSRMLS_DC)
45 globals->process = NULL;
46 globals->shm = NULL;
47 globals->process_size = 0;
48 globals->shm_size = 0;
49 globals->comspec = _strdup((GetVersion()<0x80000000)?"cmd.exe":"command.com");
51 /* Set it to INVALID_HANDLE_VALUE
52 * It will be initialized correctly in tsrm_win32_access or set to
53 * NULL if no impersonation has been done.
54 * the impersonated token can't be set here as the impersonation
55 * will happen later, in fcgi_accept_request (or whatever is the
56 * SAPI being used).
58 globals->impersonation_token = INVALID_HANDLE_VALUE;
59 globals->impersonation_token_sid = NULL;
62 static void tsrm_win32_dtor(tsrm_win32_globals *globals TSRMLS_DC)
64 shm_pair *ptr;
66 if (globals->process) {
67 free(globals->process);
70 if (globals->shm) {
71 for (ptr = globals->shm; ptr < (globals->shm + globals->shm_size); ptr++) {
72 UnmapViewOfFile(ptr->addr);
73 CloseHandle(ptr->segment);
74 UnmapViewOfFile(ptr->descriptor);
75 CloseHandle(ptr->info);
77 free(globals->shm);
80 free(globals->comspec);
82 if (globals->impersonation_token && globals->impersonation_token != INVALID_HANDLE_VALUE ) {
83 CloseHandle(globals->impersonation_token);
85 if (globals->impersonation_token_sid) {
86 free(globals->impersonation_token_sid);
90 TSRM_API void tsrm_win32_startup(void)
92 #ifdef ZTS
93 ts_allocate_id(&win32_globals_id, sizeof(tsrm_win32_globals), (ts_allocate_ctor)tsrm_win32_ctor, (ts_allocate_ctor)tsrm_win32_dtor);
94 #else
95 tsrm_win32_ctor(&win32_globals TSRMLS_CC);
96 #endif
99 TSRM_API void tsrm_win32_shutdown(void)
101 #ifndef ZTS
102 tsrm_win32_dtor(&win32_globals TSRMLS_CC);
103 #endif
106 char * tsrm_win32_get_path_sid_key(const char *pathname TSRMLS_DC)
108 PSID pSid = TWG(impersonation_token_sid);
109 DWORD sid_len = pSid ? GetLengthSid(pSid) : 0;
110 TCHAR *ptcSid = NULL;
111 char *bucket_key = NULL;
113 if (!pSid) {
114 bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(pathname) + 1);
115 if (!bucket_key) {
116 return NULL;
118 memcpy(bucket_key, pathname, strlen(pathname));
119 return bucket_key;
122 if (!ConvertSidToStringSid(pSid, &ptcSid)) {
123 return NULL;
126 bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(pathname) + strlen(ptcSid) + 1);
127 if (!bucket_key) {
128 LocalFree(ptcSid);
129 return NULL;
132 memcpy(bucket_key, ptcSid, strlen(ptcSid));
133 memcpy(bucket_key + strlen(ptcSid), pathname, strlen(pathname) + 1);
135 LocalFree(ptcSid);
136 return bucket_key;
140 PSID tsrm_win32_get_token_sid(HANDLE hToken)
142 BOOL bSuccess = FALSE;
143 DWORD dwLength = 0;
144 PTOKEN_USER pTokenUser = NULL;
145 PSID sid;
146 PSID *ppsid = &sid;
147 DWORD sid_len;
148 PSID pResultSid = NULL;
150 /* Get the actual size of the TokenUser structure */
151 if (!GetTokenInformation(
152 hToken, TokenUser, (LPVOID) pTokenUser, 0, &dwLength)) {
153 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
154 goto Finished;
157 pTokenUser = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
158 if (pTokenUser == NULL) {
159 goto Finished;
163 /* and fetch it now */
164 if (!GetTokenInformation(
165 hToken, TokenUser, (LPVOID) pTokenUser, dwLength, &dwLength)) {
166 goto Finished;
169 sid_len = GetLengthSid(pTokenUser->User.Sid);
171 /* ConvertSidToStringSid(pTokenUser->User.Sid, &ptcSidOwner); */
172 pResultSid = malloc(sid_len);
173 if (!pResultSid) {
174 goto Finished;
176 if (!CopySid(sid_len, pResultSid, pTokenUser->User.Sid)) {
177 goto Finished;
179 HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser);
180 return pResultSid;
182 Finished:
183 if (pResultSid) {
184 free(pResultSid);
186 /* Free the buffer for the token groups. */
187 if (pTokenUser != NULL) {
188 HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser);
190 return NULL;
193 TSRM_API int tsrm_win32_access(const char *pathname, int mode TSRMLS_DC)
195 time_t t;
196 HANDLE thread_token = NULL;
197 PSID token_sid;
198 SECURITY_INFORMATION sec_info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
199 GENERIC_MAPPING gen_map = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS };
200 DWORD priv_set_length = sizeof(PRIVILEGE_SET);
202 PRIVILEGE_SET privilege_set = {0};
203 DWORD sec_desc_length = 0, desired_access = 0, granted_access = 0;
204 BYTE * psec_desc = NULL;
205 BOOL fAccess = FALSE;
207 BOOL bucket_key_alloc = FALSE;
208 realpath_cache_bucket * bucket = NULL;
209 char * real_path = NULL;
211 if (mode == 1 /*X_OK*/) {
212 DWORD type;
213 return GetBinaryType(pathname, &type) ? 0 : -1;
214 } else {
215 if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
216 real_path = (char *)malloc(MAX_PATH);
217 if(tsrm_realpath(pathname, real_path TSRMLS_CC) == NULL) {
218 goto Finished;
220 pathname = real_path;
223 if(access(pathname, mode)) {
224 free(real_path);
225 return errno;
228 /* If only existence check is made, return now */
229 if (mode == 0) {
230 free(real_path);
231 return 0;
234 /* Only in NTS when impersonate==1 (aka FastCGI) */
237 AccessCheck() requires an impersonation token. We first get a primary
238 token and then create a duplicate impersonation token. The
239 impersonation token is not actually assigned to the thread, but is
240 used in the call to AccessCheck. Thus, this function itself never
241 impersonates, but does use the identity of the thread. If the thread
242 was impersonating already, this function uses that impersonation context.
244 if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
245 DWORD err = GetLastError();
246 if (GetLastError() == ERROR_NO_TOKEN) {
247 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) {
248 TWG(impersonation_token) = NULL;
249 goto Finished;
254 /* token_sid will be freed in tsrmwin32_dtor */
255 token_sid = tsrm_win32_get_token_sid(thread_token);
256 if (!token_sid) {
257 if (TWG(impersonation_token_sid)) {
258 free(TWG(impersonation_token_sid));
260 TWG(impersonation_token_sid) = NULL;
261 goto Finished;
264 /* Different identity, we need a new impersontated token as well */
265 if (!TWG(impersonation_token_sid) || !EqualSid(token_sid, TWG(impersonation_token_sid))) {
266 if (TWG(impersonation_token_sid)) {
267 free(TWG(impersonation_token_sid));
269 TWG(impersonation_token_sid) = token_sid;
271 /* Duplicate the token as impersonated token */
272 if (!DuplicateToken(thread_token, SecurityImpersonation, &TWG(impersonation_token))) {
273 goto Finished;
275 } else {
276 /* we already have it, free it then */
277 free(token_sid);
280 if (CWDG(realpath_cache_size_limit)) {
281 t = time(0);
282 bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
283 if(bucket == NULL && real_path == NULL) {
284 /* We used the pathname directly. Call tsrm_realpath */
285 /* so that entry is created in realpath cache */
286 real_path = (char *)malloc(MAX_PATH);
287 if(tsrm_realpath(pathname, real_path TSRMLS_CC) != NULL) {
288 pathname = real_path;
289 bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
294 /* Do a full access check because access() will only check read-only attribute */
295 if(mode == 0 || mode > 6) {
296 if(bucket != NULL && bucket->is_rvalid) {
297 fAccess = bucket->is_readable;
298 goto Finished;
300 desired_access = FILE_GENERIC_READ;
301 } else if(mode <= 2) {
302 if(bucket != NULL && bucket->is_wvalid) {
303 fAccess = bucket->is_writable;
304 goto Finished;
306 desired_access = FILE_GENERIC_WRITE;
307 } else if(mode <= 4) {
308 if(bucket != NULL && bucket->is_rvalid) {
309 fAccess = bucket->is_readable;
310 goto Finished;
312 desired_access = FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS;
313 } else { // if(mode <= 6)
314 if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
315 fAccess = bucket->is_readable & bucket->is_writable;
316 goto Finished;
318 desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
321 if(TWG(impersonation_token) == NULL) {
322 goto Finished;
325 /* Get size of security buffer. Call is expected to fail */
326 if(GetFileSecurity(pathname, sec_info, NULL, 0, &sec_desc_length)) {
327 goto Finished;
330 psec_desc = (BYTE *)malloc(sec_desc_length);
331 if(psec_desc == NULL ||
332 !GetFileSecurity(pathname, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) {
333 goto Finished;
336 MapGenericMask(&desired_access, &gen_map);
338 if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
339 goto Finished_Impersonate;
342 /* Keep the result in realpath_cache */
343 if(bucket != NULL) {
344 if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) {
345 bucket->is_rvalid = 1;
346 bucket->is_readable = fAccess;
348 else if(desired_access == FILE_GENERIC_WRITE) {
349 bucket->is_wvalid = 1;
350 bucket->is_writable = fAccess;
351 } else if (desired_access == (FILE_GENERIC_READ | FILE_GENERIC_WRITE)) {
352 bucket->is_rvalid = 1;
353 bucket->is_readable = fAccess;
354 bucket->is_wvalid = 1;
355 bucket->is_writable = fAccess;
359 Finished_Impersonate:
360 if(psec_desc != NULL) {
361 free(psec_desc);
362 psec_desc = NULL;
365 Finished:
366 if(thread_token != NULL) {
367 CloseHandle(thread_token);
369 if(real_path != NULL) {
370 free(real_path);
371 real_path = NULL;
374 if(fAccess == FALSE) {
375 errno = EACCES;
376 return errno;
377 } else {
378 return 0;
384 static process_pair *process_get(FILE *stream TSRMLS_DC)
386 process_pair *ptr;
387 process_pair *newptr;
389 for (ptr = TWG(process); ptr < (TWG(process) + TWG(process_size)); ptr++) {
390 if (ptr->stream == stream) {
391 break;
395 if (ptr < (TWG(process) + TWG(process_size))) {
396 return ptr;
399 newptr = (process_pair*)realloc((void*)TWG(process), (TWG(process_size)+1)*sizeof(process_pair));
400 if (newptr == NULL) {
401 return NULL;
404 TWG(process) = newptr;
405 ptr = newptr + TWG(process_size);
406 TWG(process_size)++;
407 return ptr;
410 static shm_pair *shm_get(int key, void *addr)
412 shm_pair *ptr;
413 shm_pair *newptr;
414 TSRMLS_FETCH();
416 for (ptr = TWG(shm); ptr < (TWG(shm) + TWG(shm_size)); ptr++) {
417 if (!ptr->descriptor) {
418 continue;
420 if (!addr && ptr->descriptor->shm_perm.key == key) {
421 break;
422 } else if (ptr->addr == addr) {
423 break;
427 if (ptr < (TWG(shm) + TWG(shm_size))) {
428 return ptr;
431 newptr = (shm_pair*)realloc((void*)TWG(shm), (TWG(shm_size)+1)*sizeof(shm_pair));
432 if (newptr == NULL) {
433 return NULL;
436 TWG(shm) = newptr;
437 ptr = newptr + TWG(shm_size);
438 TWG(shm_size)++;
439 return ptr;
442 static HANDLE dupHandle(HANDLE fh, BOOL inherit) {
443 HANDLE copy, self = GetCurrentProcess();
444 if (!DuplicateHandle(self, fh, self, &copy, 0, inherit, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) {
445 return NULL;
447 return copy;
450 TSRM_API FILE *popen(const char *command, const char *type)
452 TSRMLS_FETCH();
454 return popen_ex(command, type, NULL, NULL TSRMLS_CC);
457 TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, char *env TSRMLS_DC)
459 FILE *stream = NULL;
460 int fno, type_len = strlen(type), read, mode;
461 STARTUPINFO startup;
462 PROCESS_INFORMATION process;
463 SECURITY_ATTRIBUTES security;
464 HANDLE in, out;
465 DWORD dwCreateFlags = 0;
466 BOOL res;
467 process_pair *proc;
468 char *cmd;
469 int i;
470 char *ptype = (char *)type;
471 HANDLE thread_token = NULL;
472 HANDLE token_user = NULL;
473 BOOL asuser = TRUE;
475 if (!type) {
476 return NULL;
479 /*The following two checks can be removed once we drop XP support */
480 type_len = strlen(type);
481 if (type_len <1 || type_len > 2) {
482 return NULL;
485 for (i=0; i < type_len; i++) {
486 if (!(*ptype == 'r' || *ptype == 'w' || *ptype == 'b' || *ptype == 't')) {
487 return NULL;
489 ptype++;
492 security.nLength = sizeof(SECURITY_ATTRIBUTES);
493 security.bInheritHandle = TRUE;
494 security.lpSecurityDescriptor = NULL;
496 if (!type_len || !CreatePipe(&in, &out, &security, 2048L)) {
497 return NULL;
500 memset(&startup, 0, sizeof(STARTUPINFO));
501 memset(&process, 0, sizeof(PROCESS_INFORMATION));
503 startup.cb = sizeof(STARTUPINFO);
504 startup.dwFlags = STARTF_USESTDHANDLES;
505 startup.hStdError = GetStdHandle(STD_ERROR_HANDLE);
507 read = (type[0] == 'r') ? TRUE : FALSE;
508 mode = ((type_len == 2) && (type[1] == 'b')) ? O_BINARY : O_TEXT;
510 if (read) {
511 in = dupHandle(in, FALSE);
512 startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
513 startup.hStdOutput = out;
514 } else {
515 out = dupHandle(out, FALSE);
516 startup.hStdInput = in;
517 startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
520 dwCreateFlags = NORMAL_PRIORITY_CLASS;
521 if (strcmp(sapi_module.name, "cli") != 0) {
522 dwCreateFlags |= CREATE_NO_WINDOW;
525 /* Get a token with the impersonated user. */
526 if(OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
527 DuplicateTokenEx(thread_token, MAXIMUM_ALLOWED, &security, SecurityImpersonation, TokenPrimary, &token_user);
528 } else {
529 DWORD err = GetLastError();
530 if (err == ERROR_NO_TOKEN) {
531 asuser = FALSE;
535 cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c ")+2);
536 if (!cmd) {
537 return NULL;
540 sprintf(cmd, "%s /c \"%s\"", TWG(comspec), command);
541 if (asuser) {
542 res = CreateProcessAsUser(token_user, NULL, cmd, &security, &security, security.bInheritHandle, dwCreateFlags, env, cwd, &startup, &process);
543 CloseHandle(token_user);
544 } else {
545 res = CreateProcess(NULL, cmd, &security, &security, security.bInheritHandle, dwCreateFlags, env, cwd, &startup, &process);
547 free(cmd);
549 if (!res) {
550 return NULL;
553 CloseHandle(process.hThread);
554 proc = process_get(NULL TSRMLS_CC);
556 if (read) {
557 fno = _open_osfhandle((tsrm_intptr_t)in, _O_RDONLY | mode);
558 CloseHandle(out);
559 } else {
560 fno = _open_osfhandle((tsrm_intptr_t)out, _O_WRONLY | mode);
561 CloseHandle(in);
564 stream = _fdopen(fno, type);
565 proc->prochnd = process.hProcess;
566 proc->stream = stream;
567 return stream;
570 TSRM_API int pclose(FILE *stream)
572 DWORD termstat = 0;
573 process_pair *process;
574 TSRMLS_FETCH();
576 if ((process = process_get(stream TSRMLS_CC)) == NULL) {
577 return 0;
580 fflush(process->stream);
581 fclose(process->stream);
583 WaitForSingleObject(process->prochnd, INFINITE);
584 GetExitCodeProcess(process->prochnd, &termstat);
585 process->stream = NULL;
586 CloseHandle(process->prochnd);
588 return termstat;
591 TSRM_API int shmget(int key, int size, int flags)
593 shm_pair *shm;
594 char shm_segment[26], shm_info[29];
595 HANDLE shm_handle, info_handle;
596 BOOL created = FALSE;
598 if (size < 0) {
599 return -1;
602 sprintf(shm_segment, "TSRM_SHM_SEGMENT:%d", key);
603 sprintf(shm_info, "TSRM_SHM_DESCRIPTOR:%d", key);
605 shm_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_segment);
606 info_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_info);
608 if ((!shm_handle && !info_handle)) {
609 if (flags & IPC_CREAT) {
610 shm_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, shm_segment);
611 info_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(shm->descriptor), shm_info);
612 created = TRUE;
614 if ((!shm_handle || !info_handle)) {
615 return -1;
617 } else {
618 if (flags & IPC_EXCL) {
619 return -1;
623 shm = shm_get(key, NULL);
624 shm->segment = shm_handle;
625 shm->info = info_handle;
626 shm->descriptor = MapViewOfFileEx(shm->info, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
628 if (created) {
629 shm->descriptor->shm_perm.key = key;
630 shm->descriptor->shm_segsz = size;
631 shm->descriptor->shm_ctime = time(NULL);
632 shm->descriptor->shm_cpid = getpid();
633 shm->descriptor->shm_perm.mode = flags;
635 shm->descriptor->shm_perm.cuid = shm->descriptor->shm_perm.cgid= 0;
636 shm->descriptor->shm_perm.gid = shm->descriptor->shm_perm.uid = 0;
637 shm->descriptor->shm_atime = shm->descriptor->shm_dtime = 0;
638 shm->descriptor->shm_lpid = shm->descriptor->shm_nattch = 0;
639 shm->descriptor->shm_perm.mode = shm->descriptor->shm_perm.seq = 0;
642 if (shm->descriptor->shm_perm.key != key || size > shm->descriptor->shm_segsz ) {
643 CloseHandle(shm->segment);
644 UnmapViewOfFile(shm->descriptor);
645 CloseHandle(shm->info);
646 return -1;
649 return key;
652 TSRM_API void *shmat(int key, const void *shmaddr, int flags)
654 shm_pair *shm = shm_get(key, NULL);
656 if (!shm->segment) {
657 return (void*)-1;
660 shm->descriptor->shm_atime = time(NULL);
661 shm->descriptor->shm_lpid = getpid();
662 shm->descriptor->shm_nattch++;
664 shm->addr = MapViewOfFileEx(shm->segment, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
666 return shm->addr;
669 TSRM_API int shmdt(const void *shmaddr)
671 shm_pair *shm = shm_get(0, (void*)shmaddr);
673 if (!shm->segment) {
674 return -1;
677 shm->descriptor->shm_dtime = time(NULL);
678 shm->descriptor->shm_lpid = getpid();
679 shm->descriptor->shm_nattch--;
681 return UnmapViewOfFile(shm->addr) ? 0 : -1;
684 TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf) {
685 shm_pair *shm = shm_get(key, NULL);
687 if (!shm->segment) {
688 return -1;
691 switch (cmd) {
692 case IPC_STAT:
693 memcpy(buf, shm->descriptor, sizeof(struct shmid_ds));
694 return 0;
696 case IPC_SET:
697 shm->descriptor->shm_ctime = time(NULL);
698 shm->descriptor->shm_perm.uid = buf->shm_perm.uid;
699 shm->descriptor->shm_perm.gid = buf->shm_perm.gid;
700 shm->descriptor->shm_perm.mode = buf->shm_perm.mode;
701 return 0;
703 case IPC_RMID:
704 if (shm->descriptor->shm_nattch < 1) {
705 shm->descriptor->shm_perm.key = -1;
707 return 0;
709 default:
710 return -1;
714 TSRM_API char *realpath(char *orig_path, char *buffer)
716 int ret = GetFullPathName(orig_path, _MAX_PATH, buffer, NULL);
717 if(!ret || ret > _MAX_PATH) {
718 return NULL;
720 return buffer;
723 #endif