#pragma once
namespace std {
extern GENERIC_MAPPING g_GenericMapping ;
class SecureClass
{
private:
PSECURITY_DESCRIPTOR ppSD;
double len;
HANDLE ProcToken;
public:
enum Rights {
ReadLen = 1,
WriteLen = 2,
SetClassSecurity = 4,
CopyClass = 8
};
SecureClass(int FullRights, HANDLE ThreadHandle) : ppSD(NULL), len(0.), ProcToken(NULL)
{
{/* Get an impersonation token from the thread handle */
HANDLE ImpersonationToken = NULL;
if(::OpenThreadToken(ThreadHandle, TOKEN_QUERY | TOKEN_DUPLICATE, TRUE, &ProcToken) != TRUE)
{
if(::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &ProcToken) != TRUE)
{
throw std::runtime_error("Could not open access token");
}
}
if(!::DuplicateToken(ProcToken, SecurityImpersonation, &ImpersonationToken))
{
throw std::runtime_error("Could not duplicate access token");
}
::CloseHandle(this->ProcToken); this->ProcToken = NULL;
this->ProcToken = ImpersonationToken; ImpersonationToken = NULL;
}
if(::CreatePrivateObjectSecurity(NULL, /* This security descriptor has no parent */
NULL, /* The defualt constructor must start from scratch */
&this->ppSD,
FALSE, /* Containers aren't supported in this class */
ProcToken,
&g_GenericMapping) != TRUE)
{
throw std::runtime_error("Could not create private security descriptor");
}
{
PSECURITY_DESCRIPTOR pNewSD = NULL;
std::basic_stringstream<TCHAR> osTmp;
osTmp << _T("D:(A;;") << std::hex << _T("0x") << FullRights << std::dec << _T(";;;") <<
GetCurrentSid() << _T(")(A;;GA;;;SY)") << std::ends;
::ConvertStringSecurityDescriptorToSecurityDescriptor(osTmp.str().c_str(), SDDL_REVISION_1, &pNewSD, NULL);
::SetPrivateObjectSecurity(DACL_SECURITY_INFORMATION, pNewSD, &this->ppSD, &g_GenericMapping, &ProcToken);
::LocalFree(pNewSD); pNewSD = NULL;
}
};
SecureClass(const SecureClass &OldClass)
: ppSD(NULL), len(OldClass.len), ProcToken(OldClass.ProcToken)
{/* This is a special copy constructor. */
if(!OldClass.CheckClassAccess(CopyClass))
{
throw std::runtime_error("Access denied");
}
if(::DuplicateHandle(::GetCurrentProcess(), OldClass.ProcToken, ::GetCurrentProcess(),
&this->ProcToken, 0, FALSE, DUPLICATE_SAME_ACCESS) != TRUE)
{
throw std::runtime_error("Could not create thread handle");
}
if(::CreatePrivateObjectSecurity(NULL, OldClass.ppSD, &this->ppSD, FALSE,
this->ProcToken, &g_GenericMapping) != TRUE)
{
throw std::runtime_error("Could not copy private security descriptor");
}
};
double get_len(void) const
{
if(CheckClassAccess(ReadLen))
{
return len;
}
else
{
throw std::runtime_error("Access denied");
}
};
bool CheckClassAccess(DWORD RightsToCheck) const
{
DWORD GrantedAccess = 0;
BOOL AccessStatus = FALSE, fGenerateOnClose = FALSE;
try {
::MapGenericMask(&RightsToCheck, &g_GenericMapping);
::ImpersonateLoggedOnUser(ProcToken);
try {
SetPrivilege(SE_AUDIT_NAME, true);
} catch (std::logic_error &) {
/* non critical error */
}
if(!::AccessCheckAndAuditAlarm(_T("Custom Class"), this->ProcToken, _T("SecureClass"),
_T("SecObj1"), ppSD, RightsToCheck, &g_GenericMapping, FALSE, &GrantedAccess,
&AccessStatus, &fGenerateOnClose))
{/* revert to the older AccessCheck */
DWORD PrivilegeLength = 0;
::AccessCheck(this->ppSD, this->ProcToken, RightsToCheck, &g_GenericMapping, NULL,
&PrivilegeLength, &GrantedAccess, &AccessStatus);
boost::sized_array<BYTE> PrivilegeSet (PrivilegeLength);
if(!::AccessCheck(this->ppSD, this->ProcToken, RightsToCheck, &g_GenericMapping,
reinterpret_cast<PRIVILEGE_SET *> (PrivilegeSet.get()), &PrivilegeLength,
&GrantedAccess, &AccessStatus))
{
throw std::runtime_error("Could not read access rights for object");
}
}
else
{
::ObjectCloseAuditAlarm(_T("Custom Class"), this->ProcToken, fGenerateOnClose);
}
try {
SetPrivilege(SE_AUDIT_NAME, false);
} catch (std::logic_error &) {
/* non critical error */
}
::RevertToSelf();
} catch(...) {
try {
SetPrivilege(SE_AUDIT_NAME, false);
} catch (std::logic_error &) {
/* non critical error */
}
::RevertToSelf();
throw;
}
if(AccessStatus != TRUE) return false;
return GrantedAccess==RightsToCheck;
};
void set_len(double Newlen)
{
if(CheckClassAccess(WriteLen))
{
this->len = Newlen;
}
else throw std::runtime_error("Access denied");
};
void set_SecDesc(SECURITY_INFORMATION psi, PSECURITY_DESCRIPTOR pNewSD)
{
if(CheckClassAccess(WRITE_DAC))
{
::SetPrivateObjectSecurity(psi, pNewSD, &this->ppSD, &g_GenericMapping, this->ProcToken);
}
else throw std::runtime_error("Access denied");
}
~SecureClass()
{
::DestroyPrivateObjectSecurity(&ppSD); ppSD = NULL;
::CloseHandle(ProcToken); ProcToken = NULL;
};
static const std::basic_string<TCHAR> GetCurrentSid(void)
{/* Retrieves the SID of the current user, in text form. */
DWORD dwSize = 0, cbReferencedDomainName = 0;
SID_NAME_USE peUse = SidTypeUser;
/* So who IS this user? */
::GetUserName(NULL, &dwSize);
boost::sized_array<TCHAR> UserName(dwSize);
if(!::GetUserName(UserName.get(), &dwSize))
{
throw std::logic_error("Could not retrieve current username");
}
/* Now we know who we are, but what is our SID? */
dwSize = 0;
::LookupAccountName(NULL, UserName.get(), NULL, &dwSize, NULL, &cbReferencedDomainName, &peUse);
boost::sized_array<BYTE> SidUser (dwSize);
boost::sized_array<TCHAR> ReferencedDomainName(cbReferencedDomainName);
if(::LookupAccountName(NULL, UserName.get(), reinterpret_cast<PSID>(SidUser.get()), &dwSize,
ReferencedDomainName.get(), &cbReferencedDomainName, &peUse) != TRUE)
{
throw std::logic_error("Could not retrieve SID of current user");
}
/* With the SID retrieved, get it's text. */
std::basic_string<TCHAR> Result;
{
LPTSTR StringSid = NULL;
if(!::ConvertSidToStringSid(SidUser.get(), &StringSid))
{
throw std::logic_error("Could not obtain textual form of SID");
}
Result = StringSid;
::LocalFree(StringSid); StringSid = NULL;
}
return Result;
}
static void SetPrivilege(const std::basic_string<TCHAR> &lpszPrivilege = SE_SECURITY_NAME, bool bEnablePrivilege = FALSE)
{
/* throws std::logic_error on failure */
HANDLE ProcToken = NULL;
try
{
TOKEN_PRIVILEGES tp = {0};
LUID luid = {0};
if( !::OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, TRUE, &ProcToken) &&
!::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &ProcToken))
{
throw std::logic_error("Error occurred opening process token") ;
}
if(!::LookupPrivilegeValue(NULL, lpszPrivilege.c_str(), &luid))
{
throw std::logic_error("Error looking up privilege in token");
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : 0;
/* Enable the privilege or disable all privileges. */
::SetLastError(ERROR_SUCCESS);
if(!::AdjustTokenPrivileges(ProcToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL) ||
::GetLastError() != ERROR_SUCCESS)
{
throw std::logic_error("Could not enable privilege");
}
if(ProcToken != NULL)
::CloseHandle(ProcToken);
ProcToken = NULL;
} catch (const std::logic_error &) {
if(ProcToken != NULL)
::CloseHandle(ProcToken);
ProcToken = NULL;
throw;
}
}
};
GENERIC_MAPPING g_GenericMapping = {
SecureClass::ReadLen,
SecureClass::WriteLen | SecureClass::CopyClass,
SecureClass::SetClassSecurity,
SecureClass::ReadLen | SecureClass::WriteLen | SecureClass::CopyClass |
SecureClass::SetClassSecurity
};
} /* namespace std */