Skip to content

Instantly share code, notes, and snippets.

@asmichi
Last active December 29, 2023 08:13
Show Gist options
  • Save asmichi/daa11b2bf86855f6dde8b6228ca7a8b8 to your computer and use it in GitHub Desktop.
Save asmichi/daa11b2bf86855f6dde8b6228ca7a8b8 to your computer and use it in GitHub Desktop.
Create a security descriptor with an explicit ACL using Win32 APIs
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <AclAPI.h>
#include <psapi.h>
#include <sddl.h>
#include <cassert>
#include <cstdio>
#include <memory>
// Additional References:
// - https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-initializeacl
// - https://learn.microsoft.com/en-us/windows/win32/secauthz/taking-object-ownership-in-c-- (trailing hypens are parts of the URI)
// - https://learn.microsoft.com/en-us/windows/win32/secauthz/creating-a-security-descriptor-for-a-new-object-in-c--
// Creates a file named "new_file_with_explicit_security" with the security descriptor of "O:<CurrentUserSID>D:(A;;FA;;;<CurrentUserSID>)".
//
// NOTE: This code leaks on error. Employ RAII for actual code, for example.
int wmain(int argc, wchar_t* argv[])
{
DWORD error{};
DWORD dw{};
//
// Obtain the user of the current process token.
//
DWORD tokenUserLength;
if (!GetTokenInformation(GetCurrentProcessToken(), TokenUser, nullptr, 0, &tokenUserLength) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
std::printf("error: GetTokenInformation failed: %d\n", GetLastError());
return 1;
}
auto tokenUserBuf = std::make_unique<std::byte[]>(tokenUserLength);
if (!GetTokenInformation(GetCurrentProcessToken(), TokenUser, tokenUserBuf.get(), tokenUserLength, &dw))
{
std::printf("error: GetTokenInformation failed: %d\n", GetLastError());
return 1;
}
const TOKEN_USER* pTokenUser = reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
// There are currently no attributes defined for user security identifiers (SIDs).
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_user
assert(pTokenUser->User.Attributes == 0);
//
// Print the user
//
LPWSTR sidString{};
if (!ConvertSidToStringSidW(pTokenUser->User.Sid, &sidString))
{
std::printf("error: ConvertSidToStringSidW failed: %d\n", GetLastError());
return 1;
}
std::printf("User of the current process token: %ls\n", sidString);
LocalFree(sidString);
sidString = nullptr;
//
// Create an ACL that has one ACCESS_ALLOWED_ACE granting full control to the current user (and not others).
//
// https://learn.microsoft.com/en-us/windows/win32/api/aclapi/nf-aclapi-setentriesinaclw
// https://learn.microsoft.com/en-us/windows/win32/api/accctrl/ns-accctrl-explicit_access_w
// https://learn.microsoft.com/en-us/windows/win32/api/accctrl/ns-accctrl-trustee_w
//
PACL pAcl{};
EXPLICIT_ACCESSW ea{};
ea.grfAccessPermissions = FILE_ALL_ACCESS;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
ea.Trustee.ptstrName = reinterpret_cast<LPWCH>(pTokenUser->User.Sid);
if ((error = SetEntriesInAclW(1, &ea, nullptr, &pAcl) != ERROR_SUCCESS))
{
std::printf("error: SetEntriesInAclW failed: %d\n", error);
return 1;
}
//
// Create the security descriptor
//
SECURITY_DESCRIPTOR sd{};
static_assert(sizeof(sd) >= SECURITY_DESCRIPTOR_MIN_LENGTH);
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
{
std::printf("error: InitializeSecurityDescriptor failed: %d\n", GetLastError());
return 1;
}
if (!SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE))
{
std::printf("error: SetSecurityDescriptorDacl failed: %d\n", GetLastError());
return 1;
}
if (!SetSecurityDescriptorOwner(&sd, pTokenUser->User.Sid, FALSE))
{
std::printf("error: SetSecurityDescriptorOwner failed: %d\n", GetLastError());
return 1;
}
// NOTE: We may also want SetSecurityDescriptorControl
//
// Print the SDDL representation of the security descriptor
//
LPWSTR sdString{};
if (!ConvertSecurityDescriptorToStringSecurityDescriptorW(
&sd,
SDDL_REVISION_1,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION,
&sdString,
nullptr))
{
std::printf("error: ConvertSecurityDescriptorToStringSecurityDescriptorW failed: %d\n", GetLastError());
return 1;
}
std::printf("Created security descriptor: %ls\n", sdString);
LocalFree(sdString);
sdString = nullptr;
//
// Create a file named "new_file_with_explicit_security" with the security descriptor
//
SECURITY_ATTRIBUTES sa{};
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = &sd;
HANDLE hFile = CreateFileW(
L"new_file_with_explicit_security",
GENERIC_READ | GENERIC_WRITE,
0,
&sa,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr);
if (hFile == INVALID_HANDLE_VALUE)
{
std::printf("error: CreateFileW failed: %d\n", GetLastError());
return 1;
}
CloseHandle(hFile);
LocalFree(pAcl);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment