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 <Friedemann.Kleint@qt.io> Done-with: Edward Welbourne <edward.welbourne@qt.io> Task-number: QTBUG-30148 Change-Id: I7a3468ac069bf782ca312078e3a84107b6cd468c Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
This commit is contained in:
parent
83fa66b6d2
commit
ffc8409aa5
64
src/corelib/io/qfilesystemengine_win.cpp
Normal file → Executable file
64
src/corelib/io/qfilesystemengine_win.cpp
Normal file → Executable file
@ -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;
|
||||
|
25
tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
Normal file → Executable file
25
tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
Normal file → Executable file
@ -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<int> 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
|
||||
|
Loading…
Reference in New Issue
Block a user