/** In order to compile this program you must have installed the boost library.
* We need the regular expression class from it.
*
**/
#include "UserFun2000.h"
const size_t MAX_USERNAME_LENGTH = 256;
int _tmain(void)
{
#ifdef _DEBUG
::_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF |
_CRTDBG_ALLOC_MEM_DF | ::_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)) ;
#endif /* _DEBUG. */
::SetLastError(ERROR_SUCCESS);
WellKnownSid2Trustee();
std::wcout << std::endl;
Sid2UserNamePrepare();
std::wcout << _T("\r\n");
std::wcout << bool(IsAdminRunning()) << _T("\r\n");
DoWhoAmI();
{
SaferRaiiWrapper SaferObj(SAFER_LEVELID_UNTRUSTED);
SaferObj.CreateProcessAsUser(_T("\"C:\\Windows\\Notepad.exe\""));
}
try {
std::SecureClass::SetPrivilege(_T("SeSecurityPrivilege"), TRUE);
} catch(std::runtime_error &) {/* non critical error */
}
GetSecurityDescs(_T("G:\\WUTemp\\Test Folder\\Tester.txt"));
try {
std::SecureClass::SetPrivilege(_T("SeSecurityPrivilege"), FALSE);
} catch(std::runtime_error &) {/* non critical error */
}
try
{
std::wcout << _T("\r\nCreating class");
std::SecureClass secObj1(std::SecureClass::ReadLen | std::SecureClass::CopyClass, ::GetCurrentThread());
std::wcout << _T("\r\nlen is: ") << secObj1.get_len();
std::wcout << _T("\r\nCopying class");
std::SecureClass secObj2 = secObj1;
std::wcout << _T("\r\nAttempting to write class");
secObj2.set_len(5.) ; /* This should throw. */
} catch(std::runtime_error &e) {
std::wcout << _T("\r\n") << e.what() << std::endl;
}
return 0;
}
int WellKnownSid2Trustee(void)
{
const TCHAR SystemSid[] = _T("SY");
PSID SidUser = NULL;
if(! ::ConvertStringSidToSid(SystemSid, &SidUser))
{
throw std::logic_error("ConvertStringSidToSid Failed");
}
std::wcout << SystemSid;
{
TRUSTEE TrusteeSid = {0};
::BuildTrusteeWithSid(&TrusteeSid, SidUser);
}
::LocalFree(SidUser); SidUser = NULL;
return 0;
}
void Sid2UserName(IN const PSID &SidUser, OUT std::basic_string<TCHAR> &UserName)
{/** Ex. 2. Throws std::logic_error on failure. Don't pass in Well known Sids **/
if(!IsValidSid(SidUser))
{/* valid inputs? */
throw std::logic_error("The Sid is bad.");
}
/* We assume that CoInitialize has been called */
::SetLastError(ERROR_SUCCESS);
try
{
/* First we need to initialize and connect to WMI. */
WMI::ISWbemLocatorPtr Server = NULL;
_bstr_t QueryString = _T("Select * From Win32_UserAccount Where SID = \"");
Server.CreateInstance(__uuidof(WMI::SWbemLocator));
WMI::ISWbemServicesPtr objWMI = Server->ConnectServer(_bstr_t(""), _bstr_t("root\\cimv2"), _bstr_t(), _bstr_t(), _bstr_t(), _bstr_t(), 0, NULL);
/* Before continuing, we must finish building the WQL query */
{
LPTSTR TextualSid = NULL;
::ConvertSidToStringSid(SidUser, &TextualSid);
QueryString += TextualSid;
LocalFree(TextualSid) ; TextualSid = NULL;
}
QueryString += _T("\"");
{/* The query is now ready, Execute it! */
WMI::ISWbemObjectSetPtr QueryResult = objWMI->ExecQuery(QueryString, _bstr_t("WQL"), 0, NULL);
/* We should now have 1 result returned (as a collection). However, this collection still needs enumerating */
if(QueryResult->Count > 0)
{
WMI::IEnumVARIANTPtr en = QueryResult->_NewEnum;
_variant_t UserInstance;
ULONG cnt = 0;
en->Next(1, &UserInstance, &cnt);
/* We now have the Win32_UserAccount we needed to query. Query its "Name". */
QueryString = static_cast<WMI::ISWbemObjectPtr>(UserInstance)->Properties_->Item("Name", 0)->GetValue();
/* This property is a BSTR that contains the answer! */
UserName = static_cast<const TCHAR *>(QueryString);
/* We've done it! now let's clean up and get out of here! */
}
}
} catch (const _com_error &ex) {
/* translate _com_error to logic_error. */
boost::sized_array<TCHAR> ErrorText(66) ;
::_stscanf(ErrorText.get(), _T("%65x"), ex.Error());
::CoUninitialize();
throw std::logic_error("Wmi Error occurred");
}
}
int Sid2UserNamePrepare(void)
{
HANDLE hToken = NULL;
DWORD TokenInformationLength = sizeof(TOKEN_USER), ReturnLength = 0;
boost::sized_array<BYTE> TokenBuffer(sizeof(TOKEN_USER)) ;
std::basic_string<TCHAR> UserName;
/* Setup the current thread token. */
if(!::OpenThreadToken(::GetCurrentThread(), TOKEN_READ, TRUE, &hToken))
{
if(!::OpenProcessToken(::GetCurrentProcess(), TOKEN_READ, &hToken))
{
throw std::logic_error("Could not open access token");
}
}
::SetLastError(ERROR_SUCCESS);
::GetTokenInformation(hToken, TokenUser, TokenBuffer.get(), TokenInformationLength, &ReturnLength);
if(::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{/* Reallocation is necessary */
TokenInformationLength = ReturnLength;
TokenBuffer.reset(new BYTE[ReturnLength]);
if(!::GetTokenInformation(hToken, TokenUser, TokenBuffer.get(), TokenInformationLength, &ReturnLength))
{
::CloseHandle(hToken); hToken = NULL;
throw std::logic_error("Could not retrieve user from token");
}
}
/* Now that we have the SID, convert it to a user name */
TOKEN_USER *TokenInformation = reinterpret_cast<TOKEN_USER *>(TokenBuffer.get());
::CoInitialize(NULL);
Sid2UserName(TokenInformation->User.Sid, UserName);
::CoUninitialize();
std::wcout << UserName.c_str();
::CloseHandle(hToken); hToken = NULL;
return 0;
}
bool IsAdminRunning(void)
{
BOOL IsMember = FALSE;
PSID SidUser = NULL;
/** BUGBUG: this only checks if the user is in the Local Admins group.
* Then again, this entire function is a bug (design flaw).
**/
if(::ConvertStringSidToSid(_T("BA"), &SidUser) != TRUE)
{
throw std::logic_error("Could not create admin SID");
}
if(::CheckTokenMembership(NULL, SidUser, &IsMember) != TRUE)
{
throw std::logic_error("Could not determine if user is an admin");
}
::LocalFree(SidUser); SidUser = NULL;
return static_cast<bool>(IsMember == TRUE);
}
void PrintAceString(const std::basic_string<TCHAR> &AceSddlSD)
{/**
* ACE strings need to be handled separately. To get them:
* gets all ACE strings.
* For each match, group 1 gets the ACE type, group 2 gets the inheritace,
* group 3 gets the access mask, group 4 gets the object guid, group 5 gets the inherited guid
*
* ACE strings need to be printed in UserName (allow|deny|inherit)
**/
boost::wregex RegSearch(_T("\\((.*?);(.*?);(.*?);(.*?);(.*?);(.*?)\\)"));
boost::wsmatch MatchResults;
if(!AceSddlSD.empty() && boost::regex_search(AceSddlSD, MatchResults, RegSearch, boost::format_perl))
{
/* The user is in token 6. */
std::wcout << static_cast<std::basic_string<TCHAR> >(MatchResults[6]).c_str() << _T(": ");
/* Is it inherited? (ID?) */
if(static_cast<std::basic_string<TCHAR> >(MatchResults[2]).find(_T("ID")) <
static_cast<std::basic_string<TCHAR> >(MatchResults[2]).size())
std::wcout << _T("inherited ");
switch(static_cast<std::basic_string<TCHAR> >(MatchResults[1]).at(0))
{/* Get the ACE Type. */
case _T('A'):
if(static_cast<std::basic_string<TCHAR> >(MatchResults[1]).size() > 1)
std::wcout << _T("audit/alarm, ");
else
std::wcout << _T("allow, ");
break;
case _T('D'):
std::wcout << _T("deny, ");
break;
default:
/* print out the string by default. */
std::wcout << static_cast<std::basic_string<TCHAR> >(MatchResults[1]);
break;
}
/* print out the access mask untranslated */
std::wcout << static_cast<std::basic_string<TCHAR> >(MatchResults[3]).c_str() << _T("\r\n");
}
}
int TranslateSddl(const std::basic_string<TCHAR> &SddlSD)
{
/** Although SDDL is already in a readable form, Our task requires us to make it even more
* readable, using this form:
* UserName (Allow|deny|inherit). This will require some regular expressions.
**/
/* Initialize the regex objects */
boost::wregex RegSearch(_T("O:(.*?)[DGS]:"));
boost::wsmatch MatchResults;
/* Get the owner token */
if(boost::regex_search(SddlSD, MatchResults, RegSearch, boost::format_perl | boost::match_partial))
{
std::wcout << _T("Owner=") << static_cast<std::basic_string<TCHAR> >(MatchResults[1]).c_str() << _T("\r\n");
}
/* Get the Group token */
RegSearch.assign(_T("G:(.*?)[DOS]:"));
if(boost::regex_search(SddlSD, MatchResults, RegSearch, boost::format_perl | boost::match_partial))
{
std::wcout << _T("Group=") << static_cast<std::basic_string<TCHAR> >(MatchResults[1]).c_str() << _T("\r\n");
}
std::basic_string<TCHAR>::size_type start, end;
std::basic_string<TCHAR> ACEStrings = SddlSD;
/* The ACE strings need special handling */
RegSearch.assign(_T("D:(.*?)[GSO]:"));
if(boost::regex_search(SddlSD, MatchResults, RegSearch, boost::format_perl | boost::match_partial))
{/* loop through each ACE string. */
ACEStrings = static_cast<std::basic_string<TCHAR> >(MatchResults[1]);
while(!ACEStrings.empty())
{
start = ACEStrings.find_first_of(_T("("), 0);
end = ACEStrings.find_first_of(_T(")"), start) + 1;
/* Get pointers to the brackets */
PrintAceString(ACEStrings.substr(start, end - start));
ACEStrings.erase(0, end);
}
}
/* SACL is just an ACL with an S */
RegSearch.assign(_T("S:(.*?)[DOG]:"));
if(boost::regex_search(SddlSD, MatchResults, RegSearch, boost::format_perl | boost::match_partial))
{/* loop through each ACE string. */
ACEStrings = static_cast<std::basic_string<TCHAR> >(MatchResults[1]);
while(!ACEStrings.empty())
{
start = ACEStrings.find_first_of(_T("("), 0);
end = ACEStrings.find_first_of(_T(")"), start) + 1;
/* Get pointers to the brackets */
PrintAceString(ACEStrings.substr(start, end - start));
ACEStrings.erase(0, end);
}
}
return 0;
}
int DoAccessCheck(PSECURITY_DESCRIPTOR OutSecDesc, DWORD DesiredAccess)
{
/* This program performs an access check on the security descriptor. */
HANDLE ProcToken = NULL, ImpersonationToken = NULL;
/* We need an impersonation token, an access token, a generic mapping, and a desired access mask. */
BOOL AccessStatus = FALSE;
DWORD GrantedAccess = 0, PrivilegeSetLength = 0;
GENERIC_MAPPING GenericMapping =
{
READ_CONTROL | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA,
FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_WRITE_DATA | FILE_APPEND_DATA,
READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_EXECUTE,
FILE_ALL_ACCESS
} ;
if(!::OpenThreadToken(::GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, TRUE, &ProcToken))
{
if(!::OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &ProcToken))
{
return 1;
}
}
if(!::DuplicateToken(ProcToken, SecurityImpersonation, &ImpersonationToken))
{/* Make an impersonation token */
::CloseHandle(ProcToken); ProcToken = NULL;
::CloseHandle(ImpersonationToken); ImpersonationToken = NULL;
}
::MapGenericMask(&DesiredAccess, &GenericMapping);
/* map generic accesses to specific accesses. */
/* What size do we need our PRIVILEGE_SET? */
::AccessCheck(OutSecDesc, ImpersonationToken, DesiredAccess, &GenericMapping, NULL, &PrivilegeSetLength,
&GrantedAccess, &AccessStatus);
boost::scoped_array<PRIVILEGE_SET> PrivilegeSet (reinterpret_cast<PRIVILEGE_SET *>
(new BYTE[(PrivilegeSetLength + 1)]));
/* OK, we're ready: AccessCheck()! */
::AccessCheck(OutSecDesc, ImpersonationToken, DesiredAccess, &GenericMapping, PrivilegeSet.get(), &PrivilegeSetLength,
&GrantedAccess, &AccessStatus);
if(AccessStatus == TRUE)
{/* Results, results. */
_tprintf(_T("%x"), GrantedAccess==DesiredAccess);
}
::CloseHandle(ProcToken); ProcToken = NULL;
::CloseHandle(ImpersonationToken); ImpersonationToken = NULL;
return 0;
}
const std::basic_string<TCHAR> DoPrintSecurityDescriptor(PSECURITY_DESCRIPTOR ppSD, SECURITY_INFORMATION psi)
{
std::basic_string<TCHAR> Resultstr;
LPTSTR SddlSD = NULL;
/* ConvertStringSecurityDescriptorToSecurityDescriptor will allocate a buffer for us. */
::ConvertSecurityDescriptorToStringSecurityDescriptor(ppSD, SDDL_REVISION_1, psi, &SddlSD, NULL);
TranslateSddl(SddlSD);
Resultstr = SddlSD;
::LocalFree(SddlSD); SddlSD = NULL;
return Resultstr;
}
DWORD ApplyNewSecurityDescriptor(const std::basic_string<TCHAR> &FNameStr, const std::basic_string<TCHAR> &SddlForm)
{
/* We have an SDDL. Create a security descriptor from it. */
PSECURITY_DESCRIPTOR ppSD = NULL;
SECURITY_INFORMATION psi = 0;
if(!::ConvertStringSecurityDescriptorToSecurityDescriptor(SddlForm.c_str(), SDDL_REVISION_1, &ppSD, NULL))
{/* Wham! Instant security descriptor! See how easy that was? */
throw std::logic_error("Security descriptor conversion failure");
}
if(::GetFileAttributes(FNameStr.c_str()) != INVALID_FILE_ATTRIBUTES)
{/* Split up the security descriptor. */
PACL pDacl = NULL, pSacl = NULL;
PSID pOwner = NULL, pGroup = NULL;
DWORD SDRevision = 0;
SECURITY_DESCRIPTOR_CONTROL pControl = 0;
BOOL bPresent = FALSE, bDefaulted = FALSE;
if(::GetSecurityDescriptorControl(ppSD, &pControl, &SDRevision))
{/* translate the Control flags */
if(pControl & SE_DACL_PROTECTED)
psi &= PROTECTED_DACL_SECURITY_INFORMATION;
if(pControl & SE_SACL_PROTECTED)
psi &= PROTECTED_SACL_SECURITY_INFORMATION;
if(pControl & SE_DACL_AUTO_INHERIT_REQ)
psi &= UNPROTECTED_DACL_SECURITY_INFORMATION;
if(pControl & SE_SACL_AUTO_INHERIT_REQ)
psi &= UNPROTECTED_SACL_SECURITY_INFORMATION;
}
if(::GetSecurityDescriptorDacl(ppSD, &bPresent, &pDacl, &bDefaulted))
{/* Dacl present. By default, inherit. */
psi &= DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
}
if(::GetSecurityDescriptorSacl(ppSD, &bPresent, &pSacl, &bDefaulted))
{/* Sacl present */
psi &= SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION;
}
if(::GetSecurityDescriptorOwner(ppSD, &pOwner, &bDefaulted))
{/* Owner present */
psi &= OWNER_SECURITY_INFORMATION;
}
if(::GetSecurityDescriptorGroup(ppSD, &pGroup, &bDefaulted))
{/* Owner present */
psi &= GROUP_SECURITY_INFORMATION;
}
::SetNamedSecurityInfo(const_cast<LPTSTR>(FNameStr.c_str()), SE_FILE_OBJECT, psi, pOwner, pGroup, pDacl, pSacl);
}
else
{
SECURITY_ATTRIBUTES sAttribs = {sizeof(SECURITY_ATTRIBUTES), ppSD, FALSE};
::CreateFile(FNameStr.c_str(), GENERIC_ALL, FILE_SHARE_READ, &sAttribs, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
}
::LocalFree(ppSD); ppSD = NULL;
return 0;
}
DWORD GetSecurityDescs(const std::basic_string<TCHAR> &FNameStr)
{
/* Get all possible security information for the object: 0xf000000f */
SECURITY_INFORMATION psi = GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION |
SACL_SECURITY_INFORMATION | PROTECTED_SACL_SECURITY_INFORMATION |
PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION |
UNPROTECTED_DACL_SECURITY_INFORMATION;
PSECURITY_DESCRIPTOR ppSecurityDescriptor = NULL;
DWORD dwErr = ::GetNamedSecurityInfo(const_cast<LPTSTR>(FNameStr.c_str()),
SE_FILE_OBJECT, psi, NULL, NULL, NULL, NULL, &ppSecurityDescriptor);
{
PACL AbsDAcl = NULL, AbsSAcl = NULL ;
PSID AbsOwner = NULL, AbsGroup = NULL ;
DWORD dwSize = 0, dwSizes[5] = {0} ;
PSECURITY_DESCRIPTOR ppSDRel = ppSecurityDescriptor ;/* Transfer ppSD into this variable. */
::MakeAbsoluteSD(ppSDRel, NULL, &dwSizes[0], NULL, &dwSizes[4], NULL, &dwSizes[3],
NULL, &dwSizes[2], NULL, &dwSizes[1]) ;
for(size_t i = 0 ; i < 5 ; i++) dwSize += dwSizes[i] ;
ppSecurityDescriptor = reinterpret_cast<PSECURITY_DESCRIPTOR>
( ::LocalAlloc(LPTR, 2 * sizeof(ACL) + 2 * sizeof(SID) + dwSize) ) ;
if(ppSDRel == NULL) throw std::logic_error("Could not allocate memory") ;
/* HACKHACK: Allocate a 2D array with one LocalAlloc */
/* Now set the pointers to the appropriate offsets. */
dwSize = dwSizes[0] ;
AbsOwner = reinterpret_cast<PSID>(reinterpret_cast<BYTE *>(ppSecurityDescriptor) + dwSize) ;
dwSize += sizeof(SID) + dwSizes[1] ;
AbsGroup = reinterpret_cast<PSID>(reinterpret_cast<BYTE *>(ppSecurityDescriptor) + dwSize) ;
dwSize += sizeof(SID) + dwSizes[2] ;
AbsSAcl = reinterpret_cast<PACL>(reinterpret_cast<BYTE *>(ppSecurityDescriptor) + dwSize) ;
dwSize += sizeof(ACL) + dwSizes[3] ;
AbsDAcl = reinterpret_cast<PACL>(reinterpret_cast<BYTE *>(ppSecurityDescriptor) + dwSize) ;
::SetLastError(ERROR_SUCCESS) ;
::MakeAbsoluteSD(ppSDRel, ppSecurityDescriptor, &dwSizes[0], AbsDAcl, &dwSizes[4], AbsSAcl,
&dwSizes[3], AbsOwner, &dwSizes[2], AbsGroup, &dwSizes[1]) ;
/* This security descriptor is now absolute */
::LocalFree(ppSDRel) ; ppSDRel = NULL ;
}
/* Print out the contents of the security descriptor. */
DoPrintSecurityDescriptor(ppSecurityDescriptor, psi);
DoAccessCheck(ppSecurityDescriptor, FILE_GENERIC_WRITE);
/* Build a new security descriptor in SDDL */
std::basic_string<TCHAR> SddlForm = _T("O:BAG:BAD:AI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;GRGX;;;") +
std::SecureClass::GetCurrentSid() + _T(")");
ApplyNewSecurityDescriptor(FNameStr, SddlForm);
/* Cleanup. */
::LocalFree(ppSecurityDescriptor); ppSecurityDescriptor = NULL;
return dwErr;
}