Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / C#

NT Security Classes for .NET

Rate me:
Please Sign up or sign in to vote.
4.78/5 (33 votes)
19 Feb 2004CPOL2 min read 377.1K   4K   75  
A collection of .NET classes written in Managed C++ that faciliate the manipulation of NT security rights
// =========================================================================
// 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);
		}
	};
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer
United States United States
I have been a Windows software developer since 1991. Most of what I create fills the need for some aspect of bigger projects that I consult on.

Comments and Discussions