}
/* }}} */
+#ifdef PHP_WIN32
+static inline unsigned long realpath_cache_key(const char *path, int path_len TSRMLS_DC) /* {{{ */
+{
+ register unsigned long h;
+ char *bucket_key = tsrm_win32_get_path_sid_key(path TSRMLS_CC);
+ char *bucket_key_start = (char *)bucket_key;
+ const char *e = bucket_key + strlen(bucket_key);
+
+ if (!bucket_key) {
+ return 0;
+ }
+
+ for (h = 2166136261U; bucket_key < e;) {
+ h *= 16777619;
+ h ^= *bucket_key++;
+ }
+ /* if no SID were present the path is returned. Otherwise a Heap
+ allocated string is returned. */
+ if (bucket_key_start != path) {
+ LocalFree(bucket_key_start);
+ }
+ return h;
+}
+/* }}} */
+#else
static inline unsigned long realpath_cache_key(const char *path, int path_len) /* {{{ */
{
register unsigned long h;
return h;
}
/* }}} */
+#endif /* defined(PHP_WIN32) */
CWD_API void realpath_cache_clean(TSRMLS_D) /* {{{ */
{
CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC) /* {{{ */
{
+#ifdef PHP_WIN32
+ unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC);
+#else
unsigned long key = realpath_cache_key(path, path_len);
+#endif
unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) {
realpath_cache_bucket *bucket = malloc(size);
unsigned long n;
-
+
+#ifdef PHP_WIN32
+ bucket->key = realpath_cache_key(path, path_len TSRMLS_CC);
+#else
bucket->key = realpath_cache_key(path, path_len);
+#endif
bucket->path = (char*)bucket + sizeof(realpath_cache_bucket);
memcpy(bucket->path, path, path_len+1);
bucket->path_len = path_len;
static inline realpath_cache_bucket* realpath_cache_find(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */
{
+#ifdef PHP_WIN32
+ unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC);
+#else
unsigned long key = realpath_cache_key(path, path_len);
+#endif
+
unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
unsigned int retlength = 0, rname_off = 0;
int bufindex = 0, rname_len = 0, isabsolute = 0;
wchar_t * reparsetarget;
- WCHAR szVolumePathNames[MAX_PATH];
BOOL isVolume = FALSE;
if(++(*ll) > LINK_MAX) {
#include <errno.h>
#define TSRM_INCLUDE_FULL_WINDOWS_HEADERS
-
#include "SAPI.h"
#include "TSRM.h"
#ifdef TSRM_WIN32
-
+#include <Sddl.h>
#include "tsrm_win32.h"
#include "tsrm_virtual_cwd.h"
static void tsrm_win32_ctor(tsrm_win32_globals *globals TSRMLS_DC)
{
- HANDLE process_token = NULL;
-
globals->process = NULL;
globals->shm = NULL;
globals->process_size = 0;
globals->shm_size = 0;
globals->comspec = _strdup((GetVersion()<0x80000000)?"cmd.exe":"command.com");
- globals->impersonation_token = NULL;
-
- /* Access check requires impersonation token. Create a duplicate token. */
- if(OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, &process_token)) {
- DuplicateToken(process_token, SecurityImpersonation, &globals->impersonation_token);
- }
- /* impersonation_token will be closed when the process dies */
- if(process_token != NULL) {
- CloseHandle(process_token);
- process_token = NULL;
- }
+ /* Set it to INVALID_HANDLE_VALUE
+ * It will be initialized correctly in tsrm_win32_access or set to
+ * NULL if no impersonation has been done.
+ * the impersonated token can't be set here as the impersonation
+ * will happen later, in fcgi_accept_request (or whatever is the
+ * SAPI being used).
+ */
+ globals->impersonation_token = INVALID_HANDLE_VALUE;
+ globals->impersonation_token_sid = NULL;
}
static void tsrm_win32_dtor(tsrm_win32_globals *globals TSRMLS_DC)
free(globals->comspec);
- if(globals->impersonation_token) {
+ if (globals->impersonation_token && globals->impersonation_token != INVALID_HANDLE_VALUE ) {
CloseHandle(globals->impersonation_token);
}
+ if (globals->impersonation_token_sid) {
+ free(globals->impersonation_token_sid);
+ }
}
TSRM_API void tsrm_win32_startup(void)
#endif
}
+char * tsrm_win32_get_path_sid_key(const char *pathname TSRMLS_DC)
+{
+ PSID pSid = TWG(impersonation_token_sid);
+ DWORD sid_len = pSid ? GetLengthSid(pSid) : 0;
+ TCHAR *ptcSid = NULL;
+ char *bucket_key = NULL;
+
+ if (!pSid) {
+ bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(pathname));
+ if (!bucket_key) {
+ return NULL;
+ }
+ memcpy(bucket_key, pathname, strlen(pathname));
+ return bucket_key;
+ }
+
+ if (!ConvertSidToStringSid(pSid, &ptcSid)) {
+ return NULL;
+ }
+
+ bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(pathname) + strlen(ptcSid));
+ if (!bucket_key) {
+ LocalFree(ptcSid);
+ return NULL;
+ }
+
+ memcpy(bucket_key, ptcSid, strlen(ptcSid));
+ memcpy(bucket_key + strlen(ptcSid), pathname, strlen(pathname));
+
+ LocalFree(ptcSid);
+ return bucket_key;
+}
+
+
+PSID tsrm_win32_get_token_sid(HANDLE hToken)
+{
+ BOOL bSuccess = FALSE;
+ DWORD dwLength = 0;
+ PTOKEN_USER pTokenUser = NULL;
+ PSID sid;
+ PSID *ppsid = &sid;
+ DWORD sid_len;
+ PSID pResultSid = NULL;
+
+ /* Get the actual size of the TokenUser structure */
+ if (!GetTokenInformation(
+ hToken, TokenUser, (LPVOID) pTokenUser, 0, &dwLength)) {
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ goto Finished;
+ }
+
+ pTokenUser = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
+ if (pTokenUser == NULL) {
+ goto Finished;
+ }
+ }
+
+ /* and fetch it now */
+ if (!GetTokenInformation(
+ hToken, TokenUser, (LPVOID) pTokenUser, dwLength, &dwLength)) {
+ goto Finished;
+ }
+
+ sid_len = GetLengthSid(pTokenUser->User.Sid);
+
+ /* ConvertSidToStringSid(pTokenUser->User.Sid, &ptcSidOwner); */
+ pResultSid = malloc(sid_len);
+ if (!pResultSid) {
+ goto Finished;
+ }
+ if (!CopySid(sid_len, pResultSid, pTokenUser->User.Sid)) {
+ goto Finished;
+ }
+ return pResultSid;
+
+Finished:
+ if (pResultSid) {
+ free(pResultSid);
+ }
+ /* Free the buffer for the token groups. */
+ if (pTokenUser != NULL) {
+ HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser);
+ }
+ return NULL;
+}
+
TSRM_API int tsrm_win32_access(const char *pathname, int mode)
{
+ time_t t;
+ HANDLE thread_token;
+ PSID token_sid;
SECURITY_INFORMATION sec_info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
GENERIC_MAPPING gen_map = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS };
DWORD priv_set_length = sizeof(PRIVILEGE_SET);
DWORD sec_desc_length = 0, desired_access = 0, granted_access = 0;
BYTE * psec_desc = NULL;
BOOL fAccess = FALSE;
- HANDLE process_token = NULL;
+ BOOL bucket_key_alloc = FALSE;
realpath_cache_bucket * bucket = NULL;
char * real_path = NULL;
- time_t t;
TSRMLS_FETCH();
DWORD type;
return GetBinaryType(pathname, &type) ? 0 : -1;
} else {
- if(access(pathname, mode)) {
- return errno;
- }
-
if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
real_path = (char *)malloc(MAX_PATH);
if(tsrm_realpath(pathname, real_path TSRMLS_CC) == NULL) {
pathname = real_path;
}
+ if(access(pathname, mode)) {
+ return errno;
+ }
+
+ /* If only existence check is made, return now */
+ if (mode == 0) {
+ return 0;
+ }
+
+/* Only in NTS when impersonate==1 (aka FastCGI) */
+
+ /*
+ AccessCheck() requires an impersonation token. We first get a primary
+ token and then create a duplicate impersonation token. The
+ impersonation token is not actually assigned to the thread, but is
+ used in the call to AccessCheck. Thus, this function itself never
+ impersonates, but does use the identity of the thread. If the thread
+ was impersonating already, this function uses that impersonation context.
+ */
+ if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
+ DWORD err = GetLastError();
+ if (GetLastError() == ERROR_NO_TOKEN) {
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) {
+ TWG(impersonation_token) = NULL;
+ goto Finished;
+ }
+ }
+ }
+
+ /* token_sid will be freed in tsrmwin32_dtor */
+ token_sid = tsrm_win32_get_token_sid(thread_token);
+ if (!token_sid) {
+ if (TWG(impersonation_token_sid)) {
+ free(TWG(impersonation_token_sid));
+ }
+ TWG(impersonation_token_sid) = NULL;
+ goto Finished;
+ }
+
+ /* Different identity, we need a new impersontated token as well */
+ if (!TWG(impersonation_token_sid) || !EqualSid(token_sid, TWG(impersonation_token_sid))) {
+ if (TWG(impersonation_token_sid)) {
+ free(TWG(impersonation_token_sid));
+ }
+ TWG(impersonation_token_sid) = token_sid;
+
+ /* Duplicate the token as impersonated token */
+ if (!DuplicateToken(thread_token, SecurityImpersonation, &TWG(impersonation_token))) {
+ goto Finished;
+ }
+ }
+
if (CWDG(realpath_cache_size_limit)) {
t = time(0);
bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
}
}
}
-
+
/* Do a full access check because access() will only check read-only attribute */
if(mode == 0 || mode > 6) {
if(bucket != NULL && bucket->is_rvalid) {
fAccess = bucket->is_writable;
goto Finished;
}
- desired_access = FILE_GENERIC_WRITE;
+ desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
} else if(mode <= 4) {
if(bucket != NULL && bucket->is_rvalid) {
fAccess = bucket->is_readable;
goto Finished;
}
+ MapGenericMask(&desired_access, &gen_map);
+
if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
- goto Finished;
+ goto Finished_Impersonate;
}
/* Keep the result in realpath_cache */
if(bucket != NULL) {
- if(desired_access == (FILE_GENERIC_READ | FILE_FLAG_BACKUP_SEMANTICS)) {
+ if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) {
bucket->is_rvalid = 1;
bucket->is_readable = fAccess;
}
}
}
-Finished:
+Finished_Impersonate:
if(psec_desc != NULL) {
free(psec_desc);
psec_desc = NULL;
}
+Finished:
if(real_path != NULL) {
free(real_path);
real_path = NULL;
if (err == ERROR_NO_TOKEN) {
asuser = FALSE;
}
-
}
cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c ")+2);