2 +----------------------------------------------------------------------+
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 +----------------------------------------------------------------------+
28 #define TSRM_INCLUDE_FULL_WINDOWS_HEADERS
34 #include "tsrm_win32.h"
35 #include "tsrm_virtual_cwd.h"
38 static ts_rsrc_id win32_globals_id
;
40 static tsrm_win32_globals win32_globals
;
43 static void tsrm_win32_ctor(tsrm_win32_globals
*globals TSRMLS_DC
)
45 globals
->process
= 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
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
)
66 if (globals
->process
) {
67 free(globals
->process
);
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
);
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)
93 ts_allocate_id(&win32_globals_id
, sizeof(tsrm_win32_globals
), (ts_allocate_ctor
)tsrm_win32_ctor
, (ts_allocate_ctor
)tsrm_win32_dtor
);
95 tsrm_win32_ctor(&win32_globals TSRMLS_CC
);
99 TSRM_API
void tsrm_win32_shutdown(void)
102 tsrm_win32_dtor(&win32_globals TSRMLS_CC
);
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
;
114 bucket_key
= (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, strlen(pathname
) + 1);
118 memcpy(bucket_key
, pathname
, strlen(pathname
));
122 if (!ConvertSidToStringSid(pSid
, &ptcSid
)) {
126 bucket_key
= (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, strlen(pathname
) + strlen(ptcSid
) + 1);
132 memcpy(bucket_key
, ptcSid
, strlen(ptcSid
));
133 memcpy(bucket_key
+ strlen(ptcSid
), pathname
, strlen(pathname
) + 1);
140 PSID
tsrm_win32_get_token_sid(HANDLE hToken
)
142 BOOL bSuccess
= FALSE
;
144 PTOKEN_USER pTokenUser
= NULL
;
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
) {
157 pTokenUser
= (PTOKEN_USER
)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwLength
);
158 if (pTokenUser
== NULL
) {
163 /* and fetch it now */
164 if (!GetTokenInformation(
165 hToken
, TokenUser
, (LPVOID
) pTokenUser
, dwLength
, &dwLength
)) {
169 sid_len
= GetLengthSid(pTokenUser
->User
.Sid
);
171 /* ConvertSidToStringSid(pTokenUser->User.Sid, &ptcSidOwner); */
172 pResultSid
= malloc(sid_len
);
176 if (!CopySid(sid_len
, pResultSid
, pTokenUser
->User
.Sid
)) {
179 HeapFree(GetProcessHeap(), 0, (LPVOID
)pTokenUser
);
186 /* Free the buffer for the token groups. */
187 if (pTokenUser
!= NULL
) {
188 HeapFree(GetProcessHeap(), 0, (LPVOID
)pTokenUser
);
193 TSRM_API
int tsrm_win32_access(const char *pathname
, int mode TSRMLS_DC
)
196 HANDLE thread_token
= NULL
;
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*/) {
213 return GetBinaryType(pathname
, &type
) ? 0 : -1;
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
) {
220 pathname
= real_path
;
223 if(access(pathname
, mode
)) {
228 /* If only existence check is made, return now */
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
;
254 /* token_sid will be freed in tsrmwin32_dtor */
255 token_sid
= tsrm_win32_get_token_sid(thread_token
);
257 if (TWG(impersonation_token_sid
)) {
258 free(TWG(impersonation_token_sid
));
260 TWG(impersonation_token_sid
) = NULL
;
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
))) {
276 /* we already have it, free it then */
280 if (CWDG(realpath_cache_size_limit
)) {
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
;
300 desired_access
= FILE_GENERIC_READ
;
301 } else if(mode
<= 2) {
302 if(bucket
!= NULL
&& bucket
->is_wvalid
) {
303 fAccess
= bucket
->is_writable
;
306 desired_access
= FILE_GENERIC_WRITE
;
307 } else if(mode
<= 4) {
308 if(bucket
!= NULL
&& bucket
->is_rvalid
) {
309 fAccess
= bucket
->is_readable
;
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
;
318 desired_access
= FILE_GENERIC_READ
| FILE_GENERIC_WRITE
;
321 if(TWG(impersonation_token
) == NULL
) {
325 /* Get size of security buffer. Call is expected to fail */
326 if(GetFileSecurity(pathname
, sec_info
, NULL
, 0, &sec_desc_length
)) {
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
)) {
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 */
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
) {
366 if(thread_token
!= NULL
) {
367 CloseHandle(thread_token
);
369 if(real_path
!= NULL
) {
374 if(fAccess
== FALSE
) {
384 static process_pair
*process_get(FILE *stream TSRMLS_DC
)
387 process_pair
*newptr
;
389 for (ptr
= TWG(process
); ptr
< (TWG(process
) + TWG(process_size
)); ptr
++) {
390 if (ptr
->stream
== stream
) {
395 if (ptr
< (TWG(process
) + TWG(process_size
))) {
399 newptr
= (process_pair
*)realloc((void*)TWG(process
), (TWG(process_size
)+1)*sizeof(process_pair
));
400 if (newptr
== NULL
) {
404 TWG(process
) = newptr
;
405 ptr
= newptr
+ TWG(process_size
);
410 static shm_pair
*shm_get(int key
, void *addr
)
416 for (ptr
= TWG(shm
); ptr
< (TWG(shm
) + TWG(shm_size
)); ptr
++) {
417 if (!ptr
->descriptor
) {
420 if (!addr
&& ptr
->descriptor
->shm_perm
.key
== key
) {
422 } else if (ptr
->addr
== addr
) {
427 if (ptr
< (TWG(shm
) + TWG(shm_size
))) {
431 newptr
= (shm_pair
*)realloc((void*)TWG(shm
), (TWG(shm_size
)+1)*sizeof(shm_pair
));
432 if (newptr
== NULL
) {
437 ptr
= newptr
+ TWG(shm_size
);
442 static HANDLE
dupHandle(HANDLE fh
, BOOL inherit
) {
443 HANDLE copy
, self
= GetCurrentProcess();
444 if (!DuplicateHandle(self
, fh
, self
, ©
, 0, inherit
, DUPLICATE_SAME_ACCESS
|DUPLICATE_CLOSE_SOURCE
)) {
450 TSRM_API
FILE *popen(const char *command
, const char *type
)
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
)
460 int fno
, type_len
= strlen(type
), read
, mode
;
462 PROCESS_INFORMATION process
;
463 SECURITY_ATTRIBUTES security
;
465 DWORD dwCreateFlags
= 0;
470 char *ptype
= (char *)type
;
471 HANDLE thread_token
= NULL
;
472 HANDLE token_user
= 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) {
485 for (i
=0; i
< type_len
; i
++) {
486 if (!(*ptype
== 'r' || *ptype
== 'w' || *ptype
== 'b' || *ptype
== 't')) {
492 security
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
493 security
.bInheritHandle
= TRUE
;
494 security
.lpSecurityDescriptor
= NULL
;
496 if (!type_len
|| !CreatePipe(&in
, &out
, &security
, 2048L)) {
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
;
511 in
= dupHandle(in
, FALSE
);
512 startup
.hStdInput
= GetStdHandle(STD_INPUT_HANDLE
);
513 startup
.hStdOutput
= out
;
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
);
529 DWORD err
= GetLastError();
530 if (err
== ERROR_NO_TOKEN
) {
535 cmd
= (char*)malloc(strlen(command
)+strlen(TWG(comspec
))+sizeof(" /c ")+2);
540 sprintf(cmd
, "%s /c \"%s\"", TWG(comspec
), command
);
542 res
= CreateProcessAsUser(token_user
, NULL
, cmd
, &security
, &security
, security
.bInheritHandle
, dwCreateFlags
, env
, cwd
, &startup
, &process
);
543 CloseHandle(token_user
);
545 res
= CreateProcess(NULL
, cmd
, &security
, &security
, security
.bInheritHandle
, dwCreateFlags
, env
, cwd
, &startup
, &process
);
553 CloseHandle(process
.hThread
);
554 proc
= process_get(NULL TSRMLS_CC
);
557 fno
= _open_osfhandle((tsrm_intptr_t
)in
, _O_RDONLY
| mode
);
560 fno
= _open_osfhandle((tsrm_intptr_t
)out
, _O_WRONLY
| mode
);
564 stream
= _fdopen(fno
, type
);
565 proc
->prochnd
= process
.hProcess
;
566 proc
->stream
= stream
;
570 TSRM_API
int pclose(FILE *stream
)
573 process_pair
*process
;
576 if ((process
= process_get(stream TSRMLS_CC
)) == NULL
) {
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
);
591 TSRM_API
int shmget(int key
, int size
, int flags
)
594 char shm_segment
[26], shm_info
[29];
595 HANDLE shm_handle
, info_handle
;
596 BOOL created
= FALSE
;
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
);
614 if ((!shm_handle
|| !info_handle
)) {
618 if (flags
& IPC_EXCL
) {
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
);
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
);
652 TSRM_API
void *shmat(int key
, const void *shmaddr
, int flags
)
654 shm_pair
*shm
= shm_get(key
, NULL
);
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
);
669 TSRM_API
int shmdt(const void *shmaddr
)
671 shm_pair
*shm
= shm_get(0, (void*)shmaddr
);
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
);
693 memcpy(buf
, shm
->descriptor
, sizeof(struct shmid_ds
));
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
;
704 if (shm
->descriptor
->shm_nattch
< 1) {
705 shm
->descriptor
->shm_perm
.key
= -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
) {