Click here to Skip to main content
15,886,026 members
Articles / Programming Languages / C++

The Windows Access Control Model Part 4

Rate me:
Please Sign up or sign in to vote.
4.86/5 (29 votes)
7 Sep 200543 min read 228.2K   6.8K   100  
The final article in the access control series presents a guide to the access control editor and its associated ISecurityInformation interface.
/**
*	This code is by OShah. all code will have the following licence.
*	Copyright Shexec32. All code bears the following licence:
**/

/**
*	Copyright Shexec32 2004-2005. All rights reserved.
*
*	THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
*	ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
*	TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
*	PARTICULAR PURPOSE.
**/

#include "SecurityCore.h"




STDMETHODIMP_(ULONG)
CObjSecurity::AddRef(void)
{/** IUnknown methods **/
	return ::InterlockedIncrement(&m_cRef);
}



STDMETHODIMP_(ULONG)
CObjSecurity::Release(void)
{/* IUnknown */
	if (::InterlockedDecrement(&m_cRef) == 0)
	{
		delete this;
		return 0;
	}

	return m_cRef;
}



STDMETHODIMP
CObjSecurity::QueryInterface(REFIID riid, void ** ppv)
{/** We support IUnknown, ISecurityInformation, IEffectivePermission, ISecurityObjectTypeInfo,
*	ISecurityInformation2.
**/
	if (::IsEqualIID(riid, IID_IUnknown))
	{
		*ppv = this;
		AddRef();
		return S_OK;
	}
	if(::IsEqualIID(riid, IID_ISecurityInformation))
	{
		*ppv = dynamic_cast<ISecurityInformation *>(this);
		AddRef();
		return S_OK;
	}
	if (::IsEqualIID(riid, IID_IEffectivePermission))
	{
		*ppv = dynamic_cast<IEffectivePermission *>(this);
		AddRef();
		return S_OK;
	}
	if( ::IsEqualIID(riid, IID_ISecurityObjectTypeInfo))
	{
		*ppv = dynamic_cast<ISecurityObjectTypeInfo *>(this);
		AddRef();
		return S_OK;
	}
	/** Our support of ISecurityInformation2 is broken. disable this
	*	if( ::IsEqualIID(riid, IID_ISecurityInformation2))
	*	{
	*		*ppv = dynamic_cast<ISecurityInformation2 *>(this);
	*		AddRef();
	*		return S_OK;
	*	}
	**/

	*ppv = NULL;
	return E_NOINTERFACE;
}



STDMETHODIMP
CObjSecurity::PropertySheetPageCallback(HWND hwnd, UINT uMsg, SI_PAGE_TYPE uPage)
{/* This is our chance to save off the handles to the property sheets. */
#if 0
	if(uPage != SI_PAGE_PERM && uPage != SI_PAGE_ADVPERM)
		/* only act if the basic or 1st advanced sheet is shown */
		return S_OK;


	switch(uMsg)
	{/* Get the Window handle */
		case PSPCB_SI_INITDIALOG:
		{/* Save off the window handle */
			vec_OurhWnd.push_front(hwnd);
		}
		case PSPCB_RELEASE:
		{
			vec_OurhWnd.pop_front();
		}
		default:
			break;
	}
#endif
	UNREFERENCED_PARAMETER(hwnd);
	UNREFERENCED_PARAMETER(uMsg);
	UNREFERENCED_PARAMETER(uPage);
	/**	These window handles are unreliable. Instead, I'll use GetLastActivePopup, instead of maintaining
	*	a list of hWnds
	**/
	return S_OK;
}



STDMETHODIMP
CObjSecurity::GetObjectInformation(PSI_OBJECT_INFO pObjectInfo)
{/** ISecurityInformation methods **/


	/* We are expected to fill out pObjectInfo */
	pObjectInfo->dwFlags = m_dwSIFlags;
	pObjectInfo->hInstance = g_hInst;
	pObjectInfo->pszServerName = this->ServerName.get();
	if(m_pszObjectName.size() == 1)
	{/* We must strip the path from the filename. */
		std::list<sized_array<TCHAR> >::const_iterator Iter = m_pszObjectName.begin();
		std::basic_string<TCHAR> ObjName = Iter->get();
		size_t lastSlash = ObjName.find_last_of(_T("\\/"));	/* Find the last slash */
		if(lastSlash > ObjName.size()) lastSlash = 0;
		if(ObjName.size() < 4) lastSlash = 0;	/* Root folder hack. */
		else lastSlash += 1;	/* We need to set the pointer to Just after the slash */

		pObjectInfo->pszObjectName = Iter->get() + lastSlash;
	}
	else
	{/* use a constant string for multiple objects. */
		pObjectInfo->pszObjectName = L"Multiple Objects";
	}
	return S_OK;
}

STDMETHODIMP
CObjSecurity::GetAccessRights(const GUID* /*pguidObjectType*/, DWORD /*dwFlags*/,
	PSI_ACCESS *ppAccesses, ULONG *pcAccesses, ULONG *piDefaultAccess)
{/* This where we send out the Big Structure at the top of this file. */

	*ppAccesses = const_cast<SI_ACCESS *>(g_siObjAccesses);
	*pcAccesses = gc_siObjAccesses;
	*piDefaultAccess = 2;  /* Set Read access as default. */

	return S_OK;
}



STDMETHODIMP
CObjSecurity::MapGeneric(const GUID* /*pguidObjectType*/, UCHAR * /*pAceFlags*/, ACCESS_MASK *pmask)
{/* This is needed by ISecurityInformation to get GENERIC_*. We need to call MapGenericMask */

	GENERIC_MAPPING ObjMap =
	{
		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
	};
	::MapGenericMask(pmask, &ObjMap);

	return S_OK;
}



STDMETHODIMP
CObjSecurity::GetInheritTypes(PSI_INHERIT_TYPE *ppInheritTypes, ULONG *pcInheritTypes)
{/* This is where we feed in the PSI_INHERIT_TYPEs. (The folder/subfolder/file thingies) */
	*ppInheritTypes = const_cast<SI_INHERIT_TYPE *>(g_InheritTypes);
	*pcInheritTypes = gc_InheritTypes;
	return S_OK;
}



STDMETHODIMP
CObjSecurity::GetSecurity(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR *ppSD, BOOL fDefault)
{/* The first object should be enough to get the security of the object. */
	DWORD dwErr = 0;
	*ppSD = NULL;
	PSECURITY_DESCRIPTOR pSDRes = NULL;
	PrivMgr SaclPriv(SE_SECURITY_NAME, &this->PrivClassImpl);
	if(si & SACL_SECURITY_INFORMATION)
	{/* Turnon the SeSecurityPrivilege */
		SaclPriv.TurnOn();
	}

	if (fDefault)
	{/* Assume that required privileges have already been enabled */
		std::list<sized_array<TCHAR> >::const_iterator Iter = this->m_pszObjectName.begin();
		std::basic_string<TCHAR> ObjName = Iter->get();
		std::basic_string<TCHAR> PrimaryUser = _T("");

		this->GetDefaultSecurity(ObjName, PrimaryUser);

		::SetLastError(ERROR_SUCCESS);
		/* ACLUI will localfree the SD for us. */
		::ConvertStringSecurityDescriptorToSecurityDescriptor(PrimaryUser.c_str(), SDDL_REVISION_1, &pSDRes, 0);
		dwErr = ::GetLastError();

		if(dwErr == ERROR_SUCCESS) *ppSD = pSDRes;
		return HRESULT_FROM_WIN32(dwErr);
	}

	::SetLastError(ERROR_SUCCESS);
	{/* ACLUI will free ppSD for us. */
		std::list<sized_array<TCHAR> >::const_iterator Iter = this->m_pszObjectName.begin();
		dwErr = ::GetNamedSecurityInfo(Iter->get(),
			this->SeObjectType, si, NULL, NULL, NULL, NULL, &pSDRes);
		if(dwErr == ERROR_SUCCESS) *ppSD = pSDRes;
	}

	return HRESULT_FROM_WIN32(dwErr);
}



STDMETHODIMP
CObjSecurity::GetInheritSource(SECURITY_INFORMATION psi, PACL pAcl, PINHERITED_FROM *ppInheritArray)
{/** This method will get the information contained in psi and pAcl to fill out a INHERITED_FROM struct
*	with an array of inheritees.
**/
	DWORD dwErr = 0;
	size_t i = 0, dwSize = 0;
	PINHERITED_FROM InheritTmp = reinterpret_cast<PINHERITED_FROM>
		(::LocalAlloc(LPTR, (1 + pAcl->AceCount) * sizeof(INHERITED_FROM)));

	GENERIC_MAPPING ObjMap =
	{/* This is a copy of ObjMap from this->MapGeneric. */
		FILE_GENERIC_READ,
		FILE_GENERIC_WRITE,
		FILE_GENERIC_EXECUTE,
		FILE_ALL_ACCESS
	};


	{/* Allocate space for the GetInheritanceSource (pAcl will tell us how many entries we need.) */
		BOOL Container = (this->m_dwSIFlags & SI_CONTAINER) ? TRUE : FALSE;
		/* Find out again if this object is a container */
		std::list<sized_array<TCHAR> >::const_iterator Iter = this->m_pszObjectName.begin();
		dwErr = ::GetInheritanceSource(Iter->get(),
			this->SeObjectType, psi, Container, NULL, 0, pAcl, NULL, &ObjMap, InheritTmp);
		if(dwErr != ERROR_SUCCESS)
		{/* Free resources then exit. */
			::LocalFree(InheritTmp); InheritTmp = NULL;
			return HRESULT_FROM_WIN32(dwErr);
		}
		if(InheritTmp->AncestorName == NULL)
		{/* An empty list, return this empty list. */
			*ppInheritArray = InheritTmp;
			return S_OK;
		}
	}

	for(i = 0; i < pAcl->AceCount; i++)
	{/* Get the total size needed to hold all those names. */
		const std::basic_string<TCHAR> &GenName = (InheritTmp[i].AncestorName == NULL) ?
			_T("Parent") : InheritTmp[i].AncestorName;
		dwSize += GenName.size() + 1; /* We want to separate each string when we exit out. */
	}

	PINHERITED_FROM InheritResult = reinterpret_cast<PINHERITED_FROM>
		(::LocalAlloc(LPTR, (1 + pAcl->AceCount) * sizeof(INHERITED_FROM) + dwSize * sizeof(TCHAR)));
	if( InheritResult == NULL)
	{/** We're going to use the setargv method to allocate a 2 dimensional array with one allocation.
	*	The array segment will contain the PINHERITED_FROM array. The Data segment will contain 
	*	the list of names for AncestorName.
	**/
		::FreeInheritedFromArray(InheritTmp, pAcl->AceCount, NULL);
		::LocalFree(InheritTmp); InheritTmp = NULL;
		return E_OUTOFMEMORY;
	}


	{/* Copy this array into a safe location (because WE must free this array, yet it must be passed to ACLUI). */
		TCHAR *DataPtr = reinterpret_cast<TCHAR *>(reinterpret_cast<BYTE *>(InheritResult) +
			pAcl->AceCount * sizeof(INHERITED_FROM));

		for(i = 0; i < pAcl->AceCount; i++)
		{/* Copy each AncestorName into the destination data segment */
			const std::basic_string<TCHAR> &GenName = (InheritTmp[i].AncestorName == NULL) ?
				_T("Parent") : InheritTmp[i].AncestorName;	/* Proxy Object (for _tcslen) */
			CopyString(GenName, DataPtr, GenName.size());
			//GenName.copy(DataPtr, GenName.size(), 0);

			/* Update the array segment with this new data. */
			InheritResult[i].GenerationGap = InheritTmp[i].GenerationGap;
			InheritResult[i].AncestorName = DataPtr;
			/* This name is now in the data segment. Iterate to the next pointer. */
			DataPtr += GenName.size() + 1;
		}
	}

	/* Cleanup resources */
	::FreeInheritedFromArray(InheritTmp, pAcl->AceCount, NULL);
	::LocalFree(InheritTmp); InheritTmp = NULL;
	/* Possible leak? */
	*ppInheritArray = InheritResult;
	return HRESULT_FROM_WIN32(dwErr);
}



STDMETHODIMP
CObjSecurity::GetEffectivePermission(const GUID * /*pguidObjectType*/, PSID pUserSid, LPCWSTR /*pszServerName*/,
	PSECURITY_DESCRIPTOR pSD, POBJECT_TYPE_LIST* ppObjectTypeList, ULONG* pcObjectTypeListLength,
	PACCESS_MASK* ppGrantedAccessList, ULONG* pcGrantedAccessListLength)
{/** This method is called by GetEffectivePermission when the user uses ACLUI's frontend for GetEffectiveRightsFromAcl().
*	We are supplied the User SID, the server for that SID, and the SD. We must fill the object list and the
*	grantedAccessList (and their sizes).
**/
	DWORD dwErr = 0;
	BOOL AclPresent = FALSE, AclDefaulted = FALSE;
	PACCESS_MASK AccessRights = NULL;
	PACL Dacl = NULL;

	*ppObjectTypeList = const_cast<OBJECT_TYPE_LIST *>(g_DefaultOTL);
	*pcObjectTypeListLength = 1;
	::SetLastError(ERROR_SUCCESS);

	/* Get the Acl from the SD given */
	dwErr = ::GetSecurityDescriptorDacl(pSD, &AclPresent, &Dacl, &AclDefaulted);
	if(Dacl != NULL && dwErr == TRUE)
	{/* Make a trustee from the SID given */
		TRUSTEE Trustee = {0};
		::BuildTrusteeWithSid(&Trustee, pUserSid);

		/* Copy AccessRights to a LocalAlloc ptr. ACLUI will free it. */
		AccessRights = reinterpret_cast<PACCESS_MASK>(::LocalAlloc(LPTR, sizeof(PACCESS_MASK) + sizeof(ACCESS_MASK)));
		if(AccessRights == NULL)
		{
			return E_OUTOFMEMORY;
		}
		dwErr = ::GetEffectiveRightsFromAcl(Dacl, &Trustee, AccessRights);
		if(dwErr != ERROR_SUCCESS)
		{/* And call the required API. */
			::LocalFree(AccessRights); AccessRights = NULL;
			return HRESULT_FROM_WIN32(dwErr);
		}
		*ppGrantedAccessList = AccessRights;
		*pcGrantedAccessListLength = 1;
		return S_OK;
	}
	return HRESULT_FROM_WIN32(dwErr);
}



STDMETHODIMP
CObjSecurity::LookupSids(ULONG /* cSids */, PSID * /* rgpSids */, LPDATAOBJECT * /* ppdo */)
{/* ISecurityInformation2. Let ACLUI handle this work. */
	return E_NOTIMPL;
}



BOOL STDMETHODCALLTYPE
CObjSecurity::IsDaclCanonical(PACL)
{/* We'll always return TRUE (assume the dacl is ordered). */
	return TRUE;
}


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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Mr. Shah is a reclusive C++/C# developer lurking somewhere in the depths of the city of London. He learnt physics at Kings' College London and obtained a Master in Science there. Having earned an MCAD, he teeters on the brink of transitioning from C++ to C#, unsure of which language to jump to. Fortunately, he also knows how to use .NET interop to merge code between the two languages (which means he won't have to make the choice anytime soon).

His interests (apart from programming) are walking, football (the real one!), philosophy, history, retro-gaming, strategy gaming, and any good game in general.

He maintains a website / blog / FAQ / junk at shexec32.serveftp.net, where he places the best answers he's written to the questions you've asked. If you can find him, maybe you can hire Mr. Shah to help you with anything C++[/CLI]/C#/.NET related Smile | :) .

Comments and Discussions