mirror of
https://github.com/php/php-src.git
synced 2026-04-28 02:33:17 +02:00
- #44859, fixed support for windows ACL, drop win9x code
This commit is contained in:
+75
-12
@@ -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
|
||||
SHFILEINFO sfi;
|
||||
|
||||
return access(pathname, 0) == 0 &&
|
||||
SHGetFileInfo(pathname, 0, &sfi, sizeof(SHFILEINFO), SHGFI_EXETYPE) != 0 ? 0 : -1;
|
||||
#endif
|
||||
return GetBinaryType(pathname, &type) ? 0 : -1;
|
||||
} else {
|
||||
return access(pathname, mode);
|
||||
if(access(pathname, mode)) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ typedef struct {
|
||||
int process_size;
|
||||
int shm_size;
|
||||
char *comspec;
|
||||
HANDLE impersonation_token;
|
||||
} tsrm_win32_globals;
|
||||
|
||||
#ifdef ZTS
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
echo FOO
|
||||
Binary file not shown.
Reference in New Issue
Block a user