1
0
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:
Pierre Joye
2009-05-17 19:44:27 +00:00
parent 47f87a58aa
commit 10ace3fe97
8 changed files with 410 additions and 12 deletions
+75 -12
View File
@@ -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;
}
}
}
+1
View File
@@ -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.