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.cpp | 9 +++ src/network/socket/qlocalserver_win.cpp | 120 +++++++++++++++++++++++++++++++- src/network/socket/socket.pri | 2 + 3 files changed, 130 insertions(+), 1 deletion(-) (limited to 'src/network/socket') diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp index b379e99f6f..8284ae4c27 100644 --- a/src/network/socket/qlocalserver.cpp +++ b/src/network/socket/qlocalserver.cpp @@ -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. 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)) { diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri index 8f93f0d0c0..39b214ffb9 100644 --- a/src/network/socket/socket.pri +++ b/src/network/socket/socket.pri @@ -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 -- cgit v1.2.3