mirror of
https://github.com/php/php-src.git
synced 2025-08-16 14:08:47 +02:00
Improve ioutil access impl and refactor tsrm_win32_access
This commit is contained in:
parent
2fbdaec03c
commit
a9a49b8250
3 changed files with 167 additions and 161 deletions
|
@ -209,190 +209,175 @@ TSRM_API int tsrm_win32_access(const char *pathname, int mode)
|
||||||
realpath_cache_bucket * bucket = NULL;
|
realpath_cache_bucket * bucket = NULL;
|
||||||
char * real_path = NULL;
|
char * real_path = NULL;
|
||||||
|
|
||||||
|
if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
|
||||||
|
real_path = (char *)malloc(MAXPATHLEN);
|
||||||
|
if(tsrm_realpath(pathname, real_path) == NULL) {
|
||||||
|
SET_ERRNO_FROM_WIN32_CODE(ERROR_FILE_NOT_FOUND);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
pathname = real_path;
|
||||||
|
}
|
||||||
|
|
||||||
PHP_WIN32_IOUTIL_INIT_W(pathname)
|
PHP_WIN32_IOUTIL_INIT_W(pathname)
|
||||||
if (!pathw) {
|
if (!pathw) {
|
||||||
|
free(real_path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == 1 /*X_OK*/) {
|
/* Either access call failed, or the mode was asking for a specific check.*/
|
||||||
DWORD type;
|
int ret = php_win32_ioutil_access_w(pathw, mode);
|
||||||
int ret;
|
if (0 > ret || X_OK == mode || F_OK == mode) {
|
||||||
|
|
||||||
ret = GetBinaryTypeW(pathw, &type) ? 0 : -1;
|
|
||||||
|
|
||||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
PHP_WIN32_IOUTIL_CLEANUP_W()
|
||||||
|
free(real_path);
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
}
|
||||||
if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
|
|
||||||
real_path = (char *)malloc(MAXPATHLEN);
|
|
||||||
if(tsrm_realpath(pathname, real_path) == NULL) {
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
pathname = real_path;
|
|
||||||
PHP_WIN32_IOUTIL_REINIT_W(pathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(php_win32_ioutil_access(pathname, mode)) {
|
|
||||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
|
||||||
free(real_path);
|
|
||||||
return errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If only existence check is made, return now */
|
|
||||||
if (mode == 0) {
|
|
||||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
|
||||||
free(real_path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only in NTS when impersonate==1 (aka FastCGI) */
|
/* Only in NTS when impersonate==1 (aka FastCGI) */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
AccessCheck() requires an impersonation token. We first get a primary
|
AccessCheck() requires an impersonation token. We first get a primary
|
||||||
token and then create a duplicate impersonation token. The
|
token and then create a duplicate impersonation token. The
|
||||||
impersonation token is not actually assigned to the thread, but is
|
impersonation token is not actually assigned to the thread, but is
|
||||||
used in the call to AccessCheck. Thus, this function itself never
|
used in the call to AccessCheck. Thus, this function itself never
|
||||||
impersonates, but does use the identity of the thread. If the thread
|
impersonates, but does use the identity of the thread. If the thread
|
||||||
was impersonating already, this function uses that impersonation context.
|
was impersonating already, this function uses that impersonation context.
|
||||||
*/
|
*/
|
||||||
if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
|
if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
|
||||||
if (GetLastError() == ERROR_NO_TOKEN) {
|
if (GetLastError() == ERROR_NO_TOKEN) {
|
||||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) {
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) {
|
||||||
TWG(impersonation_token) = NULL;
|
TWG(impersonation_token) = NULL;
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* token_sid will be freed in tsrmwin32_dtor */
|
/* token_sid will be freed in tsrmwin32_dtor */
|
||||||
token_sid = tsrm_win32_get_token_sid(thread_token);
|
token_sid = tsrm_win32_get_token_sid(thread_token);
|
||||||
if (!token_sid) {
|
if (!token_sid) {
|
||||||
if (TWG(impersonation_token_sid)) {
|
if (TWG(impersonation_token_sid)) {
|
||||||
free(TWG(impersonation_token_sid));
|
free(TWG(impersonation_token_sid));
|
||||||
}
|
}
|
||||||
TWG(impersonation_token_sid) = NULL;
|
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;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/* we already have it, free it then */
|
||||||
|
free(token_sid);
|
||||||
|
}
|
||||||
|
|
||||||
/* Different identity, we need a new impersontated token as well */
|
if (CWDG(realpath_cache_size_limit)) {
|
||||||
if (!TWG(impersonation_token_sid) || !EqualSid(token_sid, TWG(impersonation_token_sid))) {
|
t = time(0);
|
||||||
if (TWG(impersonation_token_sid)) {
|
bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
|
||||||
free(TWG(impersonation_token_sid));
|
if(bucket == NULL && real_path == NULL) {
|
||||||
|
/* We used the pathname directly. Call tsrm_realpath */
|
||||||
|
/* so that entry is created in realpath cache */
|
||||||
|
real_path = (char *)malloc(MAXPATHLEN);
|
||||||
|
if(tsrm_realpath(pathname, real_path) != NULL) {
|
||||||
|
pathname = real_path;
|
||||||
|
bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
|
||||||
|
PHP_WIN32_IOUTIL_REINIT_W(pathname);
|
||||||
}
|
}
|
||||||
TWG(impersonation_token_sid) = token_sid;
|
|
||||||
|
|
||||||
/* Duplicate the token as impersonated token */
|
|
||||||
if (!DuplicateToken(thread_token, SecurityImpersonation, &TWG(impersonation_token))) {
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* we already have it, free it then */
|
|
||||||
free(token_sid);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (CWDG(realpath_cache_size_limit)) {
|
/* Do a full access check because access() will only check read-only attribute */
|
||||||
t = time(0);
|
if(mode == 0 || mode > 6) {
|
||||||
bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
|
if(bucket != NULL && bucket->is_rvalid) {
|
||||||
if(bucket == NULL && real_path == NULL) {
|
fAccess = bucket->is_readable;
|
||||||
/* We used the pathname directly. Call tsrm_realpath */
|
|
||||||
/* so that entry is created in realpath cache */
|
|
||||||
real_path = (char *)malloc(MAXPATHLEN);
|
|
||||||
if(tsrm_realpath(pathname, real_path) != NULL) {
|
|
||||||
pathname = real_path;
|
|
||||||
bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
|
|
||||||
PHP_WIN32_IOUTIL_REINIT_W(pathname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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_readable;
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
desired_access = FILE_GENERIC_READ;
|
|
||||||
} else if(mode <= 2) {
|
|
||||||
if(bucket != NULL && bucket->is_wvalid) {
|
|
||||||
fAccess = bucket->is_writable;
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
desired_access = FILE_GENERIC_WRITE;
|
|
||||||
} else if(mode <= 4) {
|
|
||||||
if(bucket != NULL && bucket->is_rvalid) {
|
|
||||||
fAccess = bucket->is_readable;
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
desired_access = FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS;
|
|
||||||
} else { // if(mode <= 6)
|
|
||||||
if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
|
|
||||||
fAccess = bucket->is_readable & bucket->is_writable;
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(TWG(impersonation_token) == NULL) {
|
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
desired_access = FILE_GENERIC_READ;
|
||||||
/* Get size of security buffer. Call is expected to fail */
|
} else if(mode <= 2) {
|
||||||
if(GetFileSecurityW(pathw, sec_info, NULL, 0, &sec_desc_length)) {
|
if(bucket != NULL && bucket->is_wvalid) {
|
||||||
|
fAccess = bucket->is_writable;
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
desired_access = FILE_GENERIC_WRITE;
|
||||||
psec_desc = (BYTE *)malloc(sec_desc_length);
|
} else if(mode <= 4) {
|
||||||
if(psec_desc == NULL ||
|
if(bucket != NULL && bucket->is_rvalid) {
|
||||||
!GetFileSecurityW(pathw, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) {
|
fAccess = bucket->is_readable;
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
desired_access = FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS;
|
||||||
MapGenericMask(&desired_access, &gen_map);
|
} else { // if(mode <= 6)
|
||||||
|
if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
|
||||||
if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
|
fAccess = bucket->is_readable & bucket->is_writable;
|
||||||
goto Finished_Impersonate;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Keep the result in realpath_cache */
|
if(TWG(impersonation_token) == NULL) {
|
||||||
if(bucket != NULL) {
|
goto Finished;
|
||||||
if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) {
|
}
|
||||||
bucket->is_rvalid = 1;
|
|
||||||
bucket->is_readable = fAccess;
|
/* Get size of security buffer. Call is expected to fail */
|
||||||
}
|
if(GetFileSecurityW(pathw, sec_info, NULL, 0, &sec_desc_length)) {
|
||||||
else if(desired_access == FILE_GENERIC_WRITE) {
|
goto Finished;
|
||||||
bucket->is_wvalid = 1;
|
}
|
||||||
bucket->is_writable = fAccess;
|
|
||||||
} else if (desired_access == (FILE_GENERIC_READ | FILE_GENERIC_WRITE)) {
|
psec_desc = (BYTE *)malloc(sec_desc_length);
|
||||||
bucket->is_rvalid = 1;
|
if(psec_desc == NULL ||
|
||||||
bucket->is_readable = fAccess;
|
!GetFileSecurityW(pathw, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) {
|
||||||
bucket->is_wvalid = 1;
|
goto Finished;
|
||||||
bucket->is_writable = fAccess;
|
}
|
||||||
}
|
|
||||||
|
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_Impersonate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep the result in realpath_cache */
|
||||||
|
if(bucket != NULL) {
|
||||||
|
if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) {
|
||||||
|
bucket->is_rvalid = 1;
|
||||||
|
bucket->is_readable = fAccess;
|
||||||
}
|
}
|
||||||
|
else if(desired_access == FILE_GENERIC_WRITE) {
|
||||||
|
bucket->is_wvalid = 1;
|
||||||
|
bucket->is_writable = fAccess;
|
||||||
|
} else if (desired_access == (FILE_GENERIC_READ | FILE_GENERIC_WRITE)) {
|
||||||
|
bucket->is_rvalid = 1;
|
||||||
|
bucket->is_readable = fAccess;
|
||||||
|
bucket->is_wvalid = 1;
|
||||||
|
bucket->is_writable = fAccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Finished_Impersonate:
|
Finished_Impersonate:
|
||||||
if(psec_desc != NULL) {
|
if(psec_desc != NULL) {
|
||||||
free(psec_desc);
|
free(psec_desc);
|
||||||
psec_desc = NULL;
|
psec_desc = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Finished:
|
Finished:
|
||||||
if(thread_token != NULL) {
|
if(thread_token != NULL) {
|
||||||
CloseHandle(thread_token);
|
CloseHandle(thread_token);
|
||||||
}
|
}
|
||||||
if(real_path != NULL) {
|
if(real_path != NULL) {
|
||||||
free(real_path);
|
free(real_path);
|
||||||
real_path = NULL;
|
real_path = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
PHP_WIN32_IOUTIL_CLEANUP_W()
|
||||||
if(fAccess == FALSE) {
|
if(fAccess == FALSE) {
|
||||||
errno = EACCES;
|
errno = EACCES;
|
||||||
return errno;
|
return errno;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}/*}}}*/
|
}/*}}}*/
|
||||||
|
|
||||||
|
|
|
@ -655,13 +655,33 @@ BOOL php_win32_ioutil_init(void)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}/*}}}*/
|
}/*}}}*/
|
||||||
|
|
||||||
/* an extended version could be implemented, for now direct functions can be used. */
|
|
||||||
#if 0
|
|
||||||
PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode)
|
PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode)
|
||||||
{
|
{/*{{{*/
|
||||||
return _waccess(path, mode);
|
DWORD attr, err;
|
||||||
}
|
|
||||||
#endif
|
if ((mode & X_OK) == X_OK) {
|
||||||
|
DWORD type;
|
||||||
|
return GetBinaryTypeW(path, &type) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
attr = GetFileAttributesW(path);
|
||||||
|
if (attr == INVALID_FILE_ATTRIBUTES) {
|
||||||
|
err = GetLastError();
|
||||||
|
SET_ERRNO_FROM_WIN32_CODE(err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (F_OK == mode) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mode &W_OK) == W_OK && (attr & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) {
|
||||||
|
SET_ERRNO_FROM_WIN32_CODE(ERROR_ACCESS_DENIED);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}/*}}}*/
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
PW32IO HANDLE php_win32_ioutil_findfirstfile_w(char *path, WIN32_FIND_DATA *data)
|
PW32IO HANDLE php_win32_ioutil_findfirstfile_w(char *path, WIN32_FIND_DATA *data)
|
||||||
|
|
|
@ -242,6 +242,7 @@ PW32IO int php_win32_ioutil_chdir_w(const wchar_t *path);
|
||||||
PW32IO int php_win32_ioutil_rename_w(const wchar_t *oldname, const wchar_t *newname);
|
PW32IO int php_win32_ioutil_rename_w(const wchar_t *oldname, const wchar_t *newname);
|
||||||
PW32IO wchar_t *php_win32_ioutil_getcwd_w(wchar_t *buf, size_t len);
|
PW32IO wchar_t *php_win32_ioutil_getcwd_w(wchar_t *buf, size_t len);
|
||||||
PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path);
|
PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path);
|
||||||
|
PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode);
|
PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode);
|
||||||
|
@ -264,14 +265,14 @@ __forceinline static int php_win32_ioutil_access(const char *path, mode_t mode)
|
||||||
|
|
||||||
PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
|
PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
|
||||||
|
|
||||||
ret = _waccess(pathw, mode);
|
ret = php_win32_ioutil_access_w(pathw, mode);
|
||||||
if (0 > ret) {
|
if (0 > ret) {
|
||||||
_get_errno(&err);
|
err = GetLastError();
|
||||||
}
|
}
|
||||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
PHP_WIN32_IOUTIL_CLEANUP_W()
|
||||||
|
|
||||||
if (0 > ret) {
|
if (0 > ret) {
|
||||||
_set_errno(err);
|
SET_ERRNO_FROM_WIN32_CODE(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue