// Windows/SecurityUtils.cpp

#include "StdAfx.h"

#include "SecurityUtils.h"

namespace NWindows {
namespace NSecurity {

/*
bool MyLookupAccountSid(LPCTSTR systemName, PSID sid,
  CSysString &accountName, CSysString &domainName, PSID_NAME_USE sidNameUse)
{
  DWORD accountNameSize = 0, domainNameSize = 0;

  if (!::LookupAccountSid(systemName, sid,
      accountName.GetBuffer(0), &accountNameSize,
      domainName.GetBuffer(0), &domainNameSize, sidNameUse))
  {
    if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
      return false;
  }
  bool result = BOOLToBool(::LookupAccountSid(systemName, sid,
      accountName.GetBuffer(accountNameSize), &accountNameSize,
      domainName.GetBuffer(domainNameSize), &domainNameSize, sidNameUse));
  accountName.ReleaseBuffer();
  domainName.ReleaseBuffer();
  return result;
}
*/

static void SetLsaString(LPWSTR src, PLSA_UNICODE_STRING dest)
{
  int len = (int)wcslen(src);
  dest->Length = (USHORT)(len * sizeof(WCHAR));
  dest->MaximumLength = (USHORT)((len + 1) * sizeof(WCHAR));
  dest->Buffer = src;
}

/*
static void MyLookupSids(CPolicy &policy, PSID ps)
{
  LSA_REFERENCED_DOMAIN_LIST *referencedDomains = NULL;
  LSA_TRANSLATED_NAME *names = NULL;
  NTSTATUS nts = policy.LookupSids(1, &ps, &referencedDomains, &names);
  int res = LsaNtStatusToWinError(nts);
  LsaFreeMemory(referencedDomains);
  LsaFreeMemory(names);
}
*/

#ifndef _UNICODE
typedef BOOL (WINAPI * LookupAccountNameWP)(
    LPCWSTR lpSystemName,
    LPCWSTR lpAccountName,
    PSID Sid,
    LPDWORD cbSid,
    LPWSTR ReferencedDomainName,
    LPDWORD cchReferencedDomainName,
    PSID_NAME_USE peUse
    );
#endif

static PSID GetSid(LPWSTR accountName)
{
  #ifndef _UNICODE
  HMODULE hModule = GetModuleHandle(TEXT("Advapi32.dll"));
  if (hModule == NULL)
    return NULL;
  LookupAccountNameWP lookupAccountNameW = (LookupAccountNameWP)GetProcAddress(hModule, "LookupAccountNameW");
  if (lookupAccountNameW == NULL)
    return NULL;
  #endif

  DWORD sidLen = 0, domainLen = 0;
  SID_NAME_USE sidNameUse;
  if (!
    #ifdef _UNICODE
    ::LookupAccountNameW
    #else
    lookupAccountNameW
    #endif
    (NULL, accountName, NULL, &sidLen, NULL, &domainLen, &sidNameUse))
  {
    if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
      PSID pSid = ::HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sidLen);
      LPWSTR domainName = (LPWSTR)::HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (domainLen + 1) * sizeof(WCHAR));
      BOOL res =
        #ifdef _UNICODE
        ::LookupAccountNameW
        #else
        lookupAccountNameW
        #endif
        (NULL, accountName, pSid, &sidLen, domainName, &domainLen, &sidNameUse);
      ::HeapFree(GetProcessHeap(), 0, domainName);
      if (res)
        return pSid;
    }
  }
  return NULL;
}

#define MY__SE_LOCK_MEMORY_NAME L"SeLockMemoryPrivilege"

bool AddLockMemoryPrivilege()
{
  CPolicy policy;
  LSA_OBJECT_ATTRIBUTES attr;
  attr.Length = sizeof(attr);
  attr.RootDirectory = NULL;
  attr.ObjectName  = NULL;
  attr.Attributes = 0;
  attr.SecurityDescriptor = NULL;
  attr.SecurityQualityOfService  = NULL;
  if (policy.Open(NULL, &attr,
      // GENERIC_WRITE)
      POLICY_ALL_ACCESS)
      // STANDARD_RIGHTS_REQUIRED,
      // GENERIC_READ | GENERIC_EXECUTE | POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES)
      != 0)
    return false;
  LSA_UNICODE_STRING userRights;
  wchar_t s[128] = MY__SE_LOCK_MEMORY_NAME;
  SetLsaString(s, &userRights);
  WCHAR userName[256 + 2];
  DWORD size = 256;
  if (!GetUserNameW(userName, &size))
    return false;
  PSID psid = GetSid(userName);
  if (psid == NULL)
    return false;
  bool res = false;

  /*
  PLSA_UNICODE_STRING userRightsArray;
  ULONG countOfRights;
  NTSTATUS status = policy.EnumerateAccountRights(psid, &userRightsArray, &countOfRights);
  if (status != 0)
    return false;
  bool finded = false;
  for (ULONG i = 0; i < countOfRights; i++)
  {
    LSA_UNICODE_STRING &ur = userRightsArray[i];
    if (ur.Length != s.Length() * sizeof(WCHAR))
      continue;
    if (wcsncmp(ur.Buffer, s, s.Length()) != 0)
      continue;
    finded = true;
    res = true;
    break;
  }
  if (!finded)
  */
  {
    /*
    LSA_ENUMERATION_INFORMATION *enums;
    ULONG countReturned;
    NTSTATUS status = policy.EnumerateAccountsWithUserRight(&userRights, &enums, &countReturned);
    if (status == 0)
    {
      for (ULONG i = 0; i < countReturned; i++)
        MyLookupSids(policy, enums[i].Sid);
      if (enums)
        ::LsaFreeMemory(enums);
      res = true;
    }
    */
    NTSTATUS status = policy.AddAccountRights(psid, &userRights);
    if (status == 0)
      res = true;
    // ULONG res = LsaNtStatusToWinError(status);
  }
  HeapFree(GetProcessHeap(), 0, psid);
  return res;
}

}}

