From 9a63b6108c211e8024acfafc853b6d8641107aaf Mon Sep 17 00:00:00 2001 From: vincentdarley Date: Fri, 10 Mar 2006 10:35:19 +0000 Subject: backport of some file readable/writable fixes from HEAD FossilOrigin-Name: 5e3421a17314f774db35c0055279422e349ce947 --- ChangeLog | 8 +++ win/tclWin32Dll.c | 43 ++++++++++++- win/tclWinFile.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++---- win/tclWinInt.h | 28 ++++++++- 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 + + * 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 *** 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 */ -- cgit v0.12