c++winapivisual-c++siddacl

Win32 API: Creating file public for current user but private for everyone else


I am testing the following code in C using Win32 API, which is intended to create a new file that is accessible for the current user but private (not accessible) for everyone else.

For this this a deny all permissions for everyone SID, then for current's user SID I set up the permissions.

The file is created successfully and the permissions are apparently set up successfully (see screenshots below), however when I try to open the file with notepad, it says "access is denied" (My file explorer is running under the same session), also if I open a command prompt and do "type file_created.txt" the same "access is denied" appear.

I can of course, restore manually the permissions since I am administrator, but the idea is to make it work programmatically.

Image with everyone permissions: enter image description here

Image with current user permissions: enter image description here

The code:

#include <windows.h>
#include <AccCtrl.h>
#include <aclapi.h>

#include <stdio.h>
#include <stdexcept>
#include <string>

#undef UNICODE

int GetCurrentUserSid(PSID* pSID)
{
  const int MAX_NAME = 256;
  DWORD i, dwSize = 0;
  HANDLE hToken;
  PTOKEN_USER user;
  TOKEN_INFORMATION_CLASS TokenClass = TokenUser;

  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ | TOKEN_QUERY, &hToken))
    return GetLastError();
  else
    wprintf(L"OpenProcessToken() - got the handle to the access token!\n");

  if (!GetTokenInformation(hToken, TokenClass, NULL, 0, &dwSize))
  {
    DWORD dwResult = GetLastError();
    if (dwResult != ERROR_INSUFFICIENT_BUFFER)
    {
      wprintf(L"GetTokenInformation() failed, error %u\n", dwResult);
      return FALSE;
    }
    else
      wprintf(L"GetTokenInformation() - have an ample buffer...\n");
  }
  else
    wprintf(L"GetTokenInformation() - buffer for Token group is OK\n");

  user = (PTOKEN_USER)LocalAlloc(GPTR, dwSize);
  if (!GetTokenInformation(hToken, TokenClass, user, dwSize, &dwSize))
  {
    wprintf(L"GetTokenInformation() failed, error %u\n", GetLastError());
    return FALSE;
  }
  else
    wprintf(L"GetTokenInformation() for getting the TokenGroups is OK\n");

  DWORD dw_sid_len = GetLengthSid(user->User.Sid);
  *pSID = (SID*)LocalAlloc(GPTR, dw_sid_len);
  CopySid(dw_sid_len, *pSID, user->User.Sid);
  return 0;
}

DWORD set_file_security(LPSTR filename)
{
  PACL pNewDACL = NULL;
  PSID current_user = NULL;
  DWORD sid_size = SECURITY_MAX_SID_SIZE;
  SID everyone_sid;
  DWORD dwRes;
  if (CreateWellKnownSid(WinWorldSid, NULL, &everyone_sid, &sid_size) ==
    FALSE) {
    throw std::runtime_error("CreateWellKnownSid() failed: " +
      std::to_string(GetLastError()));
  }

  GetCurrentUserSid(&current_user);

  EXPLICIT_ACCESSA ea[2];
  ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESSA));

  ea[0].grfAccessPermissions = ACCESS_SYSTEM_SECURITY | READ_CONTROL | WRITE_DAC | GENERIC_ALL;
  ea[0].grfAccessMode = GRANT_ACCESS;
  ea[0].grfInheritance = NO_INHERITANCE;
  ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  ea[0].Trustee.ptstrName = reinterpret_cast<char*>(current_user);

  ea[1].grfAccessPermissions = ACCESS_SYSTEM_SECURITY | READ_CONTROL | WRITE_DAC | GENERIC_ALL;
  ea[1].grfAccessMode = DENY_ACCESS;
  ea[1].grfInheritance = NO_INHERITANCE;
  ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  ea[1].Trustee.ptstrName = reinterpret_cast<char*>(&everyone_sid);

  dwRes = SetEntriesInAclA(2, ea, NULL, &pNewDACL);
  if (ERROR_SUCCESS != dwRes) {
    printf("SetEntriesInAcl Error %u\n", dwRes);
    //TODO: goto Cleanup;
  }

  PSECURITY_DESCRIPTOR pSD = NULL;

  // Initialize a security descriptor.  
  pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,
    SECURITY_DESCRIPTOR_MIN_LENGTH);
  if (NULL == pSD)
  {
    _tprintf(_T("LocalAlloc Error %u\n"), GetLastError());
    goto Cleanup;
  }

  if (!InitializeSecurityDescriptor(pSD,
    SECURITY_DESCRIPTOR_REVISION))
  {
    _tprintf(_T("InitializeSecurityDescriptor Error %u\n"),
      GetLastError());
    goto Cleanup;
  }

  // Add the ACL to the security descriptor. 
  if (!SetSecurityDescriptorDacl(pSD,
    TRUE,     // bDaclPresent flag   
    pNewDACL,
    FALSE))   // not a default DACL
  {
    _tprintf(_T("SetSecurityDescriptorDacl Error %u\n"),
      GetLastError());
    goto Cleanup;
  }
  SECURITY_ATTRIBUTES sa;
  // Initialize a security attributes structure.
  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  sa.lpSecurityDescriptor = pSD;
  sa.bInheritHandle = FALSE;

  HANDLE hFile = CreateFileA(filename, GENERIC_ALL, 0, &sa, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);  
  CloseHandle(hFile);

  //dwRes = SetNamedSecurityInfoA(filename, SE_FILE_OBJECT,
  //  DACL_SECURITY_INFORMATION, NULL, NULL, pNewDACL, NULL);
  //if (ERROR_SUCCESS != dwRes) {
  //  printf("SetNamedSecurityInfo Error %u\n", dwRes);
  //  //goto Cleanup;
  //}

Cleanup:

  if (pNewDACL != NULL)
    LocalFree((HLOCAL)pNewDACL);

  return dwRes;
}

int main()
{
  //return 0;

  // Create Everyone SID.
  DWORD sid_size = SECURITY_MAX_SID_SIZE;
  SID everyone_sid;
  if (CreateWellKnownSid(WinWorldSid, NULL, &everyone_sid, &sid_size) ==
    FALSE) {
    throw std::runtime_error("CreateWellKnownSid() failed: " +
      std::to_string(GetLastError()));
  }

  LPSTR filename = "created_file.txt";  

  set_file_security(filename);

  return 0;
}

NOTE: I realized the code has memory leaks and other issues, I was just quickly hacking to test the idea.


Solution

  • In the Windows OS explicit deny permissions have a precedence over explicit allow permissions. So since "Everyone" group includes your account, access to the file is denied, even if you enabled it for yourself. In fact you don't need deny rule altogether, if access rights are not set in the object ACL for some user, access will be denied by default.