summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvincentdarley <vincentdarley>2006-03-10 10:35:20 (GMT)
committervincentdarley <vincentdarley>2006-03-10 10:35:20 (GMT)
commit016159504aae4e7bcafa2c3aef5c0a23d55fa308 (patch)
treed250cf6e569ea5102585a2cb99bbcb5daa76138d
parent962568fbf7071d27b14e7329a74ac3ac9c91727f (diff)
downloadtcl-016159504aae4e7bcafa2c3aef5c0a23d55fa308.zip
tcl-016159504aae4e7bcafa2c3aef5c0a23d55fa308.tar.gz
tcl-016159504aae4e7bcafa2c3aef5c0a23d55fa308.tar.bz2
backport of some file readable/writable fixes from HEAD
-rw-r--r--ChangeLog8
-rw-r--r--win/tclWin32Dll.c43
-rw-r--r--win/tclWinFile.c184
-rw-r--r--win/tclWinInt.h28
4 files changed, 249 insertions, 14 deletions
diff --git a/ChangeLog b/ChangeLog
index 173123a..96987e8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2006-03-10 Vince Darley <vincentdarley@sourceforge.net>
+
+ * win/tclWin32Dll.c:
+ * win/tclWinInt.h:
+ * win/tclWinFile.c: backported some fixes from HEAD relating
+ to 'file readable' and 'file writable', but main 'file writable'
+ bug still outstanding.
+
2006-03-07 Don Porter <dgp@users.sourceforge.net>
*** 8.4.13 TAGGED FOR RELEASE ***
diff --git a/win/tclWin32Dll.c b/win/tclWin32Dll.c
index 1e4a0b3..c107396 100644
--- a/win/tclWin32Dll.c
+++ b/win/tclWin32Dll.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWin32Dll.c,v 1.24.2.8 2005/11/03 11:53:59 patthoyts Exp $
+ * RCS: @(#) $Id: tclWin32Dll.c,v 1.24.2.9 2006/03/10 10:35:24 vincentdarley Exp $
*/
#include "tclWinInt.h"
@@ -118,6 +118,11 @@ static TclWinProcs asciiProcs = {
(int (__cdecl*)(CONST TCHAR *, struct _utimbuf *)) _utime,
NULL,
NULL,
+ /* getLongPathNameProc */
+ NULL,
+ /* Security SDK - not available on 95,98,ME */
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ /* ReadConsole and WriteConsole */
(BOOL (WINAPI *)(HANDLE, LPVOID, DWORD, LPDWORD, LPVOID)) ReadConsoleA,
(BOOL (WINAPI *)(HANDLE, const VOID*, DWORD, LPDWORD, LPVOID)) WriteConsoleA
};
@@ -169,6 +174,11 @@ static TclWinProcs unicodeProcs = {
(int (__cdecl*)(CONST TCHAR *, struct _utimbuf *)) _wutime,
NULL,
NULL,
+ /* getLongPathNameProc */
+ NULL,
+ /* Security SDK - will be filled in on NT,XP,2000,2003 */
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ /* ReadConsole and WriteConsole */
(BOOL (WINAPI *)(HANDLE, LPVOID, DWORD, LPDWORD, LPVOID)) ReadConsoleW,
(BOOL (WINAPI *)(HANDLE, const VOID*, DWORD, LPDWORD, LPVOID)) WriteConsoleW
};
@@ -692,6 +702,36 @@ TclWinSetInterfaces(
"GetVolumeNameForVolumeMountPointW");
FreeLibrary(hInstance);
}
+ hInstance = LoadLibraryA("advapi32");
+ if (hInstance != NULL) {
+ tclWinProcs->getFileSecurityProc = (BOOL (WINAPI *)(
+ LPCTSTR lpFileName,
+ SECURITY_INFORMATION RequestedInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ DWORD nLength, LPDWORD lpnLengthNeeded))
+ GetProcAddress(hInstance, "GetFileSecurityW");
+ tclWinProcs->impersonateSelfProc = (BOOL (WINAPI *) (
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel))
+ GetProcAddress(hInstance, "ImpersonateSelf");
+ tclWinProcs->openThreadTokenProc = (BOOL (WINAPI *) (
+ HANDLE ThreadHandle, DWORD DesiredAccess,
+ BOOL OpenAsSelf, PHANDLE TokenHandle))
+ GetProcAddress(hInstance, "OpenThreadToken");
+ tclWinProcs->revertToSelfProc = (BOOL (WINAPI *) (void))
+ GetProcAddress(hInstance, "RevertToSelf");
+ tclWinProcs->mapGenericMaskProc = (VOID (WINAPI *) (
+ PDWORD AccessMask, PGENERIC_MAPPING GenericMapping))
+ GetProcAddress(hInstance, "MapGenericMask");
+ tclWinProcs->accessCheckProc = (BOOL (WINAPI *)(
+ PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ HANDLE ClientToken, DWORD DesiredAccess,
+ PGENERIC_MAPPING GenericMapping,
+ PPRIVILEGE_SET PrivilegeSet,
+ LPDWORD PrivilegeSetLength, LPDWORD GrantedAccess,
+ LPBOOL AccessStatus)) GetProcAddress(hInstance,
+ "AccessCheck");
+ FreeLibrary(hInstance);
+ }
}
} else {
tclWinProcs = &asciiProcs;
@@ -710,6 +750,7 @@ TclWinSetInterfaces(
(HANDLE (WINAPI *)(CONST TCHAR*, UINT,
LPVOID, UINT, LPVOID, DWORD)) GetProcAddress(hInstance,
"FindFirstFileExA");
+ tclWinProcs->getLongPathNameProc = NULL;
tclWinProcs->getVolumeNameForVMPProc =
(BOOL (WINAPI *)(CONST TCHAR*, TCHAR*,
DWORD)) GetProcAddress(hInstance,
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index 4171e67..f4d882b 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinFile.c,v 1.44.2.13 2005/11/15 16:41:41 kennykb Exp $
+ * RCS: @(#) $Id: tclWinFile.c,v 1.44.2.14 2006/03/10 10:35:25 vincentdarley Exp $
*/
//#define _WIN32_WINNT 0x0500
@@ -1334,16 +1334,22 @@ NativeAccess(
if (attr == 0xffffffff) {
/*
- * File doesn't exist.
+ * File doesn't exist.
*/
TclWinConvertError(GetLastError());
return -1;
}
- if ((mode & W_OK) && (attr & FILE_ATTRIBUTE_READONLY)) {
+ if ((mode & W_OK)
+ && !(attr & FILE_ATTRIBUTE_DIRECTORY)
+ /* && (tclWinProcs->getFileSecurityProc == NULL) */
+ && (attr & FILE_ATTRIBUTE_READONLY)) {
/*
- * File is not writable.
+ * We don't have the advanced 'getFileSecurityProc', and
+ * our attributes say the file is not writable. If we
+ * do have 'getFileSecurityProc', we'll do a more
+ * robust XP-related check below.
*/
Tcl_SetErrno(EACCES);
@@ -1351,20 +1357,174 @@ NativeAccess(
}
if (mode & X_OK) {
- if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ if (!(attr & FILE_ATTRIBUTE_DIRECTORY) && !NativeIsExec(nativePath)) {
/*
- * Directories are always executable.
+ * It's not a directory and doesn't have the correct extension.
+ * Therefore it can't be executable
+ */
+
+ Tcl_SetErrno(EACCES);
+ return -1;
+ }
+ }
+
+ /*
+ * It looks as if the permissions are ok, but if we are on NT, 2000 or XP,
+ * we have a more complex permissions structure so we try to check that.
+ * The code below is remarkably complex for such a simple thing as finding
+ * what permissions the OS has set for a file.
+ *
+ * If we are simply checking for file existence, then we don't need all
+ * these complications (which are really quite slow: with this code 'file
+ * readable' is 5-6 times slower than 'file exists').
+ */
+
+ if ((mode != F_OK) && (tclWinProcs->getFileSecurityProc != NULL)) {
+ SECURITY_DESCRIPTOR *sdPtr = NULL;
+ unsigned long size;
+ GENERIC_MAPPING genMap;
+ HANDLE hToken = NULL;
+ DWORD desiredAccess = 0;
+ DWORD grantedAccess = 0;
+ BOOL accessYesNo = FALSE;
+ PRIVILEGE_SET privSet;
+ DWORD privSetSize = sizeof(PRIVILEGE_SET);
+ int error;
+
+ /*
+ * First find out how big the buffer needs to be
+ */
+
+ size = 0;
+ (*tclWinProcs->getFileSecurityProc)(nativePath,
+ OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION
+ | DACL_SECURITY_INFORMATION, 0, 0, &size);
+
+ /*
+ * Should have failed with ERROR_INSUFFICIENT_BUFFER
+ */
+
+ error = GetLastError();
+ if (error != ERROR_INSUFFICIENT_BUFFER) {
+ /*
+ * Most likely case is ERROR_ACCESS_DENIED, which we will convert
+ * to EACCES - just what we want!
+ */
+
+ TclWinConvertError((DWORD)error);
+ return -1;
+ }
+
+ /*
+ * Now size contains the size of buffer needed
+ */
+
+ sdPtr = (SECURITY_DESCRIPTOR *) HeapAlloc(GetProcessHeap(), 0, size);
+
+ if (sdPtr == NULL) {
+ goto accessError;
+ }
+
+ /*
+ * Call GetFileSecurity() for real
+ */
+
+ if (!(*tclWinProcs->getFileSecurityProc)(nativePath,
+ OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION
+ | DACL_SECURITY_INFORMATION, sdPtr, size, &size)) {
+ /*
+ * Error getting owner SD
+ */
+
+ goto accessError;
+ }
+
+ /*
+ * Perform security impersonation of the user and open the
+ * resulting thread token.
+ */
+
+ if (!(*tclWinProcs->impersonateSelfProc)(SecurityImpersonation)) {
+ /*
+ * Unable to perform security impersonation.
*/
- return 0;
+ goto accessError;
}
- if (NativeIsExec(nativePath)) {
- return 0;
+ if (!(*tclWinProcs->openThreadTokenProc)(GetCurrentThread (),
+ TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &hToken)) {
+ /*
+ * Unable to get current thread's token.
+ */
+
+ goto accessError;
+ }
+
+ (*tclWinProcs->revertToSelfProc)();
+
+ /*
+ * Setup desiredAccess according to the access priveleges we are
+ * checking.
+ */
+
+ if (mode & R_OK) {
+ desiredAccess |= FILE_GENERIC_READ;
+ }
+ if (mode & W_OK) {
+ desiredAccess |= FILE_GENERIC_WRITE;
+ }
+ if (mode & X_OK) {
+ desiredAccess |= FILE_GENERIC_EXECUTE;
+ }
+
+ memset (&genMap, 0x0, sizeof (GENERIC_MAPPING));
+ genMap.GenericRead = FILE_GENERIC_READ;
+ genMap.GenericWrite = FILE_GENERIC_WRITE;
+ genMap.GenericExecute = FILE_GENERIC_EXECUTE;
+ genMap.GenericAll = FILE_ALL_ACCESS;
+
+ /*
+ * Perform access check using the token.
+ */
+
+ if (!(*tclWinProcs->accessCheckProc)(sdPtr, hToken, desiredAccess,
+ &genMap, &privSet, &privSetSize, &grantedAccess,
+ &accessYesNo)) {
+ /*
+ * Unable to perform access check.
+ */
+
+ accessError:
+ TclWinConvertError(GetLastError());
+ if (sdPtr != NULL) {
+ HeapFree(GetProcessHeap(), 0, sdPtr);
+ }
+ if (hToken != NULL) {
+ CloseHandle(hToken);
+ }
+ return -1;
}
- Tcl_SetErrno(EACCES);
- return -1;
- }
+ /*
+ * Clean up.
+ */
+
+ HeapFree(GetProcessHeap (), 0, sdPtr);
+ CloseHandle(hToken);
+ if (!accessYesNo) {
+ Tcl_SetErrno(EACCES);
+ return -1;
+ }
+ /*
+ * For directories the above checks are ok. For files, though,
+ * we must still check the 'attr' value.
+ */
+ if ((mode & W_OK)
+ && (attr & FILE_ATTRIBUTE_READONLY)) {
+ Tcl_SetErrno(EACCES);
+ return -1;
+ }
+ }
return 0;
}
diff --git a/win/tclWinInt.h b/win/tclWinInt.h
index 5cefc9d..3b06f50 100644
--- a/win/tclWinInt.h
+++ b/win/tclWinInt.h
@@ -8,7 +8,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinInt.h,v 1.20.2.4 2005/11/03 11:53:59 patthoyts Exp $
+ * RCS: @(#) $Id: tclWinInt.h,v 1.20.2.5 2006/03/10 10:35:25 vincentdarley Exp $
*/
#ifndef _TCLWININT
@@ -112,6 +112,32 @@ typedef struct TclWinProcs {
LPVOID, DWORD);
BOOL (WINAPI *getVolumeNameForVMPProc)(CONST TCHAR*, TCHAR*, DWORD);
+ DWORD (WINAPI *getLongPathNameProc)(CONST TCHAR*, TCHAR*, DWORD);
+ /*
+ * These six are for the security sdk to get correct file
+ * permissions on NT, 2000, XP, etc. On 95,98,ME they are
+ * always null.
+ */
+ BOOL (WINAPI *getFileSecurityProc)(LPCTSTR lpFileName,
+ SECURITY_INFORMATION RequestedInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ DWORD nLength,
+ LPDWORD lpnLengthNeeded);
+ BOOL (WINAPI *impersonateSelfProc) (SECURITY_IMPERSONATION_LEVEL
+ ImpersonationLevel);
+ BOOL (WINAPI *openThreadTokenProc) (HANDLE ThreadHandle,
+ DWORD DesiredAccess, BOOL OpenAsSelf,
+ PHANDLE TokenHandle);
+ BOOL (WINAPI *revertToSelfProc) (void);
+ VOID (WINAPI *mapGenericMaskProc) (PDWORD AccessMask,
+ PGENERIC_MAPPING GenericMapping);
+ BOOL (WINAPI *accessCheckProc)(PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ HANDLE ClientToken, DWORD DesiredAccess,
+ PGENERIC_MAPPING GenericMapping,
+ PPRIVILEGE_SET PrivilegeSet,
+ LPDWORD PrivilegeSetLength,
+ LPDWORD GrantedAccess,
+ LPBOOL AccessStatus);
/*
* Unicode console support. WriteConsole and ReadConsole
*/