From ffc8409aa58c04c1dd140001976b55925ac959f6 Mon Sep 17 00:00:00 2001 From: Dyami Caliri Date: Mon, 15 Sep 2014 14:09:22 -0700 Subject: Use AccessCheck for current user effective file permissions On Windows, QFileInfo.isWritable() was returning true in situations where the file would only be writable with elevated privileges. Using AccessCheck instead of GetEffectiveRightsFromAcl to get the correct results. Done-with: Friedemann Kleint Done-with: Edward Welbourne Task-number: QTBUG-30148 Change-Id: I7a3468ac069bf782ca312078e3a84107b6cd468c Reviewed-by: Friedemann Kleint --- src/corelib/io/qfilesystemengine_win.cpp | 64 +++++++++++++++++++---- tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp | 25 ++++++--- 2 files changed, 74 insertions(+), 15 deletions(-) mode change 100644 => 100755 src/corelib/io/qfilesystemengine_win.cpp mode change 100644 => 100755 tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp old mode 100644 new mode 100755 index b01aa55756..c9a56a81cc --- a/src/corelib/io/qfilesystemengine_win.cpp +++ b/src/corelib/io/qfilesystemengine_win.cpp @@ -178,6 +178,7 @@ static TRUSTEE_W currentUserTrusteeW; static TRUSTEE_W worldTrusteeW; static PSID currentUserSID = 0; static PSID worldSID = 0; +static HANDLE currentUserImpersonatedToken = nullptr; /* Deletes the allocated SIDs during global static cleanup @@ -198,6 +199,11 @@ SidCleanup::~SidCleanup() ::FreeSid(worldSID); worldSID = 0; } + + if (currentUserImpersonatedToken) { + ::CloseHandle(currentUserImpersonatedToken); + currentUserImpersonatedToken = nullptr; + } } Q_GLOBAL_STATIC(SidCleanup, initSidCleanup) @@ -254,6 +260,12 @@ static void resolveLibs() ::CloseHandle(token); } + token = nullptr; + if (::OpenProcessToken(hnd, TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | STANDARD_RIGHTS_READ, &token)) { + ::DuplicateToken(token, SecurityImpersonation, ¤tUserImpersonatedToken); + ::CloseHandle(token); + } + typedef BOOL (WINAPI *PtrAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*); PtrAllocateAndInitializeSid ptrAllocateAndInitializeSid = (PtrAllocateAndInitializeSid)GetProcAddress(advapiHnd, "AllocateAndInitializeSid"); if (ptrAllocateAndInitializeSid) { @@ -721,15 +733,49 @@ bool QFileSystemEngine::fillPermissions(const QFileSystemEntry &entry, QFileSyst ACCESS_MASK access_mask; TRUSTEE_W trustee; if (what & QFileSystemMetaData::UserPermissions) { // user - data.knownFlagsMask |= QFileSystemMetaData::UserPermissions; - if(ptrGetEffectiveRightsFromAclW(pDacl, ¤tUserTrusteeW, &access_mask) != ERROR_SUCCESS) - access_mask = (ACCESS_MASK)-1; - if(access_mask & ReadMask) - data.entryFlags |= QFileSystemMetaData::UserReadPermission; - if(access_mask & WriteMask) - data.entryFlags|= QFileSystemMetaData::UserWritePermission; - if(access_mask & ExecMask) - data.entryFlags|= QFileSystemMetaData::UserExecutePermission; + // Using AccessCheck because GetEffectiveRightsFromAcl doesn't account for elevation + if (currentUserImpersonatedToken) { + GENERIC_MAPPING mapping = {FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS}; + PRIVILEGE_SET privileges; + DWORD grantedAccess; + BOOL result; + + data.knownFlagsMask |= QFileSystemMetaData::UserPermissions; + DWORD genericAccessRights = GENERIC_READ; + ::MapGenericMask(&genericAccessRights, &mapping); + + DWORD privilegesLength = sizeof(privileges); + if (::AccessCheck(pSD, currentUserImpersonatedToken, genericAccessRights, + &mapping, &privileges, &privilegesLength, &grantedAccess, &result) && result) { + data.entryFlags |= QFileSystemMetaData::UserReadPermission; + } + + privilegesLength = sizeof(privileges); + genericAccessRights = GENERIC_WRITE; + ::MapGenericMask(&genericAccessRights, &mapping); + if (::AccessCheck(pSD, currentUserImpersonatedToken, genericAccessRights, + &mapping, &privileges, &privilegesLength, &grantedAccess, &result) && result) { + data.entryFlags |= QFileSystemMetaData::UserWritePermission; + } + + privilegesLength = sizeof(privileges); + genericAccessRights = GENERIC_EXECUTE; + ::MapGenericMask(&genericAccessRights, &mapping); + if (::AccessCheck(pSD, currentUserImpersonatedToken, genericAccessRights, + &mapping, &privileges, &privilegesLength, &grantedAccess, &result) && result) { + data.entryFlags |= QFileSystemMetaData::UserExecutePermission; + } + } else { // fallback to GetEffectiveRightsFromAcl + data.knownFlagsMask |= QFileSystemMetaData::UserPermissions; + if (ptrGetEffectiveRightsFromAclW(pDacl, ¤tUserTrusteeW, &access_mask) != ERROR_SUCCESS) + access_mask = ACCESS_MASK(-1); + if (access_mask & ReadMask) + data.entryFlags |= QFileSystemMetaData::UserReadPermission; + if (access_mask & WriteMask) + data.entryFlags|= QFileSystemMetaData::UserWritePermission; + if (access_mask & ExecMask) + data.entryFlags|= QFileSystemMetaData::UserExecutePermission; + } } if (what & QFileSystemMetaData::OwnerPermissions) { // owner data.knownFlagsMask |= QFileSystemMetaData::OwnerPermissions; diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp old mode 100644 new mode 100755 index f35dab2cad..efb261ce7e --- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp +++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp @@ -64,6 +64,14 @@ #define Q_NO_SYMLINKS #endif +#if defined(Q_OS_WIN) +QT_BEGIN_NAMESPACE +extern Q_CORE_EXPORT int qt_ntfs_permission_lookup; +QT_END_NAMESPACE +# ifndef Q_OS_WINRT +bool IsUserAdmin(); +# endif +#endif #if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS) inline bool qt_isEvilFsTypeName(const char *name) @@ -1634,6 +1642,15 @@ void tst_QFileInfo::isWritable() QVERIFY2(fi.exists(), msgDoesNotExist(fi.absoluteFilePath()).constData()); QVERIFY(!fi.isWritable()); #endif + +#if defined (Q_OS_WIN) && !defined(Q_OS_WINRT) + QScopedValueRollback ntfsMode(qt_ntfs_permission_lookup); + qt_ntfs_permission_lookup = 1; + QFileInfo fi2(QFile::decodeName(qgetenv("SystemRoot") + "/system.ini")); + QVERIFY(fi2.exists()); + QCOMPARE(fi2.isWritable(), IsUserAdmin()); +#endif + #if defined (Q_OS_QNX) // On QNX /etc is usually on a read-only filesystem QVERIFY(!QFileInfo("/etc/passwd").isWritable()); #elif defined (Q_OS_UNIX) && !defined(Q_OS_VXWORKS) // VxWorks does not have users/groups @@ -1807,7 +1824,7 @@ void tst_QFileInfo::detachingOperations() } #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) -BOOL IsUserAdmin() +bool IsUserAdmin() { BOOL b; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; @@ -1825,13 +1842,9 @@ BOOL IsUserAdmin() FreeSid(AdministratorsGroup); } - return(b); + return b != FALSE; } -QT_BEGIN_NAMESPACE -extern Q_CORE_EXPORT int qt_ntfs_permission_lookup; -QT_END_NAMESPACE - #endif // Q_OS_WIN && !Q_OS_WINRT #ifndef Q_OS_WINRT -- cgit v1.2.3