// =========================================================================
// mmsseclib.h - CLR classes for manipulating NT security objects
// =========================================================================
#pragma once
using namespace System;
using namespace System::Collections;
using namespace System::ComponentModel;
using namespace System::Runtime::InteropServices;
using namespace System::Security::Principal;
using namespace Win32;
#include "enums.h"
#include "mgdhelp.h"
#include <lm.h>
namespace mmsseclib
{
// =========================================================================
// CAccessToken - Internal class to get manage access tokens privileges
// =========================================================================
class CAccessToken
{
private:
HANDLE m_hToken;
public:
CAccessToken() : m_hToken(0)
{
::ImpersonateSelf(SecurityImpersonation);
if (!::OpenThreadToken(::GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &m_hToken))
::OpenThreadToken(::GetCurrentThread(), TOKEN_READ, TRUE, &m_hToken);
}
~CAccessToken()
{
::CloseHandle(m_hToken);
::RevertToSelf();
}
BOOL SetPrivilege(LPCTSTR lpPrivType, BOOL bEnabled)
{
LUID luidPrivilegeLUID;
if (::LookupPrivilegeValue(NULL, lpPrivType, &luidPrivilegeLUID)) {
TOKEN_PRIVILEGES tpTokenPrivilege;
tpTokenPrivilege.PrivilegeCount = 1;
tpTokenPrivilege.Privileges[0].Luid = luidPrivilegeLUID;
tpTokenPrivilege.Privileges[0].Attributes = bEnabled ? SE_PRIVILEGE_ENABLED : 0;
::AdjustTokenPrivileges(m_hToken, FALSE, &tpTokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
if (::GetLastError() == ERROR_SUCCESS)
return TRUE;
}
return FALSE;
}
BOOL CheckPrivilege(LPCTSTR lpPrivType)
{
LUID luidPrivilegeLUID;
BOOL bReturn = FALSE;
if (::LookupPrivilegeValue(NULL, lpPrivType, &luidPrivilegeLUID)) {
PRIVILEGE_SET privSet;
privSet.PrivilegeCount = 1;
privSet.Control = 0;
privSet.Privilege[0].Luid = luidPrivilegeLUID;
privSet.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!::PrivilegeCheck(m_hToken, &privSet, &bReturn))
return FALSE;
}
return bReturn;
}
operator HANDLE() const { return m_hToken; }
};
// =========================================================================
// Helper functions and declarations
// =========================================================================
__declspec(selectany) extern const SID_IDENTIFIER_AUTHORITY
SecurityNullSidAuthority = SECURITY_NULL_SID_AUTHORITY,
SecurityWorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY,
SecurityLocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY,
SecurityCreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY,
SecurityNonUniqueAuthority = SECURITY_NON_UNIQUE_AUTHORITY,
SecurityNTAuthority = SECURITY_NT_AUTHORITY;
SID __nogc * g_CopySid(const SID __nogc * pSid)
{
DWORD len = ::GetLengthSid((PSID)pSid);
Buffer<SID> m_pSid(len);
if (::CopySid(len, m_pSid, (PSID)pSid))
return m_pSid.Detach();
return NULL;
}
SID __nogc * g_GetSidFromName(LPCTSTR pszAccountName, LPCTSTR pszSystem)
{
DWORD dwSidSize = ::GetSidLengthRequired(SID_MAX_SUB_AUTHORITIES);
DWORD dwDomainSize = 128;
DWORD cbSid = dwSidSize, cbDomain = dwDomainSize;
Buffer<SID> pSid(dwSidSize);
Buffer<TCHAR> szDomain(dwDomainSize);
SID_NAME_USE snu;
BOOL bSuccess = ::LookupAccountName(pszSystem, pszAccountName, pSid, &cbSid, szDomain, &cbDomain, &snu);
if (!bSuccess)
{
if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
if (cbSid > dwSidSize)
{
// Failed because sid was too small, reallocate it.
pSid.ReAllocate(cbSid);
}
if (cbDomain > dwDomainSize)
{
// Failed because domain was not big enough, reallocate it.
szDomain.ReAllocate(cbDomain);
}
bSuccess = ::LookupAccountName(pszSystem, pszAccountName, pSid, &cbSid, szDomain, &cbDomain, &snu);
}
}
if (bSuccess)
return pSid.Detach();
return NULL;
};
ACL __nogc * g_CopyAcl(const ACL __nogc * pAcl)
{
if (!pAcl) return NULL;
Buffer<ACL> m_pAcl(pAcl->AclSize);
::CopyMemory((void*)m_pAcl, pAcl, pAcl->AclSize);
return m_pAcl.Detach();
}
/*
String* g_GetDCName(String* domain)
{
String* res = NULL;
BYTE __nogc * sname;
IntPtr pDomain = Marshal::StringToHGlobalUni(domain);
LPWSTR szDomain = (LPWSTR)pDomain.ToPointer();
__try
{
NET_API_STATUS stat = NetGetDCName(NULL, szDomain, &sname);
if (stat == NERR_Success)
res = new String((const __wchar_t *)sname);
else
throw new Win32Exception(stat);
}
__finally
{
NetApiBufferFree(sname);
Marshal::FreeHGlobal(pDomain);
}
return res;
}
*/
// =========================================================================
// WindowsUser - represents a Windows identity (SID)
// =========================================================================
public __gc class WindowsUser : public ICloneable
{
public private:
SID __nogc * m_pSid;
String* m_strDomain;
String* m_strAccountName;
String* m_strSystem;
SID_NAME_USE m_snu;
WindowsUser(const SID __nogc * pSid) : m_pSid(0), m_snu(SidTypeInvalid)
{
Copy(pSid);
}
WindowsUser(const SID_IDENTIFIER_AUTHORITY &sidIdentifierAuthority, long subAuthority1) : m_pSid(0)
{
long sa __gc[] = { subAuthority1 };
Load(sidIdentifierAuthority, sa);
}
WindowsUser(const SID_IDENTIFIER_AUTHORITY &sidIdentifierAuthority, long subAuthority1, long subAuthority2) : m_pSid(0)
{
long sa __gc[] = { subAuthority1, subAuthority2 };
Load(sidIdentifierAuthority, sa );
}
// Copies a SID into the object
bool Copy(const SID __nogc * pSid)
{
m_pSid = g_CopySid(pSid);
return (m_pSid != NULL);
}
// Creates a SID from wellknown account info
void Load(const SID_IDENTIFIER_AUTHORITY &sidIdentifierAuthority, long subAuthorities __gc[])
{
BYTE nSubAuthorityCount = (BYTE)subAuthorities->Count;
Buffer<SID> pSid(::GetSidLengthRequired(nSubAuthorityCount));
if (!::InitializeSid(pSid, const_cast<SID_IDENTIFIER_AUTHORITY *>(&sidIdentifierAuthority), nSubAuthorityCount))
throw new Win32Exception(::GetLastError());
for (UINT i = 0; i < nSubAuthorityCount; i++)
*::GetSidSubAuthority(pSid, i) = subAuthorities[i];
m_pSid = pSid.Detach();
}
// Creates a SID by looking up an account
void Load(String* accountName, String* system)
{
MString pszAccountName(accountName), pszSystem(system);
Buffer<SID> pSid(g_GetSidFromName(pszAccountName, pszSystem));
if ((LPVOID)pSid)
{
if (Copy(pSid))
{
m_strDomain = NULL;
m_strAccountName = NULL;
m_strSystem = (LPCTSTR)pszSystem;
}
}
else
throw new Win32Exception(::GetLastError());
}
// Creates a SID by extracting it from a token
void Load(HANDLE hToken)
{
DWORD dwInfoBufferSize = 0;
::GetTokenInformation(hToken, TokenUser, NULL, 0, &dwInfoBufferSize);
Buffer<TOKEN_USER> pTokenUser(dwInfoBufferSize);
if (::GetTokenInformation(hToken, TokenUser, pTokenUser, dwInfoBufferSize, &dwInfoBufferSize)) {
// Copy the SID out of the token
if (Copy((SID*)pTokenUser->User.Sid))
return;
}
throw new Win32Exception(::GetLastError());
}
#if(_WIN32_WINNT >= 0x0500)
// Creates a SID by converting it from its string representation
void Load(String* sidString)
{
Buffer<SID> pSid;
if (::ConvertStringSidToSid((LPCTSTR)MString(sidString), reinterpret_cast<PSID*>(&pSid)))
{
m_pSid = pSid.Detach();
try
{
GetSidInfo();
}
catch (Exception*)
{
Clear();
throw;
}
}
}
#endif
// Fetches account name, domain and name use for current SID
void GetSidInfo()
{
MString pszSystem(m_strSystem);
DWORD cbName = UNLEN+1, cbDomain = DNLEN+1;
Buffer<TCHAR> szName(cbName);
Buffer<TCHAR> szDomain(cbDomain);
SID_NAME_USE snu;
if (!::LookupAccountSid(pszSystem, m_pSid, szName, &cbName, szDomain, &cbDomain, &snu))
{
switch(::GetLastError())
{
case ERROR_INSUFFICIENT_BUFFER:
szName.ReAllocate(cbName);
szDomain.ReAllocate(cbDomain);
if (!::LookupAccountSid(pszSystem, m_pSid, szName, &cbName, szDomain, &cbDomain, &snu))
throw new Win32Exception(::GetLastError());
break;
case ERROR_NONE_MAPPED:
m_strAccountName = String::Empty;
m_strDomain = String::Empty;
m_snu = SidTypeUnknown;
return;
default:
throw new Win32Exception(::GetLastError());
}
}
m_strAccountName = szName;
m_strDomain = szDomain;
m_snu = snu;
}
// Cleans it out
void Clear()
{
::LocalFree(m_pSid);
m_pSid = NULL;
m_strDomain = NULL;
m_strAccountName = NULL;
m_strSystem = NULL;
m_snu = SidTypeInvalid;
}
// Frees allocated SID
~WindowsUser()
{
::LocalFree(m_pSid);
m_pSid = NULL;
}
public:
// Constructs a user based on StringSid or account name
WindowsUser(String* accountName) : m_pSid(0)
{
#if(_WIN32_WINNT >= 0x0500)
if (accountName->StartsWith(S"S-1-"))
{
try
{
Load(accountName);
return;
}
catch (Win32Exception*)
{
}
}
#endif
Load(accountName, NULL);
}
// Constructs a user based on account name on remote computer
WindowsUser(String* accountName, String* system) : m_pSid(0)
{
Load(accountName, system);
}
// Constructs a user based on information in a token
WindowsUser(IntPtr token) : m_pSid(0)
{
Load(token.ToPointer());
}
// Copies correctly
virtual Object * Clone()
{
return new WindowsUser(m_pSid);
}
// Gets the account name for the user
__property String* get_AccountName()
{
if (!m_pSid)
throw new NotSupportedException(S"Object does not represent a valid user");
if (m_strAccountName == NULL)
GetSidInfo();
return m_strAccountName;
}
// Gets the domain for the user
__property String* get_Domain()
{
if (!m_pSid)
throw new NotSupportedException(S"Object does not represent a valid user");
if (m_strDomain == NULL)
GetSidInfo();
return m_strDomain;
}
// Gets the full name (Domain\Account) for the user
__property String* get_FullName()
{
if (!m_pSid)
throw new NotSupportedException(S"Object does not represent a valid user");
if (m_strAccountName == NULL)
GetSidInfo();
if (m_strAccountName->StartsWith(m_strDomain))
return m_strAccountName;
return String::Concat(m_strDomain, S"\\", m_strAccountName);
}
#if(_WIN32_WINNT >= 0x0500)
// Gets the StringSid for the user
__property String* get_SidString()
{
Buffer<TCHAR> szSid;
if (::ConvertSidToStringSid(m_pSid, &szSid))
return new String(szSid);
return NULL;
}
#endif
// Compares two users based on identity
virtual bool Equals(Object __gc * value)
{
if (value->GetType() != __typeof(WindowsUser))
return false;
return (TRUE == ::EqualSid(m_pSid, dynamic_cast<WindowsUser*>(value)->m_pSid));
}
// Overrides == operator
static bool op_Equality(WindowsUser* user1, WindowsUser* user2)
{
return user1->Equals(user2);
}
// Overrides != operator
static bool op_Inequality(WindowsUser* user1, WindowsUser* user2)
{
return !user1->Equals(user2);
}
virtual int GetHashCode()
{
return ToString()->GetHashCode();
}
virtual String* ToString()
{
#if(_WIN32_WINNT >= 0x0500)
return get_SidString();
#else
return get_FullName();
#endif
}
// Static method to return current user
__property static WindowsUser* get_CurrentUser()
{
HANDLE hAccessToken;
if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_READ, &hAccessToken))
return new WindowsUser(hAccessToken);
throw new Win32Exception(::GetLastError());
}
// Static well-known users on all systems
__gc class WellKnownIdentities
{
public:
static WindowsUser* Null = new WindowsUser(SecurityNullSidAuthority, SECURITY_NULL_RID);
static WindowsUser* World = new WindowsUser(SecurityWorldSidAuthority, SECURITY_WORLD_RID);
static WindowsUser* Local = new WindowsUser(SecurityLocalSidAuthority, SECURITY_LOCAL_RID);
static WindowsUser* CreatorOwner = new WindowsUser(SecurityCreatorSidAuthority, SECURITY_CREATOR_OWNER_RID);
static WindowsUser* CreatorGroup = new WindowsUser(SecurityCreatorSidAuthority, SECURITY_CREATOR_GROUP_RID);
static WindowsUser* CreatorOwnerServer = new WindowsUser(SecurityCreatorSidAuthority, SECURITY_CREATOR_OWNER_SERVER_RID);
static WindowsUser* CreatorGroupServer = new WindowsUser(SecurityCreatorSidAuthority, SECURITY_CREATOR_GROUP_SERVER_RID);
static WindowsUser* Dialup = new WindowsUser(SecurityNTAuthority, SECURITY_DIALUP_RID);
static WindowsUser* Network = new WindowsUser(SecurityNTAuthority, SECURITY_NETWORK_RID);
static WindowsUser* Batch = new WindowsUser(SecurityNTAuthority, SECURITY_BATCH_RID);
static WindowsUser* Interactive = new WindowsUser(SecurityNTAuthority, SECURITY_INTERACTIVE_RID);
static WindowsUser* Service = new WindowsUser(SecurityNTAuthority, SECURITY_SERVICE_RID);
static WindowsUser* AnonymousLogon = new WindowsUser(SecurityNTAuthority, SECURITY_ANONYMOUS_LOGON_RID);
static WindowsUser* Proxy = new WindowsUser(SecurityNTAuthority, SECURITY_PROXY_RID);
static WindowsUser* ServerLogon = new WindowsUser(SecurityNTAuthority, SECURITY_SERVER_LOGON_RID);
static WindowsUser* Self = new WindowsUser(SecurityNTAuthority, SECURITY_PRINCIPAL_SELF_RID);
static WindowsUser* AuthenticatedUser = new WindowsUser(SecurityNTAuthority, SECURITY_AUTHENTICATED_USER_RID);
static WindowsUser* RestrictedCode = new WindowsUser(SecurityNTAuthority, SECURITY_RESTRICTED_CODE_RID);
static WindowsUser* TerminalServer = new WindowsUser(SecurityNTAuthority, SECURITY_TERMINAL_SERVER_RID);
static WindowsUser* System = new WindowsUser(SecurityNTAuthority, SECURITY_LOCAL_SYSTEM_RID);
static WindowsUser* Admins = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
static WindowsUser* Users = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS);
static WindowsUser* Guests = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS);
static WindowsUser* PowerUsers = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS);
static WindowsUser* AccountOps = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ACCOUNT_OPS);
static WindowsUser* SystemOps = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_SYSTEM_OPS);
static WindowsUser* PrintOps = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_PRINT_OPS);
static WindowsUser* BackupOps = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_BACKUP_OPS);
static WindowsUser* Replicator = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REPLICATOR);
static WindowsUser* RasServers = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_RAS_SERVERS);
static WindowsUser* PreW2KAccess = new WindowsUser(SecurityNTAuthority, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_PREW2KCOMPACCESS);
};
};
/*
// =========================================================================
// WindowsGroup
// =========================================================================
public __gc class WindowsGroup : public WindowsUser
{
public:
// Constructs a group based on StringSid or name
WindowsGroup(String* accountName) : WindowsUser(accountName)
{
}
// Constructs a group based on name on remote computer
WindowsGroup(String* accountName, String* system) : WindowsUser(accountName, system)
{
}
// Constructs a group based on information in a token
WindowsGroup(IntPtr token) : WindowsUser(token)
{
}
__property String* get_Comment()
{
return String::Empty;
}
bool HasMember(String* accountName)
{
return HasMember(new WindowsUser(accountName));
}
bool HasMember(WindowsUser* user)
{
return false;
}
__property WindowsUser* get_Members()
{
return NULL;
}
};
*/
// =========================================================================
// AccessList
// =========================================================================
public __gc class AccessEntry
{
public private:
AccessRights m_rights;
AceInheritanceFlags m_inheritance;
AceType m_type;
WindowsUser* m_trustee;
// Constructor from EXPLICIT_ACCESS
AccessEntry(EXPLICIT_ACCESS __nogc * pAce)
{
m_rights = (AccessRights)pAce->grfAccessPermissions;
m_inheritance = (AceInheritanceFlags)pAce->grfInheritance;
m_type = (AceType)pAce->grfAccessMode;
m_trustee = new WindowsUser(reinterpret_cast<SID*>(pAce->Trustee.ptstrName));
}
// Constructor from ACE
AccessEntry(ACCESS_ALLOWED_ACE __nogc * pAce)
{
m_rights = (AccessRights)pAce->Mask;
m_inheritance = (AceInheritanceFlags)pAce->Header.AceFlags;
m_type = (AceType)pAce->Header.AceType;
m_trustee = new WindowsUser(reinterpret_cast<SID*>(&(pAce->SidStart)));
}
// Compares object to an ACE
bool Equals(const ACCESS_ALLOWED_ACE& ace)
{
return (ace.Mask == m_rights &&
ace.Header.AceFlags == m_inheritance &&
ace.Header.AceType == m_type &&
::EqualSid(m_trustee->m_pSid, reinterpret_cast<SID*>(const_cast<DWORD*>(&(ace.SidStart)))));
}
public:
// Constructor
AccessEntry(WindowsUser* trustee, AccessRights rights, AceInheritanceFlags inheritance)
{
m_trustee = trustee;
m_rights = rights;
m_inheritance = inheritance;
m_type = AceType::Undefined;
}
__property AccessRights get_Rights() { return m_rights; }
__property AceInheritanceFlags get_Inheritance() { return m_inheritance; }
__property AceType get_Type() { return m_type; }
__property WindowsUser* get_Trustee() { return m_trustee; }
__property bool get_IsInherited() { return (AceInheritanceFlags::Inherited == (m_inheritance & AceInheritanceFlags::Inherited)); }
};
public __abstract __gc class AccessList;
__delegate void AclChangeHandler(AccessList* acl);
// =========================================================================
// AccessList - abstract class providing base ACL manipulation
// =========================================================================
//[System::Reflection::DefaultMemberAttribute("Perm")]
public __abstract __gc class AccessList : public ICloneable, public ICollection//, public IList
{
public private:
AclChangeHandler* OnAclChange;
ACL __nogc * m_pAcl;
bool isDacl, batch, inhFromParent;
AccessEntry* aces __gc[];
AccessList(ACL __nogc * pAcl, bool bDacl) : m_pAcl(0), batch(false), isDacl(bDacl), inhFromParent(false)
{
Assign(pAcl);
}
void CheckInheritance()
{
int count = get_Count();
for (int i = 0; i < count; i++)
{
ACCESS_ALLOWED_ACE __nogc * pAce = NULL;
if (!::GetAce(m_pAcl, i, (LPVOID*)&pAce))
throw new Win32Exception(::GetLastError());
if (AceInheritanceFlags::Inherited == (pAce->Header.AceFlags & AceInheritanceFlags::Inherited))
{
inhFromParent = true;
return;
}
}
inhFromParent = false;
}
void Assign(ACL __nogc * pAcl)
{
if (m_pAcl)
::LocalFree(m_pAcl);
m_pAcl = g_CopyAcl(pAcl);
CheckInheritance();
}
virtual ~AccessList()
{
::LocalFree(m_pAcl);
m_pAcl = NULL;
}
void Commit()
{
if (!batch && OnAclChange != NULL)
OnAclChange(this);
}
virtual void /*IList::*/RemoveAt(int index)
{
if (m_pAcl == NULL || index < 0 || index >= get_Count())
throw new ArgumentOutOfRangeException(S"index");
::DeleteAce(m_pAcl, index);
}
public:
void BeginUpdate()
{
batch = true;
}
void EndUpdate()
{
batch = false;
Commit();
}
void RevokeAccess(WindowsUser* trustee)
{
// The following commented lines of code should work but seems to a bug
// so I do it manually below.
/*
EXPLICIT_ACCESS ea;
ea.grfAccessMode = REVOKE_ACCESS;
ea.grfAccessPermissions = 0;
ea.grfInheritance = VALID_INHERIT_FLAGS;
::BuildTrusteeWithSid(&ea.Trustee, trustee->m_pSid);
Buffer<ACL> pAcl;
DWORD dwRes = ::SetEntriesInAcl(1, &ea, m_pAcl, &pAcl);
if (dwRes != ERROR_SUCCESS)
throw new Win32Exception(dwRes);
Assign(pAcl.Detach());
*/
int aceCount = get_Count();
for (int i = aceCount-1; i >= 0; i--)
{
ACCESS_ALLOWED_ACE __nogc * pAce = NULL;
if (!::GetAce(m_pAcl, i, (LPVOID*)&pAce))
throw new Win32Exception(::GetLastError());
if (::EqualSid(trustee->m_pSid, reinterpret_cast<SID*>(&(pAce->SidStart))))
::DeleteAce(m_pAcl, i);
}
Commit();
}
virtual Object * Clone() = 0;
virtual IEnumerator* GetEnumerator()
{
int aceCount = get_Count();
aces = __gc new AccessEntry*[aceCount];
for (int i = 0; i < aceCount; i++)
{
aces[i] = get_Item(i);
}
return aces->GetEnumerator();
}
virtual void CopyTo(Array* array, int index)
{
int aceCount = get_Count();
if (array == NULL)
throw new ArgumentNullException(S"array");
if ((array->Length-index) < aceCount)
throw new ArgumentException(S"Not enough space in destination array to perform Copy");
if (array->Rank != 1)
throw new ArgumentException(S"Cannot Copy into multidimensional array.");
if (index < 0)
throw new ArgumentOutOfRangeException(S"index");
for (int i = 0; i < aceCount; i++)
{
array->set_Item(i+index, get_Item(i));
}
}
__property virtual int get_Count()
{
if (m_pAcl == NULL) return 0;
ACL_SIZE_INFORMATION asi;
if (!::GetAclInformation(m_pAcl, (LPVOID)&asi, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
throw new Win32Exception(::GetLastError());
return (int)asi.AceCount;
}
__property virtual bool get_IsNull()
{
return (m_pAcl == NULL);
}
__property virtual bool get_IsSynchronized()
{
return false;
}
__property virtual Object* get_SyncRoot()
{
throw new NotImplementedException();
return NULL;
}
virtual bool Contains(AccessEntry* value)
{
int aceCount = get_Count();
for (int i = 0; i < aceCount; i++)
{
ACCESS_ALLOWED_ACE __nogc * pAce = NULL;
if (!::GetAce(m_pAcl, i, (LPVOID*)&pAce))
throw new Win32Exception(::GetLastError());
if (value->Equals(*pAce))
return true;
}
return false;
}
__property virtual bool get_InheritFromParent()
{
return inhFromParent;
}
__property virtual void set_InheritFromParent(bool value)
{
if (inhFromParent != value)
{
inhFromParent = value;
if (!inhFromParent)
{
int count = get_Count();
for (int i = count-1; i >= 0; i--)
{
ACCESS_ALLOWED_ACE __nogc * pAce = NULL;
if (!::GetAce(m_pAcl, i, (LPVOID*)&pAce))
throw new Win32Exception(::GetLastError());
if (AceInheritanceFlags::Inherited == (pAce->Header.AceFlags & AceInheritanceFlags::Inherited))
::DeleteAce(m_pAcl, i);
}
}
else
{
if (m_pAcl == NULL)
set_Empty(true);
}
Commit();
}
}
__property virtual bool get_Empty()
{
return (m_pAcl != NULL && get_Count() == 0);
}
__property virtual void set_Empty(bool value)
{
if (value)
{
if (m_pAcl == NULL)
{
Buffer<ACL> pAcl(sizeof(ACL));
if (!::InitializeAcl(pAcl, sizeof(ACL), ACL_REVISION))
throw new Win32Exception(::GetLastError());
m_pAcl = pAcl.Detach();
}
else
{
int aceCount = get_Count();
for (int i = aceCount-1; i >= 0; i--)
::DeleteAce(m_pAcl, i);
}
}
}
virtual void Clear()
{
::LocalFree(m_pAcl);
m_pAcl = NULL;
inhFromParent = false;
Commit();
}
__property AccessEntry* get_Item(int index)
{
if (m_pAcl == NULL || index < 0 || index >= get_Count())
throw new ArgumentOutOfRangeException(S"index");
ACCESS_ALLOWED_ACE __nogc * pAce = NULL;
if (!::GetAce(m_pAcl, index, (LPVOID*)&pAce))
throw new Win32Exception(::GetLastError());
return new AccessEntry(pAce);
}
/*
virtual int Add(AccessEntry* value)
{
return -1;
}
__property virtual bool get_IsFixedSize()
{
return false;
}
__property virtual bool get_IsReadOnly()
{
return false;
}
__property void set_Perm(int index, AccessEntry* value)
{
}
virtual int IndexOf(AccessEntry* value)
{
return -1;
}
virtual void Remove(AccessEntry* value)
{
}
private:
virtual int IList::Add(Object* value)
{
if (value->GetType() == __typeof(AccessEntry))
return Add(static_cast<AccessEntry*>(value));
return -1;
}
virtual bool IList::Contains(Object* value)
{
if (value->GetType() == __typeof(AccessEntry))
return Contains(static_cast<AccessEntry*>(value));
return false;
}
__property __sealed virtual Object* IList::get_Item(int index)
{
return get_Perm(index);
}
__property __sealed virtual void IList::set_Item(int index, Object* value)
{
throw new NotImplementedException();
}
virtual int IList::IndexOf(Object* value)
{
if (value->GetType() == __typeof(AccessEntry))
return IndexOf(static_cast<AccessEntry*>(value));
return -1;
}
virtual void IList::Insert(int index, Object* value)
{
throw new NotImplementedException();
}
virtual void IList::RemoveAt(int index)
{
throw new NotImplementedException();
}
virtual void IList::Remove(Object* value)
{
if (value->GetType() == __typeof(AccessEntry))
Remove(static_cast<AccessEntry*>(value));
}
*/
};
// =========================================================================
// AuditingList - Provides access to DACL
// =========================================================================
public __gc class PermissionsList : public AccessList
{
public private:
PermissionsList(ACL __nogc * pAcl) : AccessList(pAcl, true)
{
}
public:
virtual Object * Clone()
{
return new PermissionsList(m_pAcl);
}
void GrantAccess(AccessEntry* ace)
{
GrantAccess(ace->Trustee, ace->Rights, ace->Inheritance);
}
void GrantAccess(WindowsUser* trustee, AccessRights rights, AceInheritanceFlags inheritance)
{
EXPLICIT_ACCESS ea;
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessMode = GRANT_ACCESS;
ea.grfAccessPermissions = rights;
ea.grfInheritance = inheritance;
::BuildTrusteeWithSid(&ea.Trustee, trustee->m_pSid);
Buffer<ACL> pAcl;
DWORD dwRes = ::SetEntriesInAcl(1, &ea, m_pAcl, &pAcl);
if (dwRes != ERROR_SUCCESS)
throw new Win32Exception(dwRes);
Assign(pAcl.Detach());
Commit();
}
void SetAccess(AccessEntry* ace)
{
SetAccess(ace->Trustee, ace->Rights, ace->Inheritance);
}
void SetAccess(WindowsUser* trustee, AccessRights rights, AceInheritanceFlags inheritance)
{
EXPLICIT_ACCESS ea;
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessMode = SET_ACCESS;
ea.grfAccessPermissions = rights;
ea.grfInheritance = inheritance;
::BuildTrusteeWithSid(&ea.Trustee, trustee->m_pSid);
Buffer<ACL> pAcl;
DWORD dwRes = ::SetEntriesInAcl(1, &ea, m_pAcl, &pAcl);
if (dwRes != ERROR_SUCCESS)
throw new Win32Exception(dwRes);
Assign(pAcl.Detach());
Commit();
}
void DenyAccess(AccessEntry* ace)
{
DenyAccess(ace->Trustee, ace->Rights, ace->Inheritance);
}
void DenyAccess(WindowsUser* trustee, AccessRights rights, AceInheritanceFlags inheritance)
{
EXPLICIT_ACCESS ea;
ea.grfAccessMode = DENY_ACCESS;
ea.grfAccessPermissions = rights;
ea.grfInheritance = inheritance;
::BuildTrusteeWithSid(&ea.Trustee, trustee->m_pSid);
Buffer<ACL> pAcl;
DWORD dwRes = ::SetEntriesInAcl(1, &ea, m_pAcl, &pAcl);
if (dwRes != ERROR_SUCCESS)
throw new Win32Exception(dwRes);
Assign(pAcl.Detach());
Commit();
}
AccessRights GetEffectiveRights(WindowsUser* user)
{
ACCESS_MASK mask = 0;
TRUSTEE tr;
::BuildTrusteeWithSid(&tr, user->m_pSid);
DWORD dwRes = ::GetEffectiveRightsFromAcl(m_pAcl, &tr, &mask);
if (dwRes != ERROR_SUCCESS)
throw new Win32Exception(dwRes);
return (AccessRights)mask;
}
};
// =========================================================================
// AuditingList - Provides access to SACL
// =========================================================================
public __gc class AuditingList : public AccessList
{
public private:
AuditingList(ACL __nogc * pAcl) : AccessList(pAcl, false)
{
}
public:
virtual Object * Clone()
{
return new AuditingList(m_pAcl);
}
void SetAuditSuccess(AccessEntry* ace)
{
SetAuditSuccess(ace->Trustee, ace->Rights, ace->Inheritance);
}
void SetAuditSuccess(WindowsUser* trustee, AccessRights rights, AceInheritanceFlags inheritance)
{
EXPLICIT_ACCESS ea;
ea.grfAccessMode = SET_AUDIT_SUCCESS;
ea.grfAccessPermissions = rights;
ea.grfInheritance = inheritance;
::BuildTrusteeWithSid(&ea.Trustee, trustee->m_pSid);
Buffer<ACL> pAcl;
DWORD dwRes = ::SetEntriesInAcl(1, &ea, m_pAcl, &pAcl);
if (dwRes != ERROR_SUCCESS)
throw new Win32Exception(dwRes);
Assign(pAcl.Detach());
Commit();
}
void SetAuditFailure(AccessEntry* ace)
{
SetAuditFailure(ace->Trustee, ace->Rights, ace->Inheritance);
}
void SetAuditFailure(WindowsUser* trustee, AccessRights rights, AceInheritanceFlags inheritance)
{
EXPLICIT_ACCESS ea;
ea.grfAccessMode = SET_AUDIT_FAILURE;
ea.grfAccessPermissions = rights;
ea.grfInheritance = inheritance;
::BuildTrusteeWithSid(&ea.Trustee, trustee->m_pSid);
Buffer<ACL> pAcl;
DWORD dwRes = ::SetEntriesInAcl(1, &ea, m_pAcl, &pAcl);
if (dwRes != ERROR_SUCCESS)
throw new Win32Exception(dwRes);
Assign(pAcl.Detach());
Commit();
}
void GetAuditedRights(WindowsUser* user, AccessRights* successfulRights, AccessRights* failedRights)
{
ACCESS_MASK smask = 0, fmask = 0;
TRUSTEE tr;
::BuildTrusteeWithSid(&tr, user->m_pSid);
DWORD dwRes = ::GetAuditedPermissionsFromAcl(m_pAcl, &tr, &smask, &fmask);
if (dwRes != ERROR_SUCCESS)
throw new Win32Exception(dwRes);
*successfulRights = (AccessRights)smask;
*failedRights = (AccessRights)fmask;
}
};
// =========================================================================
// SecuredObject - Represents security on an NT object
// =========================================================================
public __gc class SecuredObject : public ICloneable
{
public private:
MString* objName;
IntPtr objHandle;
SecuredObjectType objType;
PermissionsList* perms;
AuditingList* audit;
~SecuredObject()
{
if (objName != NULL)
delete objName;
}
SECURITY_DESCRIPTOR __nogc * GetInfo(SECURITY_INFORMATION secInfo, SID __nogc ** pOwner, SID __nogc ** pGroup, ACL __nogc ** pDacl, ACL __nogc ** pSacl)
{
Buffer<SECURITY_DESCRIPTOR> pSD;
DWORD ret = ERROR_SUCCESS;
SE_OBJECT_TYPE seot = (SE_OBJECT_TYPE)objType;
if (objHandle == IntPtr::Zero)
ret = ::GetNamedSecurityInfo(*objName,
seot, secInfo, (PSID*)pOwner, (PSID*)pGroup, (PACL*)pDacl, (PACL*)pSacl,
reinterpret_cast<PSECURITY_DESCRIPTOR *>(&pSD));
else
ret = ::GetSecurityInfo(objHandle.ToPointer(),
seot, secInfo, (PSID*)pOwner, (PSID*)pGroup, (PACL*)pDacl, (PACL*)pSacl,
reinterpret_cast<PSECURITY_DESCRIPTOR *>(&pSD));
if (ret != ERROR_SUCCESS)
throw new Win32Exception(ret);
return pSD.Detach();
}
void SetInfo(SECURITY_INFORMATION secInfo, SID __nogc * pOwner, SID __nogc * pGroup, ACL __nogc * pDacl, ACL __nogc * pSacl)
{
DWORD ret = ERROR_SUCCESS;
SE_OBJECT_TYPE seot = (SE_OBJECT_TYPE)objType;
if (objHandle == IntPtr::Zero)
ret = ::SetNamedSecurityInfo(*objName, seot, secInfo, pOwner, pGroup, pDacl, pSacl);
else
ret = ::SetSecurityInfo(objHandle.ToPointer(), seot, secInfo, pOwner, pGroup, pDacl, pSacl);
if (ret != ERROR_SUCCESS)
throw new Win32Exception(ret);
}
public:
SecuredObject(String* ObjectName, SecuredObjectType ObjectType)
{
objName = new MString(ObjectName);
objType = ObjectType;
}
SecuredObject(IntPtr hObject, SecuredObjectType ObjectType)
{
objHandle = hObject;
objType = ObjectType;
}
virtual Object * Clone()
{
SecuredObject* so;
if (objHandle == IntPtr::Zero)
so = new SecuredObject(objName, objType);
else
so = new SecuredObject(objHandle, objType);
return so;
}
#if(_WIN32_WINNT >= 0x0500)
virtual String* ToString()
{
CAccessToken tok;
if (!tok.CheckPrivilege(SE_SECURITY_NAME))
tok.SetPrivilege(SE_SECURITY_NAME, TRUE);
SECURITY_INFORMATION secInfo = OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION|SACL_SECURITY_INFORMATION;
Buffer<SECURITY_DESCRIPTOR> pSD(GetInfo(secInfo, NULL, NULL, NULL, NULL));
Buffer<TCHAR> pszSD;
if (!::ConvertSecurityDescriptorToStringSecurityDescriptor(pSD, SDDL_REVISION_1, secInfo, &pszSD, NULL))
throw new Win32Exception(::GetLastError());
return pszSD;
}
#endif
public:
__property WindowsUser* get_Owner()
{
SID __nogc * pSid;
Buffer<SECURITY_DESCRIPTOR> pSD(GetInfo(OWNER_SECURITY_INFORMATION, &pSid, NULL, NULL, NULL));
return new WindowsUser(pSid);
}
__property void set_Owner(WindowsUser* value)
{
CAccessToken tok;
if (!tok.CheckPrivilege(SE_TAKE_OWNERSHIP_NAME))
tok.SetPrivilege(SE_TAKE_OWNERSHIP_NAME, TRUE);
SetInfo(OWNER_SECURITY_INFORMATION, value->m_pSid, NULL, NULL, NULL);
}
__property WindowsUser* get_Group()
{
SID __nogc * pSid;
Buffer<SECURITY_DESCRIPTOR> pSD(GetInfo(GROUP_SECURITY_INFORMATION, NULL, &pSid, NULL, NULL));
return new WindowsUser(pSid);
}
__property void set_Group(WindowsUser* value)
{
SetInfo(GROUP_SECURITY_INFORMATION, NULL, value->m_pSid, NULL, NULL);
}
__property PermissionsList* get_Permissions()
{
if (perms == NULL)
{
ACL __nogc * pAcl;
Buffer<SECURITY_DESCRIPTOR> pSD(GetInfo(DACL_SECURITY_INFORMATION, NULL, NULL, &pAcl, NULL));
perms = new PermissionsList(pAcl);
perms->OnAclChange = new AclChangeHandler(this,&SecuredObject::DaclChanged);
}
return perms;
}
__property AuditingList* get_Auditing()
{
if (audit == NULL)
{
CAccessToken tok;
if (!tok.CheckPrivilege(SE_SECURITY_NAME))
tok.SetPrivilege(SE_SECURITY_NAME, TRUE);
ACL __nogc * pAcl;
Buffer<SECURITY_DESCRIPTOR> pSD(GetInfo(SACL_SECURITY_INFORMATION, NULL, NULL, NULL, &pAcl));
audit = new AuditingList(pAcl);
audit->OnAclChange = new AclChangeHandler(this,&SecuredObject::SaclChanged);
}
return audit;
}
private:
void DaclChanged(AccessList* acl)
{
SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;
if (perms->InheritFromParent)
secInfo |= UNPROTECTED_DACL_SECURITY_INFORMATION;
SetInfo(secInfo, NULL, NULL, acl->m_pAcl, NULL);
ACL __nogc * pAcl;
Buffer<SECURITY_DESCRIPTOR> pSD(GetInfo(DACL_SECURITY_INFORMATION, NULL, NULL, &pAcl, NULL));
perms->Assign(pAcl);
}
void SaclChanged(AccessList* acl)
{
CAccessToken tok;
if (!tok.CheckPrivilege(SE_SECURITY_NAME))
tok.SetPrivilege(SE_SECURITY_NAME, TRUE);
SECURITY_INFORMATION secInfo = SACL_SECURITY_INFORMATION;
if (perms->InheritFromParent)
secInfo |= UNPROTECTED_SACL_SECURITY_INFORMATION;
SetInfo(secInfo, NULL, NULL, NULL, acl->m_pAcl);
ACL __nogc * pAcl;
Buffer<SECURITY_DESCRIPTOR> pSD(GetInfo(SACL_SECURITY_INFORMATION, NULL, NULL, NULL, &pAcl));
audit->Assign(pAcl);
}
};
}