diff options
Diffstat (limited to 'win')
-rw-r--r-- | win/tclWin32Dll.c | 37 | ||||
-rw-r--r-- | win/tclWinFCmd.c | 31 | ||||
-rw-r--r-- | win/tclWinFile.c | 158 | ||||
-rw-r--r-- | win/tclWinInt.h | 27 |
4 files changed, 228 insertions, 25 deletions
diff --git a/win/tclWin32Dll.c b/win/tclWin32Dll.c index 09b9046..dc497f4 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.27 2003/09/29 22:38:21 dkf Exp $ + * RCS: @(#) $Id: tclWin32Dll.c,v 1.28 2003/10/13 16:48:07 vincentdarley Exp $ */ #include "tclWinInt.h" @@ -99,6 +99,8 @@ static TclWinProcs asciiProcs = { (int (__cdecl*)(CONST TCHAR *, struct _utimbuf *)) _utime, NULL, NULL, + /* Security SDK - not available on 95,98,ME */ + NULL, NULL, NULL, NULL, NULL, NULL }; static TclWinProcs unicodeProcs = { @@ -148,6 +150,8 @@ static TclWinProcs unicodeProcs = { (int (__cdecl*)(CONST TCHAR *, struct _utimbuf *)) _wutime, NULL, NULL, + /* Security SDK - will be filled in on NT,XP,2000,2003 */ + NULL, NULL, NULL, NULL, NULL, NULL }; TclWinProcs *tclWinProcs; @@ -567,6 +571,37 @@ 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; diff --git a/win/tclWinFCmd.c b/win/tclWinFCmd.c index 3f2addb..519df62 100644 --- a/win/tclWinFCmd.c +++ b/win/tclWinFCmd.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: tclWinFCmd.c,v 1.36 2003/06/02 15:58:47 vincentdarley Exp $ + * RCS: @(#) $Id: tclWinFCmd.c,v 1.37 2003/10/13 16:48:07 vincentdarley Exp $ */ #include "tclWinInt.h" @@ -862,12 +862,13 @@ TclpObjCopyDirectory(srcPathPtr, destPathPtr, errorPtr) { Tcl_DString ds; Tcl_DString srcString, dstString; + Tcl_Obj *normSrcPtr, *normDestPtr; int ret; - Tcl_WinUtfToTChar(Tcl_FSGetTranslatedStringPath(NULL,srcPathPtr), - -1, &srcString); - Tcl_WinUtfToTChar(Tcl_FSGetTranslatedStringPath(NULL,destPathPtr), - -1, &dstString); + normSrcPtr = Tcl_FSGetNormalizedPath(NULL,srcPathPtr); + Tcl_WinUtfToTChar(Tcl_GetString(normSrcPtr), -1, &srcString); + normDestPtr = Tcl_FSGetNormalizedPath(NULL,destPathPtr); + Tcl_WinUtfToTChar(Tcl_GetString(normDestPtr), -1, &dstString); ret = TraverseWinTree(TraversalCopy, &srcString, &dstString, &ds); @@ -875,7 +876,13 @@ TclpObjCopyDirectory(srcPathPtr, destPathPtr, errorPtr) Tcl_DStringFree(&dstString); if (ret != TCL_OK) { - *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); + if (!strcmp(Tcl_DStringValue(&ds), Tcl_GetString(normSrcPtr))) { + *errorPtr = srcPathPtr; + } else if (!strcmp(Tcl_DStringValue(&ds), Tcl_GetString(normDestPtr))) { + *errorPtr = destPathPtr; + } else { + *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); + } Tcl_DStringFree(&ds); Tcl_IncrRefCount(*errorPtr); } @@ -918,6 +925,7 @@ TclpObjRemoveDirectory(pathPtr, recursive, errorPtr) Tcl_Obj **errorPtr; { Tcl_DString ds; + Tcl_Obj *normPtr = NULL; int ret; if (recursive) { /* @@ -926,8 +934,8 @@ TclpObjRemoveDirectory(pathPtr, recursive, errorPtr) * optimize this case easily. */ Tcl_DString native; - Tcl_WinUtfToTChar(Tcl_FSGetTranslatedStringPath(NULL, pathPtr), - -1, &native); + normPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr); + Tcl_WinUtfToTChar(Tcl_GetString(normPtr), -1, &native); ret = DoRemoveDirectory(&native, recursive, &ds); Tcl_DStringFree(&native); } else { @@ -937,7 +945,12 @@ TclpObjRemoveDirectory(pathPtr, recursive, errorPtr) if (ret != TCL_OK) { int len = Tcl_DStringLength(&ds); if (len > 0) { - *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); + if (normPtr != NULL + && !strcmp(Tcl_DStringValue(&ds), Tcl_GetString(normPtr))) { + *errorPtr = pathPtr; + } else { + *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); + } Tcl_IncrRefCount(*errorPtr); } Tcl_DStringFree(&ds); diff --git a/win/tclWinFile.c b/win/tclWinFile.c index 8768261..f787669 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.54 2003/09/29 22:38:21 dkf Exp $ + * RCS: @(#) $Id: tclWinFile.c,v 1.55 2003/10/13 16:48:07 vincentdarley Exp $ */ //#define _WIN32_WINNT 0x0500 @@ -1291,9 +1291,10 @@ TclpGetUserHome(name, bufferPtr) */ static int -NativeAccess( - CONST TCHAR *nativePath, /* Path of file to access (UTF-8). */ - int mode) /* Permission setting. */ +NativeAccess(nativePath, mode) + CONST TCHAR *nativePath; /* Path of file to access, native + * encoding. */ + int mode; /* Permission setting. */ { DWORD attr; @@ -1312,26 +1313,151 @@ NativeAccess( /* * File is not writable. */ - Tcl_SetErrno(EACCES); return -1; } 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 */ - - return 0; - } - if (NativeIsExec(nativePath)) { - return 0; + Tcl_SetErrno(EACCES); + return -1; } - 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; + BOOL accessYesNo; + 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(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. + */ + goto accessError; + } + if (!(*tclWinProcs->openThreadTokenProc)(GetCurrentThread (), + TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &hToken)) { + /* + * Unable to get current thread's token. + */ + goto accessError; + } + (*tclWinProcs->revertToSelfProc)(); + memset (&genMap, 0x00, sizeof (GENERIC_MAPPING)); + /* + * Fill GenericMask type according to access priveleges + * we are checking. + */ + genMap.GenericAll = 0; + if (mode & R_OK) { + genMap.GenericRead = FILE_GENERIC_READ; + } + if (mode & W_OK) { + genMap.GenericWrite = FILE_GENERIC_WRITE; + } + if (mode & X_OK) { + genMap.GenericExecute = FILE_GENERIC_EXECUTE; + } + (*tclWinProcs->mapGenericMaskProc)(&desiredAccess, &genMap); + /* + * 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; + } + /* + * Clean up. + */ + HeapFree(GetProcessHeap (), 0, sdPtr); + CloseHandle(hToken); + if (!accessYesNo) { + Tcl_SetErrno(EACCES); + return -1; + } + } return 0; } @@ -1582,9 +1708,13 @@ TclpObjStat(pathPtr, statPtr) transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); if (transPtr == NULL || (strpbrk(Tcl_GetString(transPtr), "?*") != NULL)) { + if (transPtr != NULL) { + Tcl_DecrRefCount(transPtr); + } Tcl_SetErrno(ENOENT); return -1; } + Tcl_DecrRefCount(transPtr); #endif /* diff --git a/win/tclWinInt.h b/win/tclWinInt.h index d953c5b..b2cb74e 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.22 2003/04/18 20:17:45 hobbs Exp $ + * RCS: @(#) $Id: tclWinInt.h,v 1.23 2003/10/13 16:48:07 vincentdarley Exp $ */ #ifndef _TCLWININT @@ -111,6 +111,31 @@ typedef struct TclWinProcs { LPVOID, UINT, LPVOID, DWORD); BOOL (WINAPI *getVolumeNameForVMPProc)(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); } TclWinProcs; EXTERN TclWinProcs *tclWinProcs; |