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 <joerg.bornemann@nokia.com>
This commit is contained in:
Shane Kearns 2012-05-02 15:32:09 +01:00 committed by Qt by Nokia
parent 95c4be798c
commit 39eb95ece1
3 changed files with 130 additions and 1 deletions

View File

@ -95,8 +95,10 @@ QT_BEGIN_NAMESPACE
Access is restricted to the same user as the process that created the socket.
\value GroupAccessOption
Access is restricted to the same group but not the user that created the socket on Linux.
Access is restricted to the primary group of the process on Windows
\value OtherAccessOption
Access is available to everyone but the user and group that created the socket on Linux.
Access is available to everyone on Windows.
\value WorldAccessOption
No access restrictions.
@ -150,6 +152,13 @@ QLocalServer::~QLocalServer()
honor file permissions for Unix domain sockets and by default
have WorldAccess and these permission flags will have no effect.
On Windows, UserAccessOption is sufficient to allow a non
elevated process to connect to a local server created by an
elevated process run by the same user. GroupAccessOption
refers to the primary group of the process (see TokenPrimaryGroup
in the Windows documentation). OtherAccessOption refers to
the well known "Everyone" group.
By default none of the flags are set, access permissions
are the platform default.

View File

@ -42,9 +42,14 @@
#include "qlocalserver.h"
#include "qlocalserver_p.h"
#include "qlocalsocket.h"
#include <QtCore/private/qsystemerror_p.h>
#include <qdebug.h>
#include <Aclapi.h>
#include <AccCtrl.h>
#include <Sddl.h>
// 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<SECURITY_DESCRIPTOR> 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<wchar_t, QScopedPointerArrayDeleter<wchar_t>> groupName(new wchar_t[groupNameSize]);
QScopedPointer<wchar_t, QScopedPointerArrayDeleter<wchar_t>> 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)) {

View File

@ -40,6 +40,8 @@ win32:SOURCES += socket/qnativesocketengine_win.cpp \
socket/qlocalsocket_win.cpp \
socket/qlocalserver_win.cpp
win32:!wince*:LIBS += -lAdvapi32
wince*: {
SOURCES -= socket/qlocalsocket_win.cpp \
socket/qlocalserver_win.cpp