summaryrefslogtreecommitdiffstats
path: root/win/tclWinFile.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/tclWinFile.c')
-rw-r--r--win/tclWinFile.c184
1 files changed, 172 insertions, 12 deletions
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;
}