From 39eb95ece12445af944cb9fd17136d7f6c20e4cd Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Wed, 2 May 2012 15:32:09 +0100 Subject: Implement QLocalServer::socketOptions on windows Tested manually using the localfortuneserver example and multiple login sessions. The linux autotest isn't suitable for windows due to pipe permissions not appearing in the filesystem. Task-number: QTBUG-25147 Change-Id: I5ea4db81d1870dc45bd483fa8d0b06afede3b722 Reviewed-by: Joerg Bornemann --- src/network/socket/qlocalserver_win.cpp | 120 +++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) (limited to 'src/network/socket/qlocalserver_win.cpp') diff --git a/src/network/socket/qlocalserver_win.cpp b/src/network/socket/qlocalserver_win.cpp index 07357e5789..00eae32c99 100644 --- a/src/network/socket/qlocalserver_win.cpp +++ b/src/network/socket/qlocalserver_win.cpp @@ -42,9 +42,14 @@ #include "qlocalserver.h" #include "qlocalserver_p.h" #include "qlocalsocket.h" +#include #include +#include +#include +#include + // The buffer size need to be 0 otherwise data could be // lost if the socket that has written data closes the connection // before it is read. Pipewriter is used for write buffering. @@ -62,6 +67,116 @@ bool QLocalServerPrivate::addListener() listeners << Listener(); Listener &listener = listeners.last(); + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = FALSE; //non inheritable handle, same as default + sa.lpSecurityDescriptor = 0; //default security descriptor + + QScopedPointer pSD; + PSID worldSID = 0; + QByteArray aclBuffer; + QByteArray tokenUserBuffer; + QByteArray tokenGroupBuffer; + + // create security descriptor if access options were specified + if ((socketOptions & QLocalServer::WorldAccessOption)) { + pSD.reset(new SECURITY_DESCRIPTOR); + if (!InitializeSecurityDescriptor(pSD.data(), SECURITY_DESCRIPTOR_REVISION)) { + setError(QLatin1String("QLocalServerPrivate::addListener")); + return false; + } + HANDLE hToken = NULL; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + return false; + DWORD dwBufferSize = 0; + GetTokenInformation(hToken, TokenUser, 0, 0, &dwBufferSize); + tokenUserBuffer.fill(0, dwBufferSize); + PTOKEN_USER pTokenUser = (PTOKEN_USER)tokenUserBuffer.data(); + if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwBufferSize, &dwBufferSize)) { + setError(QLatin1String("QLocalServerPrivate::addListener")); + CloseHandle(hToken); + return false; + } + + dwBufferSize = 0; + GetTokenInformation(hToken, TokenPrimaryGroup, 0, 0, &dwBufferSize); + tokenGroupBuffer.fill(0, dwBufferSize); + PTOKEN_PRIMARY_GROUP pTokenGroup = (PTOKEN_PRIMARY_GROUP)tokenGroupBuffer.data(); + if (!GetTokenInformation(hToken, TokenPrimaryGroup, pTokenGroup, dwBufferSize, &dwBufferSize)) { + setError(QLatin1String("QLocalServerPrivate::addListener")); + CloseHandle(hToken); + return false; + } + CloseHandle(hToken); + +#ifdef QLOCALSERVER_DEBUG + DWORD groupNameSize; + DWORD domainNameSize; + SID_NAME_USE groupNameUse; + LPWSTR groupNameSid; + LookupAccountSid(0, pTokenGroup->PrimaryGroup, 0, &groupNameSize, 0, &domainNameSize, &groupNameUse); + QScopedPointer> groupName(new wchar_t[groupNameSize]); + QScopedPointer> domainName(new wchar_t[domainNameSize]); + if (LookupAccountSid(0, pTokenGroup->PrimaryGroup, groupName.data(), &groupNameSize, domainName.data(), &domainNameSize, &groupNameUse)) { + qDebug() << "primary group" << QString::fromWCharArray(domainName.data()) << "\\" << QString::fromWCharArray(groupName.data()) << "type=" << groupNameUse; + } + if (ConvertSidToStringSid(pTokenGroup->PrimaryGroup, &groupNameSid)) { + qDebug() << "primary group SID" << QString::fromWCharArray(groupNameSid) << "valid" << IsValidSid(pTokenGroup->PrimaryGroup); + LocalFree(groupNameSid); + } +#endif + + SID_IDENTIFIER_AUTHORITY WorldAuth = SECURITY_WORLD_SID_AUTHORITY; + if (!AllocateAndInitializeSid(&WorldAuth, 1, SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, + &worldSID)) { + setError(QLatin1String("QLocalServerPrivate::addListener")); + return false; + } + + //calculate size of ACL buffer + DWORD aclSize = sizeof(ACL) + ((sizeof(ACCESS_ALLOWED_ACE)) * 3); + aclSize += GetLengthSid(pTokenUser->User.Sid) - sizeof(DWORD); + aclSize += GetLengthSid(pTokenGroup->PrimaryGroup) - sizeof(DWORD); + aclSize += GetLengthSid(worldSID) - sizeof(DWORD); + aclSize = (aclSize + (sizeof(DWORD) - 1)) & 0xfffffffc; + + aclBuffer.fill(0, aclSize); + PACL acl = (PACL)aclBuffer.data(); + InitializeAcl(acl, aclSize, ACL_REVISION_DS); + + if (socketOptions & QLocalServer::UserAccessOption) { + if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, pTokenUser->User.Sid)) { + setError(QLatin1String("QLocalServerPrivate::addListener")); + FreeSid(worldSID); + return false; + } + } + if (socketOptions & QLocalServer::GroupAccessOption) { + if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, pTokenGroup->PrimaryGroup)) { + setError(QLatin1String("QLocalServerPrivate::addListener")); + FreeSid(worldSID); + return false; + } + } + if (socketOptions & QLocalServer::OtherAccessOption) { + if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, worldSID)) { + setError(QLatin1String("QLocalServerPrivate::addListener")); + FreeSid(worldSID); + return false; + } + } + SetSecurityDescriptorOwner(pSD.data(), pTokenUser->User.Sid, FALSE); + SetSecurityDescriptorGroup(pSD.data(), pTokenGroup->PrimaryGroup, FALSE); + if (!SetSecurityDescriptorDacl(pSD.data(), TRUE, acl, FALSE)) { + setError(QLatin1String("QLocalServerPrivate::addListener")); + FreeSid(worldSID); + return false; + } + + sa.lpSecurityDescriptor = pSD.data(); + } + listener.handle = CreateNamedPipe( (const wchar_t *)fullServerName.utf16(), // pipe name PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access @@ -72,7 +187,7 @@ bool QLocalServerPrivate::addListener() BUFSIZE, // output buffer size BUFSIZE, // input buffer size 3000, // client time-out - NULL); + &sa); if (listener.handle == INVALID_HANDLE_VALUE) { setError(QLatin1String("QLocalServerPrivate::addListener")); @@ -80,6 +195,9 @@ bool QLocalServerPrivate::addListener() return false; } + if (worldSID) + FreeSid(worldSID); + memset(&listener.overlapped, 0, sizeof(listener.overlapped)); listener.overlapped.hEvent = eventHandle; if (!ConnectNamedPipe(listener.handle, &listener.overlapped)) { -- cgit v1.2.3