diff options
author | Pierre Joye <pajoye@php.net> | 2009-05-17 19:44:27 +0000 |
---|---|---|
committer | Pierre Joye <pajoye@php.net> | 2009-05-17 19:44:27 +0000 |
commit | 10ace3fe979eba2e440cb184f0ae53ecbba26465 (patch) | |
tree | 5f728f22c2df8ba03c3f0ae5a4ab10d68ce3e80a | |
parent | 47f87a58aa6b2ff1c0e67225e884c7e3a788effb (diff) | |
download | php-git-10ace3fe979eba2e440cb184f0ae53ecbba26465.tar.gz |
- #44859, fixed support for windows ACL, drop win9x code
-rw-r--r-- | TSRM/tsrm_win32.c | 85 | ||||
-rw-r--r-- | TSRM/tsrm_win32.h | 1 | ||||
-rw-r--r-- | ext/standard/tests/file/windows_acls/bug44859.phpt | 60 | ||||
-rw-r--r-- | ext/standard/tests/file/windows_acls/bug44859_2.phpt | 60 | ||||
-rw-r--r-- | ext/standard/tests/file/windows_acls/bug44859_3.phpt | 36 | ||||
-rw-r--r-- | ext/standard/tests/file/windows_acls/common.inc | 177 | ||||
-rw-r--r-- | ext/standard/tests/file/windows_acls/tiny.bat | 1 | ||||
-rw-r--r-- | ext/standard/tests/file/windows_acls/tiny.exe | bin | 0 -> 133 bytes |
8 files changed, 409 insertions, 11 deletions
diff --git a/TSRM/tsrm_win32.c b/TSRM/tsrm_win32.c index 635bdf1c47..9e7ece0ed3 100644 --- a/TSRM/tsrm_win32.c +++ b/TSRM/tsrm_win32.c @@ -23,6 +23,7 @@ #include <io.h> #include <process.h> #include <time.h> +#include <errno.h> #define TSRM_INCLUDE_FULL_WINDOWS_HEADERS @@ -45,6 +46,7 @@ static void tsrm_win32_ctor(tsrm_win32_globals *globals TSRMLS_DC) globals->process_size = 0; globals->shm_size = 0; globals->comspec = _strdup((GetVersion()<0x80000000)?"cmd.exe":"command.com"); + globals->impersonation_token = NULL; } static void tsrm_win32_dtor(tsrm_win32_globals *globals TSRMLS_DC) @@ -86,21 +88,82 @@ TSRM_API void tsrm_win32_shutdown(void) TSRM_API int tsrm_win32_access(const char *pathname, int mode) { + 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); + + PRIVILEGE_SET privilege_set = {0}; + DWORD sec_desc_length = 0, desired_access = 0, granted_access = 0; + BYTE * psec_desc = NULL; + BOOL fAccess = FALSE; + HANDLE process_token = NULL; + TSRMLS_FETCH(); + if (mode == 1 /*X_OK*/) { -#if 1 - /* This code is not supported by Windows 98, - * but we don't support it anymore */ DWORD type; + return GetBinaryType(pathname, &type) ? 0 : -1; + } else { + if(access(pathname, mode)) { + return errno; + } - return GetBinaryType(pathname, &type)?0:-1; -#else - SHFILEINFO sfi; + /* Do a full access check because access() will only check read-only attribute */ + if(mode == 0 || mode > 6) { + desired_access = FILE_GENERIC_READ; + } else if(mode <= 2) { + desired_access = FILE_GENERIC_WRITE; + } else if(mode <= 4) { + desired_access = FILE_GENERIC_READ; + } else { // if(mode <= 6) + desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; + } - return access(pathname, 0) == 0 && - SHGetFileInfo(pathname, 0, &sfi, sizeof(SHFILEINFO), SHGFI_EXETYPE) != 0 ? 0 : -1; -#endif - } else { - return access(pathname, mode); + /* Get size of security buffer. Call is expected to fail */ + if(GetFileSecurity(pathname, sec_info, NULL, 0, &sec_desc_length)) { + goto Finished; + } + + psec_desc = (BYTE *)malloc(sec_desc_length); + if(psec_desc == NULL || + !GetFileSecurity(pathname, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) { + goto Finished; + } + + if(TWG(impersonation_token) == NULL) { + + if(!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, &process_token)) { + goto Finished; + } + + /* Access check requires impersonation token. Create a duplicate token. */ + if(!DuplicateToken(process_token, SecurityImpersonation, &TWG(impersonation_token))) { + goto Finished; + } + } + + if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) { + goto Finished; + } + +Finished: + + /* impersonation_token will be closed when the process dies */ + if(process_token != NULL) { + CloseHandle(process_token); + process_token = NULL; + } + + if(psec_desc != NULL) { + free(psec_desc); + psec_desc = NULL; + } + + if(fAccess == FALSE) { + errno = EACCES; + return errno; + } else { + return 0; + } } } diff --git a/TSRM/tsrm_win32.h b/TSRM/tsrm_win32.h index a83d6ed1c8..acd0e683a2 100644 --- a/TSRM/tsrm_win32.h +++ b/TSRM/tsrm_win32.h @@ -63,6 +63,7 @@ typedef struct { int process_size; int shm_size; char *comspec; + HANDLE impersonation_token; } tsrm_win32_globals; #ifdef ZTS diff --git a/ext/standard/tests/file/windows_acls/bug44859.phpt b/ext/standard/tests/file/windows_acls/bug44859.phpt new file mode 100644 index 0000000000..0716ee5ac4 --- /dev/null +++ b/ext/standard/tests/file/windows_acls/bug44859.phpt @@ -0,0 +1,60 @@ +--TEST-- +bug #44859 (incorrect result with NTFS ACL permissions, is_writable) +--SKIPIF-- +<?php +include_once __DIR__ . '/common.inc'; +skipif(); +?> +--FILE-- +<?php +include_once __DIR__ . '/common.inc'; + +$iteration = array( + PHPT_ACL_READ => false, + PHPT_ACL_NONE => false, + PHPT_ACL_WRITE => true, + PHPT_ACL_WRITE|PHPT_ACL_READ => true, +); + +echo "Testing file:\n"; +$i = 1; +$path = __DIR__ . '/a.txt'; +foreach ($iteration as $perms => $exp) { + create_file($path, $perms); + echo 'Iteration #' . $i++ . ': '; + if (is_writable($path) == $exp) { + echo "passed.\n"; + } else { + var_dump(is_writable($path), $exp); + echo "failed.\n"; + } + delete_file($path); +} + +echo "Testing directory:\n"; +$path = __DIR__ . '/adir'; +$i = 1; +foreach ($iteration as $perms => $exp) { + create_file($path, $perms); + echo 'Iteration #' . $i++ . ': '; + if (is_writable($path) == $exp) { + echo "passed.\n"; + } else { + var_dump(is_writable($path), $exp); + echo "failed.\n"; + } + delete_file($path); +} + +?> +--EXPECT-- +Testing file: +Iteration #1: passed. +Iteration #2: passed. +Iteration #3: passed. +Iteration #4: passed. +Testing directory: +Iteration #1: passed. +Iteration #2: passed. +Iteration #3: passed. +Iteration #4: passed. diff --git a/ext/standard/tests/file/windows_acls/bug44859_2.phpt b/ext/standard/tests/file/windows_acls/bug44859_2.phpt new file mode 100644 index 0000000000..8326eb473e --- /dev/null +++ b/ext/standard/tests/file/windows_acls/bug44859_2.phpt @@ -0,0 +1,60 @@ +--TEST-- +bug #44859 (incorrect result with NTFS ACL permissions, is_readable) +--SKIPIF-- +<?php +include_once __DIR__ . '/common.inc'; +skipif(); +?> +--FILE-- +<?php +include_once __DIR__ . '/common.inc'; + +$iteration = array( + PHPT_ACL_READ => true, + PHPT_ACL_NONE => false, + PHPT_ACL_WRITE => false, + PHPT_ACL_WRITE|PHPT_ACL_READ => true, +); + +echo "Testing file:\n"; +$i = 1; +$path = __DIR__ . '/a.txt'; +foreach ($iteration as $perms => $exp) { + create_file($path, $perms); + echo 'Iteration #' . $i++ . ': '; + if (is_readable($path) == $exp) { + echo "passed.\n"; + } else { + var_dump(is_writable($path), $exp); + echo "failed.\n"; + } + delete_file($path); +} + +echo "Testing directory:\n"; +$path = __DIR__ . '/adir'; +$i = 1; +foreach ($iteration as $perms => $exp) { + create_file($path, $perms); + echo 'Iteration #' . $i++ . ': '; + if (is_readable($path) == $exp) { + echo "passed.\n"; + } else { + var_dump(is_writable($path), $exp); + echo "failed.\n"; + } + delete_file($path); +} + +?> +--EXPECT-- +Testing file: +Iteration #1: passed. +Iteration #2: passed. +Iteration #3: passed. +Iteration #4: passed. +Testing directory: +Iteration #1: passed. +Iteration #2: passed. +Iteration #3: passed. +Iteration #4: passed. diff --git a/ext/standard/tests/file/windows_acls/bug44859_3.phpt b/ext/standard/tests/file/windows_acls/bug44859_3.phpt new file mode 100644 index 0000000000..7300112fc0 --- /dev/null +++ b/ext/standard/tests/file/windows_acls/bug44859_3.phpt @@ -0,0 +1,36 @@ +--TEST-- +bug #44859 (incorrect result with NTFS ACL permissions, is_executable) +--SKIPIF-- +<?php +include_once __DIR__ . '/common.inc'; +skipif(); +?> +--FILE-- +<?php +include_once __DIR__ . '/common.inc'; + +$iteration = array( + 'tiny.exe' => true, + //'tiny.bat' => true, To be fixed in _access + __FILE__ => false +); + +$i = 1; +$path = __DIR__; + +foreach ($iteration as $file => $exp) { + $path = __DIR__ . '/' . $file; + echo 'Iteration #' . $i++ . ': '; + if (is_executable($path) == $exp) { + echo "passed.\n"; + } else { + var_dump(is_executable($path), $exp); + echo "failed.\n"; + } +} + + +?> +--EXPECT-- +Iteration #1: passed. +Iteration #2: passed. diff --git a/ext/standard/tests/file/windows_acls/common.inc b/ext/standard/tests/file/windows_acls/common.inc new file mode 100644 index 0000000000..6bcaf2b327 --- /dev/null +++ b/ext/standard/tests/file/windows_acls/common.inc @@ -0,0 +1,177 @@ +<?php +error_reporting(E_ALL); +define('PHPT_ACL_READ', 1 << 1); +define('PHPT_ACL_WRITE', 1 << 2); +define('PHPT_ACL_EXEC', 1 << 3); +define('PHPT_ACL_NONE', 1 << 4); +define('PHPT_ACL_FULL', 1 << 5); + +define('PHPT_ACL_GRANT', 1); +define('PHPT_ACL_DENY', 2); + +function skipif() { + if(substr(PHP_OS, 0, 3) != 'WIN' ) { + die('skip windows only test'); + } + if(stripos(php_uname(), 'XP') !== FALSE) { + die('skip windows 2003 or newer only test'); + } +} + +function get_username(){ + return getenv('USERNAME'); +} + +function get_domainname() +{ + return getenv('USERDOMAIN'); +} + +function icacls_set($path, $mode, $perm) { + $user = get_username(); + $path_escaped = '"' . $path . '"'; + $perm_entry = array(); + + if ($perm & PHPT_ACL_READ) $perm_entry[] = 'R'; + if ($perm & PHPT_ACL_WRITE) $perm_entry[] = 'W'; + if ($perm & PHPT_ACL_EXEC) $perm_entry[] = 'RX'; + if ($perm & PHPT_ACL_FULL) $perm_entry[] = 'F'; + + // Deny all + $cmd = 'icacls ' . $path_escaped . ' /inheritance:r /deny ' . $user . ':(F,M,R,RX,W)'; + exec($cmd); + + if ($perm & PHPT_ACL_NONE) { + /* + This is required to remove all the previously denied + permission for the USER. Just granting permission doesn't + remove the previously denied permission. + */ + $cmd = 'icacls ' . $path_escaped . ' /' . 'remove:d'; + $cmd .= ' ' . $user; + exec($cmd); + $cmd = 'icacls ' . $path_escaped . ' /' . 'remove:g'; + $cmd .= ' ' . $user; + exec($cmd); + return; + } + + if ($mode == PHPT_ACL_GRANT) { + $mode = 'grant'; + } else { + $mode = 'deny'; + } + + + // Deny all + $cmd = 'icacls ' . $path_escaped . ' /deny ' . $user . ':(F,M,R,RX,W)'; + exec($cmd); + + /* + This is required to remove all the previously denied + permission for the USER. Just granting permission doesn't + remove the previously denied permission. + */ + $cmd = 'icacls ' . $path_escaped . ' /' . 'remove:d'; + $cmd .= ' ' . $user; + exec($cmd); + $cmd = 'icacls ' . $path_escaped . ' /' . 'remove:g'; + $cmd .= ' ' . $user; + exec($cmd); + + + /* + Required to set no permission and check that is_readable() + returns false. If the $perm_entry contains 'N' skip this step. + This will make the file/dir with NO aceess. + */ + if (!in_array('N', $perm_entry)) { + /* + This is required to remove all the previously denied + permission for the USER. Just granting permission doesn't + remove the previously denied permission. + */ + $cmd = 'icacls ' . $path_escaped . ' /' . 'remove:d'; + $cmd .= ' ' . get_username(); + exec($cmd); + $cmd = 'icacls ' . $path_escaped . ' /' . 'remove:g'; + $cmd .= ' ' . get_username(); + exec($cmd); + + $cmd = 'icacls ' . $path_escaped . ' /' . $mode; + $cmd .= ' ' . get_username(); + $cmd .= ':' . '(' . implode($perm_entry, ',') . ')'; + exec($cmd); + } +} + +function create_dir($name, $perms) { + if (empty($name)) { + echo "create_dir: Empty name is not allowed\n"; + return; + } + + mkdir($name); + $dst = realpath($name); + icacls_set($name, PHPT_ACL_GRANT, $perms); +} + +function create_file($name, $perms) { + if (empty($name)) { + echo "create_dir: Empty name is not allowed\n"; + return; + } + + touch($name); + $dst = realpath($name); + icacls_set($name, PHPT_ACL_GRANT, $perms); +} + +function delete_file($path) { + icacls_set($path, PHPT_ACL_GRANT, PHPT_ACL_FULL); + if (is_file($path)) { + unlink($path); + } else { + echo "delete_file: '$path' is not a file\n"; + return; + } +} + +function delete_dir($path) { + if (is_dir($path)) { + icacls_set($path, PHPT_ACL_GRANT, PHPT_ACL_FULL); + rmdir($path); + } else { + echo "delete_dir: '$path' is not a directory\n"; + return; + } +} +if (0) { +$path = __DIR__ . '/a.txt'; +create_file($path, PHPT_ACL_NONE); +if (!is_writable($path)) { + echo "PHPT_ACL_NONE success!!\n"; +} else { + echo "PHPT_ACL_NONE failed!!\n"; +} +delete_file($path); + +$path = __DIR__ . '/a.txt'; +create_file($path, PHPT_ACL_READ); +if (!is_writable($path)) { + echo "PHPT_ACL_READ success!!\n"; +} else { + echo "PHPT_ACL_READ failed!!\n"; +} +delete_file($path); + +$path = __DIR__ . '/adir'; +create_dir($path, PHPT_ACL_READ); +if (!is_writable($path)) { + echo "PHPT_ACL_READ dir success!!\n"; +} else { + echo "PHPT_ACL_READ dir failed!!\n"; +} +delete_dir($path); + +} diff --git a/ext/standard/tests/file/windows_acls/tiny.bat b/ext/standard/tests/file/windows_acls/tiny.bat new file mode 100644 index 0000000000..c5464d3bd9 --- /dev/null +++ b/ext/standard/tests/file/windows_acls/tiny.bat @@ -0,0 +1 @@ +echo FOO diff --git a/ext/standard/tests/file/windows_acls/tiny.exe b/ext/standard/tests/file/windows_acls/tiny.exe Binary files differnew file mode 100644 index 0000000000..a27e3e6b9f --- /dev/null +++ b/ext/standard/tests/file/windows_acls/tiny.exe |