diff options
Diffstat (limited to 'win/tclWinFile.c')
-rw-r--r-- | win/tclWinFile.c | 184 |
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; } |