I created a named event, named mutex, named semaphore and a named waitable timer. CreateEvent, CreateMutex, CreateSemaphore and CreateWaitableTimer were passed nullptr as the lpEventAttributes, lpMutexAttributes, lpSemaphoreAttributes and lpTimerAttributes parameters, respectively. According to the documentation for these functions they should receive a default security descriptor with ACLs based on the primary or impersonation token of the user.
The default DACL from the user's process token shows that it grants GENERIC_READ and GENERIC_EXECUTE to the logon sid for the session. The user's account and the SYSTEM account get GENERIC_ALL. However, the security descriptor created for each named synchronization object grants the logon sid the same access permissions (Full Control) as the creating user account and the SYSTEM account. These permissions granted to the logon sid exceed what would be expected from the GENERIC_READ and GENERIC_EXECUTE that are contained in the process token's default DACL. Consequently, any other process running in the same session under a different account that receives the logon sid in its token will have full control over the synchronization objects. This is a security consideration that should be addressed.
This can be seen with the following sample code -
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <AclAPI.h>
#include <sddl.h>
#include <stdio.h>
#include <tchar.h>
void PrintDefaultDACL();
void PrintDACL(HANDLE h);
int main()
{
_tprintf(_T("\nPrint Default DACL from process token\n"));
PrintDefaultDACL();
_tprintf(_T("\nPrint DACL for named event\n"));
HANDLE h1 = CreateEvent(nullptr, FALSE, FALSE, _T("EventTest"));
PrintDACL(h1);
_tprintf(_T("\nPrint DACL for named mutex\n"));
HANDLE h2 = CreateMutex(nullptr, FALSE, _T("MutexTest"));
PrintDACL(h2);
_tprintf(_T("\nPrint DACL for named semaphore\n"));
HANDLE h3 = CreateSemaphore(nullptr, 5, 5, _T("SemaphoreTest"));
PrintDACL(h3);
_tprintf(_T("\nPrint DACL for named waitable timer\n"));
HANDLE h4 = CreateWaitableTimer(nullptr, FALSE, _T("WaitableTimerTest"));
PrintDACL(h4);
CloseHandle(h1);
CloseHandle(h2);
CloseHandle(h3);
CloseHandle(h4);
return 0;
}
void PrintDefaultDACL()
{
HANDLE hToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
DWORD dwBytes = 0;
PTOKEN_DEFAULT_DACL pDefaultDacl = nullptr;
if (!GetTokenInformation(hToken, TokenDefaultDacl, pDefaultDacl, 0, &dwBytes) &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
pDefaultDacl = (PTOKEN_DEFAULT_DACL)LocalAlloc(LPTR, dwBytes);
if (GetTokenInformation(hToken, TokenDefaultDacl, pDefaultDacl, dwBytes, &dwBytes))
{
PSECURITY_DESCRIPTOR pSD = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
if (SetSecurityDescriptorDacl(pSD, TRUE, pDefaultDacl->DefaultDacl, FALSE))
{
LPTSTR pszSD = nullptr;
ULONG uLen = 0;
if (ConvertSecurityDescriptorToStringSecurityDescriptor(pSD,
SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &pszSD, &uLen))
{
_tprintf_s(_T("%s\n"), pszSD);
LocalFree(pszSD);
}
}
}
LocalFree(pSD);
}
LocalFree(pDefaultDacl);
}
CloseHandle(hToken);
}
}
void PrintDACL(HANDLE h)
{
PSECURITY_DESCRIPTOR pSD = nullptr;
if (GetSecurityInfo(h, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
nullptr, nullptr, nullptr, nullptr, &pSD) == ERROR_SUCCESS)
{
LPTSTR pszSD = nullptr;
ULONG ulen = 0;
if (ConvertSecurityDescriptorToStringSecurityDescriptor(pSD,
SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &pszSD, &ulen))
{
_tprintf_s(_T("DACL: %s\n"), pszSD);
LocalFree(pszSD);
}
LocalFree(pSD);
}
}

