Click here to Skip to main content
15,891,375 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.9K   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"
#include "URLCtrl.h"
#include "resource2.h"
/* I moved all the utility classes from SecurityCore.h to here (because they were cluttering up SecurityCore.h. */


const SI_ACCESS g_siObjAccesses[] =
{/** This structure describes each flag in the file access mask. It is constant. ACLUI displays these
*	strings in its UI.
**/
	{ &GUID_NULL, FILE_ALL_ACCESS, L"Full Control",
		SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE },
	{ &GUID_NULL, FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,
		L"Modify", SI_ACCESS_GENERAL | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE },
	{ &GUID_NULL, FILE_GENERIC_EXECUTE,
		L"Execute", SI_ACCESS_GENERAL | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE },
	{ &GUID_NULL, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,
		L"List Folder Contents", SI_ACCESS_CONTAINER | CONTAINER_INHERIT_ACE },
	{ &GUID_NULL, FILE_GENERIC_READ,
		L"Read", SI_ACCESS_GENERAL | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE },
	{ &GUID_NULL, FILE_GENERIC_WRITE,
		L"Write", SI_ACCESS_GENERAL | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE },

	{ &GUID_NULL, FILE_EXECUTE, L"Traverse Folder/Execute File", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, FILE_READ_DATA, L"List Folder/Read Data", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, FILE_READ_ATTRIBUTES, L"Read Attributes", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, FILE_READ_EA, L"Read Extended Attributes", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, FILE_WRITE_DATA, L"Create Files/Write Data", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, FILE_APPEND_DATA, L"Create Folders/Append Data", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, FILE_WRITE_ATTRIBUTES, L"Write Attributes", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, FILE_WRITE_EA, L"Write Extended Attributes", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, FILE_DELETE_CHILD, L"Delete Children", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, DELETE, L"Delete", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, READ_CONTROL, L"Read Permissions", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, WRITE_DAC, L"Set Permissions", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, WRITE_OWNER, L"Take Ownership", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, SYNCHRONIZE, L"Synchronize", SI_ACCESS_SPECIFIC },
	{ &GUID_NULL, FILE_GENERIC_EXECUTE, L"Traverse/Execute", 0 },
	{ &GUID_NULL, FILE_GENERIC_EXECUTE | FILE_GENERIC_WRITE, L"Write and Execute", 0 },
	{ &GUID_NULL, FILE_GENERIC_EXECUTE | FILE_GENERIC_WRITE | FILE_GENERIC_READ,
		L"Read Write and Execute", 0 },

	{ &GUID_NULL, 0, L"None", 0 }
	// { &GUID_NULL, SERVICE, L"Insert Privilege Here", SI_ACCESS_GENERAL | SI_ACCESS_SPECIFIC }
};

const OBJECT_TYPE_LIST g_DefaultOTL[] =
{/** This empty object list is required by GetEffectivePermission(). This structure is required by ACLUI
*	to be a static constant.
**/
	{ 0, 0, const_cast<LPGUID>(&GUID_NULL) },
};


const SI_INHERIT_TYPE g_InheritTypes[] =
{/* This gets the inheritance of the file. This structure is constant */
	&GUID_NULL, 0, L"This Object",
	&GUID_NULL, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, L"This object, inherited object and containers",
	&GUID_NULL, CONTAINER_INHERIT_ACE, L"This object and containers",
	&GUID_NULL, OBJECT_INHERIT_ACE, L"This object and inherited objects",
	&GUID_NULL, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, L"Inherited containers/objects",
	&GUID_NULL, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE, L"Inherited Containers",
	&GUID_NULL, INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE, L"Inherited Objects"
};


const ULONG
	gc_siObjAccesses = sizeof(g_siObjAccesses) / sizeof(g_siObjAccesses[0]) ,
	gc_DefaultOTL = sizeof(g_DefaultOTL) / sizeof(g_DefaultOTL[0]) ,
	gc_InheritTypes = sizeof(g_InheritTypes) / sizeof(g_InheritTypes[0]);



/* These are flags for DllInstallEx() */
const int /* All used by ShellDLL.cpp */
	SILENT = (1 << 0) ,
	ALLUSERSONLY = (1 << 1) ,
	MEONLY = (1 << 2) ,
	HKCRONLY = (1 << 3);
HINSTANCE g_hInst;		/* Application Constant (set at startup) */
UINT g_RefCount;		/* This WORM variable may need interlocked access. */
HANDLE g_hMutex;		/* Application Constant (set at startup). */





PrivMgrImpl::PrivMgrImpl()
	: RefCount(0)
{
}


PrivMgrImpl::~PrivMgrImpl()
{
	if(RefCount > 0)
	{/* Clobber the impersonation */
		::RevertToSelf();
		/* Once called, the impersonation is obliterated and all enabled privileges are finished. */
	}

}




BOOL PrivMgrImpl::TrySetPrivilege(LPCTSTR lpszPrivilege /* = SE_SECURITY_NAME */,
	BOOL bEnablePrivilege /* = FALSE */)
{
	/* This function handles reference counting of the privilege, and also overall impersonation. */
	DWORD Result = 0;
	HANDLE hToken = NULL;
	if(::OpenThreadToken(::GetCurrentThread(), TOKEN_WRITE, TRUE, &hToken) != TRUE
		|| hToken == NULL || hToken == INVALID_HANDLE_VALUE)
	{
		/* ImpersonateSelf Then call OpenThreadToken */
		if(::ImpersonateSelf(SecurityImpersonation) != TRUE)
		{
			throw DWORD(4);
		}

		if(::OpenThreadToken(::GetCurrentThread(), TOKEN_WRITE, TRUE, &hToken) != TRUE
			|| hToken == NULL || hToken == INVALID_HANDLE_VALUE)
		{
			if( ::OpenProcessToken(::GetCurrentProcess(), TOKEN_WRITE, &hToken) != TRUE
				|| hToken == NULL || hToken == INVALID_HANDLE_VALUE)
			{/* Whenever something went wrong, do not commit the new reference count. */
				throw DWORD(0);
			}
		}
	}

	Result = this->DoSetPrivilege(hToken, lpszPrivilege, bEnablePrivilege);
	if(hToken != NULL && hToken != INVALID_HANDLE_VALUE) ::CloseHandle(hToken); hToken = NULL;

	if(Result != 0)
		throw DWORD(Result);

	/* Increment ref count accordingly */
	if(bEnablePrivilege == TRUE) this->RefCount++;
	else this->RefCount--;

	if(this->RefCount < 1)
	{/* Are any other privileges enabled? */
		if(::RevertToSelf() != TRUE)
			throw DWORD(5);
	}
	return Result;
}




DWORD PrivMgrImpl::DoSetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege /* = SE_SECURITY_NAME */,
	BOOL bEnablePrivilege /*= FALSE */)
{/* Straight out of the Platform SDK, this function. */
	TOKEN_PRIVILEGES tp = {0};
	LUID luid = {0};

	if( ::LookupPrivilegeValue(NULL, lpszPrivilege, &luid) != TRUE )
	{
		return 1; 
	}

	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = luid;
	if(bEnablePrivilege == TRUE)
	{/* On those brain dead systems that define TRUE != 1, disable the privilege (fail securely). */
		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	}
	else
	{
		tp.Privileges[0].Attributes = 0;
	}


	::SetLastError(ERROR_SUCCESS);
	if( ::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL) != TRUE
		&& ::GetLastError() != ERROR_SUCCESS)
	{/* AdjustTokenPrivilege doesn't fail in case of non-admin. Just GetLastError() == ERROR_NOT_ALL_ASSIGNED. */
		return 2;
	}

	return 0;
}



PrivMgr::PrivMgr(const std::basic_string<TCHAR> &sPrivilege, PrivMgrImpl *HandedClass)
	: PrivilegeName(sPrivilege), ObjImplPtr(HandedClass), SecuritySet(FALSE)
{
}


PrivMgr::~PrivMgr()
{
	if(this->SecuritySet == TRUE)
		this->ObjImplPtr->TrySetPrivilege(this->PrivilegeName.c_str(), FALSE);
}

BOOL PrivMgr::TurnOn(void)
{
	try {
		this->ObjImplPtr->TrySetPrivilege(this->PrivilegeName.c_str(), TRUE);
		this->SecuritySet = TRUE;
		return TRUE;
	} catch (DWORD &) {
	}
	return FALSE;
}




const _bstr_t __stdcall GetLastErrorText(DWORD dwError)
{/** Formats the error message specified and converts it to a string. The return value is a BSTR denoting the
*	error string. If the message isn't found, "Unknown Win32 Error" is returned.
*	This is an API entry point. Fortunately, it only takes a DWORD, so no input checking is really required.
**/
	_bstr_t szMsg;
	LPTSTR szMsgBuf = NULL;

	/* Let FormatMessage format a buffer for us. */
	DWORD dwRes = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
		NULL, dwError, 0, reinterpret_cast<LPTSTR>(&szMsgBuf), 32000, NULL);
	if (dwRes == 0) {
		szMsg = _T("Unknown Win32 Error.");
	}
	else szMsg = szMsgBuf;
	::LocalFree(szMsgBuf); szMsgBuf = NULL;
	return szMsg;
}



void About_OnHelp(HWND TheirhWnd)
{/**	This is a generic WM_HELP handler that displays the manual whenever help is needed.
*	If help does not exist, this function does nothing but waste some CPU instructions.
*	You pass in the parent Window handle, 
**/
	::sized_array<TCHAR> HelpFileName (FILENAME_MAX);
	/* Use FILENAME_MAX as default, so we may not even need a realloc */
	TCHAR *lpFilePart = NULL;	/* dummy pointer for final parameter for SearchPath */
	DWORD len = ::SearchPath(NULL, _T("filepermsbox.txt"), NULL, FILENAME_MAX, HelpFileName.get(), &lpFilePart);
	if(len > FILENAME_MAX)
	{/* We don't usually reach here. */
		HelpFileName.reset(len + 1);
		len = ::SearchPath(NULL, _T("filepermsbox.txt"), NULL, len + 1, HelpFileName.get(), &lpFilePart);
	}
	if(*HelpFileName.get() != _T('\0'))
	{/* Test for an empty string. If we didn't find the documentation, do nothing. */
		::ShellExecute(TheirhWnd, NULL, HelpFileName.get(), NULL, NULL, SW_SHOWNORMAL);
	}
}




INT_PTR CALLBACK AboutFunc(HWND TheirhWnd, UINT Msg, WPARAM wParam, LPARAM)
{/* This is the about dialog box. The resource has done most of the stuff for us. */
	switch(Msg)
	{
		case WM_INITDIALOG:
		{
			const std::basic_string<TCHAR> &HomePageLink (_T("http://hometown.aol.co.uk/shexec32/"));
			::SetDlgItemText(TheirhWnd, IDC_EDIT1,
				_T("FilePermsBox. Shows the File Security tab in Windows Explorer, even if it was disabled."));
			::SetDlgItemText(TheirhWnd, IDC_HOMEPAGE, HomePageLink.c_str());

			/* make the url label clickable. */
			::urlctrl_set(::GetDlgItem(TheirhWnd, IDC_HOMEPAGE), HomePageLink);
			return TRUE;
		}
		case WM_COMMAND:
		{
			switch LOWORD(wParam)
			{
				case IDCANCEL:
				{/* Close */
					::PostMessage(TheirhWnd, WM_CLOSE, 0, 0);
					return FALSE;
				}
				default:
					break;
			}
			return FALSE;
		}
		case WM_HELP:
		{/* The help cursor was used. Show the manual. */
			About_OnHelp(TheirhWnd);
			return FALSE;
		}
		case WM_CLOSE:
		{
			EndDialog(TheirhWnd, 0);
			return TRUE;
		}
		default:
			break;
	}

	return FALSE;
}




BOOL __stdcall ShowAbout(HWND TheirhWnd)
{/** This API (implemented in the dll), displays an about dialog box. The about box is modal on TheirhWnd.
*	If the HWND is invalid, the dialog will be be modal on the desktop.
**/
	if(::IsWindow(TheirhWnd) != TRUE)
		TheirhWnd = NULL;
	::DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_DIALOG3), TheirhWnd, AboutFunc, 0);
	return TRUE;
}




const std::basic_string<TCHAR>::size_type
	CopyString(const std::basic_string<TCHAR> &OldClass, TCHAR *OutBuffer, size_t Count)
{/** A Thin wrapper for basic_string<TCHAR>::copy. Dangerous function! Do not reuse.
*	Notice that you have to pass in the Count of OutBuffer to use this function. Since this
*	is developer input (or even worse, user input), CopyString shouldn't be used except in the
*	most extreme of cases.
*
*	OldClass: Which basic_string to copy from
*	OutBuffer: The Buffer to copy to. Must be large enough to contain the contaminated string
*	Count: the size of OutBuffer. A good choice for this is OldClass.size()
**/
	std::basic_string<TCHAR>::size_type Result = 0;
#ifndef _SCL_INSECURE_DEPRECATE
	/* I don't trust std::min, especially when windows.h is involved. */
	size_t BufSize = (OldClass.size() > Count) ? Count : OldClass.size();
	Result = OldClass.copy(OutBuffer, BufSize, 0);
#else
	OldClass._Copy_s(OutBuffer, Count, OldClass.size(), 0);
#endif /* _SCL_INSECURE_DEPRECATE */
	return Result;
}



BOOL __stdcall CheckVersion(void)
{/** API Entrypoint for FilePermsBoxDLL, takes no input (cool), and returns the major version
*	number of the OS. ::_osver should no longer be used.
**/
#pragma warning (push)
#pragma warning (disable:4127)	/* Conditional expression is constant */
#pragma warning (disable:6326)	/* potential comparison of a constant with another constant */
	::OSVERSIONINFOEX osVer = {0};
	osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
	::GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&osVer));

	if((osVer.dwMajorVersion < 5) || (FALSE != 0) || (NULL != 0) || (sizeof(BOOL) != sizeof(int)))
	{/* Refuse to install in WinNT/9x. */
		return FALSE;
	}
#pragma warning (pop)

	return TRUE;
}



#ifdef _WIN64
ULONG_PTR LONG_PTR2(void *converterNumber)
{/* These functions are API'ed. Due to the fact we do not dereference the pointers, we are safe. */
	return reinterpret_cast<ULONG_PTR>(converterNumber);
}
#else /* Set*LongPtr -Wp64 hack. silences the C4244 warning because setclasslongptr does that. */
ULONG LONG_PTR2(void *converterNumber)
{
	return PtrToUlong(converterNumber);
}
#endif /* _WIN64 TODO: When Set*LongPtr is fixed, remove this hack. */




BOOL __stdcall GetSDForObject(const std::basic_string<TCHAR> &FileName,
	std::basic_string<TCHAR> &SDDLString, const BOOL IncludeInherited)
{/** This function converts a filename into its string security descriptor (in SDDL). IncludeInherited
*	means Unprotected acls will be included too. This is an API entry point, so only reference types and
*	value types should be used.
**/
	DWORD dwErr = 0;
	LPTSTR SDDLBuffer = NULL;
	PSECURITY_DESCRIPTOR ppSD = NULL;
	/* This privilege will be removed upon exit of this function. */
	PrivMgrImpl ClassMgrImpl;
	{
		PrivMgr SecPriv (SE_SECURITY_NAME, &ClassMgrImpl);

		if( ::GetFileAttributes(FileName.c_str()) == INVALID_FILE_ATTRIBUTES )
		{/* validate inputs */
			::SetLastError(ERROR_INVALID_PARAMETER);
			return FALSE;
		}

		SECURITY_INFORMATION psi = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
			OWNER_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
		if(IncludeInherited == TRUE)
			psi = psi | UNPROTECTED_DACL_SECURITY_INFORMATION;
		if(SecPriv.TurnOn())
		{
			psi = psi | SACL_SECURITY_INFORMATION | PROTECTED_SACL_SECURITY_INFORMATION;
			if(IncludeInherited == TRUE)
				psi = psi | UNPROTECTED_SACL_SECURITY_INFORMATION;
		}

		/* Allocate a writable copy of FileName */
		sized_array<TCHAR> FNameWritable (FileName);
		//sized_array<TCHAR> FNameWritable (FileName.size() + 1);
		//FileName.copy(FNameWritable.get(), FileName.size(), 0);

		/* And Get its SD. */
		dwErr = ::GetNamedSecurityInfo(FNameWritable.get(), SE_FILE_OBJECT, psi, NULL, NULL, NULL, NULL, &ppSD);
		if(dwErr != ERROR_SUCCESS) return FALSE;

		/* Get this resultant SD in SDDL */
		dwErr = ::ConvertSecurityDescriptorToStringSecurityDescriptor(ppSD, SDDL_REVISION_1, psi, &SDDLBuffer, NULL);

		/* Cleanup. */
		::LocalFree(ppSD); ppSD = NULL;
		if(dwErr == TRUE)/* Copy the SDDL into the caller supplied string. */
			SDDLString = SDDLBuffer;
		::LocalFree(SDDLBuffer); SDDLBuffer = NULL;
	}
	return TRUE;
}




BOOL FixSDs(HWND TheirhWnd, SECURITY_INFORMATION psi,
	const std::list< const std::basic_string<TCHAR> > &FileNamesCollection)
{/* This function creates a temporary CObjSecurity, creates a new repair SD, and passes it to SetSecurity. */
	PSECURITY_DESCRIPTOR pSD = NULL;
	try {
		PrivMgrImpl PrivClassImpl;
		{
			PrivMgr SecurityPriv(SE_SECURITY_NAME, &PrivClassImpl);
			/* Instantiate a CObjSecurity and call its methods to get the security. */
			std::auto_ptr<CObjSecurity> SecurityCore ( new CObjSecurity(FileNamesCollection,
				SE_FILE_OBJECT, TheirhWnd) );

			SecurityPriv.TurnOn();
			SecurityCore->MakeWritable(TRUE);
			SecurityCore->GetSecurity(psi, &pSD, TRUE);
			SecurityCore->SetSecurity(psi, pSD);	/* Setsecurity will walk the tree for us. */

			::LocalFree(pSD); pSD = NULL;
		}
	} catch(const std::basic_string<TCHAR> &e) {/* Another problem occurred, show the user. */
		MessageBox(TheirhWnd, e.c_str(), _T("Error Setting Security"), MB_OK | MB_ICONERROR);
		return FALSE;
	}
	return TRUE;
}



const BOOL AreAclsEqual(std::basic_string<TCHAR> &FirstAcl, std::basic_string<TCHAR> &SecondAcl)
{/** This procedure determines if the 1stAcl has the same information as 2ndAcl. 1stAcl and
*	2ndAcl are string security descriptors (The D:ARAI part is not included).
*	(A;;FA;;;BA)(A;OICIIO;0x001701bf;;;PU)(A;OICIIO;GRGWGX;;;S-1-5-123243273948765-4572369875263-1003)
*
*	We don't receive the D:PARAI part (just the brackets in between). Each bracket represents an ACE.
*	Get the substring between these brackets, then find this substring in the second ACL. And
*	loop for each ACE.
*
*	Note: This function should be called IsFirstAclContainedInSecondAcl(). This is because the
*	function only tests for containment, not equality. To test for equality, Call this function
*	again with IsSecondAclContainedInFirstAcl().
*
*	This function was tested in a separate application. It may not have integrated properly into this application.
**/
	BOOL result = TRUE;/* Default to true. */
	std::basic_string<TCHAR> TempString, ACETemp = FirstAcl;
	/* ACETemp will be our work string. */
	std::basic_string<TCHAR>::size_type Iter1 = 0, Iter2 = 0;

	while(ACETemp.size() > 1)
	{
		/* Anchor Iter1 and Iter2 between the brackets (inclusive) */
		Iter1 = ACETemp.find(_T('('));
		/* Anchor Iter1 at the First Bracket. */
		if(Iter1 > ACETemp.size())
		{/* End of ACLs, return the result. */
			return result;
		}

		/* Anchor Iter2 at the second bracket. */
		Iter2 = ACETemp.find(_T(')'), Iter1 + 1);
		TempString = ACETemp.substr(Iter1, Iter2 + 1 - Iter1);

		/* Now that we have an ACE, find this ACE in SecondAcl. */
		if(SecondAcl.find(TempString) > SecondAcl.size())
		{/* Not found eh? The acls aren't equal. */
			result = FALSE;
			return result;
		}
		/* We must now erase this ACE from ACEAnchor. */
		ACETemp = ACETemp.substr(Iter2 + 1, ACETemp.size() - Iter2 - 1);
	}

	return result;
}



SECURITY_INFORMATION CompareSDs(const std::basic_string<TCHAR> &SDStr, const std::basic_string<TCHAR> &SDStr1st)
{/** This function compares string security descriptor 1 against string security descriptor 2.
*	This requires us to tear apart the security descriptor into pieces.
*	The return value is either 0 if the SDs are unequal, otherwise, psi returns the information that's wrong.
*	The function leaks an exception if there are problems.
**/
	std::basic_string<TCHAR> pSD = SDStr, pSD1st = SDStr1st;
	std::basic_string<TCHAR>::size_type Iter1 = 0, Iter2 = 0;
	std::basic_string<TCHAR>::size_type i = 0, j = 0;
	SECURITY_INFORMATION psi = 0;

	for(i = 0; i < SDStr.size() && i < SDStr1st.size(); i = j)
	{/* Get the next colon. */
		Iter1 = SDStr.find(_T(":"), i);
		if(Iter1 > SDStr.size()) break;
		/* Anchor Iter2 to the next colon. */
		Iter2 = SDStr.find(_T(":"), Iter1 + 1) - 1;
		/* Place to just before the letter before the colon. */
		if(Iter2 > SDStr.size()) Iter2 = pSD.size();
		pSD = SDStr.substr(Iter1 + 1, Iter2 - Iter1 - 1);
		j = Iter1 + 1;
		/* pSD should now contain the value of this part. */

		switch(static_cast<TCHAR>(SDStr.at(Iter1 - 1)))
		{
			case _T('D'):
			{/* DACL information. */
				Iter1 = SDStr1st.find(_T("D:"));
				if(Iter1 > SDStr1st.size())
				{/* not found ==> conflict */
					psi = psi | DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
					break;
				}
				Iter2 = SDStr1st.find(_T(":"), Iter1 + 2) - 1;
				if(Iter2 > SDStr1st.size()) Iter2 = SDStr1st.size();
				pSD1st = SDStr1st.substr(Iter1 + 2, Iter2 - Iter1 - 2);

				/* check the control flags */
				Iter1 = pSD.find(_T('('));
				Iter2 = pSD1st.find(_T('('));
				if(Iter1 != Iter2)
				{/* Wrong control flags. */
					psi = psi | DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
					break;
				}
				if(pSD.substr(0, Iter1).compare(pSD1st.substr(0, Iter2)) != 0)
				{/* The control flags aren't the same */
					psi = psi | DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
					break;
				}
				Iter1 = pSD.find(_T('('));
				if(Iter1 < pSD.size())
				{/* Only copy ACEs if they are there. */
					pSD = pSD.substr(Iter1, pSD.size() - Iter1);/* pSD now points to the bit after the control stuff */
				}

				/* Repeat for pSD1st */
				Iter1 = pSD1st.find(_T('('));
				if(Iter1 < pSD.size())
				{
					pSD1st = pSD1st.substr(Iter1, pSD1st.size() - Iter1);
				}
				if(pSD.find(_T('(')) > pSD.size() && pSD.find(_T(')')) < pSD1st.size())
				{/* One has ACEs, but the other doesn't: they're different. */
					psi = psi | DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
					break;
				}


				if(AreAclsEqual(pSD, pSD1st) != TRUE || AreAclsEqual(pSD1st, pSD) != TRUE)
				{/* Cause it's so tough, we're sending it to another function to compare the ACLs. */
					psi = psi | DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
					break;
				}
				break;
			}
			case _T('O'):
			{/* Owner information. Find the corresponding item in SDStr1st */
				Iter1 = SDStr1st.find(_T("O:"));
				if(Iter1 > SDStr1st.size())
				{/* not found ==> conflict */
					psi |= OWNER_SECURITY_INFORMATION;
					break;
				}
				Iter2 = SDStr1st.find(_T(":"), Iter1 + 2) - 1;
				if(Iter2 > SDStr1st.size()) Iter2 = SDStr1st.size();
				pSD1st = SDStr1st.substr(Iter1 + 2, Iter2 - Iter1 - 2);

				if(pSD.compare(pSD1st) != 0)
				{/* The SIDs should be equal. */
					psi |= OWNER_SECURITY_INFORMATION;
					break;
				}
				break;
			}
			case _T('G'):
			{/* Group information can be tackled in a similar fashion as owner information */
				Iter1 = SDStr1st.find(_T("G:"));
				if(Iter1 > SDStr1st.size())
				{/* not found ==> conflict */
					psi |= GROUP_SECURITY_INFORMATION;
					break;
				}
				Iter2 = SDStr1st.find(_T(":"), Iter1 + 2) - 1;
				if(Iter2 > SDStr1st.size()) Iter2 = SDStr1st.size();
				pSD1st = SDStr1st.substr(Iter1 + 2, Iter2 - Iter1 - 2);

				if(pSD.compare(pSD1st) != 0)
				{/* The SIDs should be equal. */
					psi |= GROUP_SECURITY_INFORMATION;
					break;
				}
				break;
			}
			case _T('S'):
			{/* SACL information. Once DACL is written, then this case is rather easy. */
				Iter1 = SDStr1st.find(_T("S:"));
				/* 1.08 fix D->S. */
				if(Iter1 > SDStr1st.size())
				{/* not found ==> conflict */
					psi = psi | SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION;
					break;
				}
				Iter2 = SDStr1st.find(_T(":"), Iter1 + 2) - 1;
				if(Iter2 > SDStr1st.size()) Iter2 = SDStr1st.size();
				pSD1st = SDStr1st.substr(Iter1 + 2, Iter2 - Iter1 - 2);

				/* check the control flags */
				Iter1 = pSD.find(_T('('));
				Iter2 = pSD1st.find(_T('('));
				if(Iter1 != Iter2)
				{/* Wrong control flags. */
					psi = psi | SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION;
					break;
				}
				if(pSD.substr(0, Iter1).compare(pSD1st.substr(0, Iter2)) != 0)
				{/* The control flags aren't the same */
					psi = psi | SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION;
					break;
				}
				Iter1 = pSD.find(_T('('));
				if(Iter1 < pSD.size())
				{/* Only copy ACEs if they are there. */
					pSD = pSD.substr(Iter1, pSD.size() - Iter1);/* pSD now points to the bit after the control stuff */
				}

				/* Repeat for pSD1st */
				Iter1 = pSD1st.find(_T('('));
				if(Iter1 < pSD.size())
				{
					pSD1st = pSD1st.substr(Iter1, pSD1st.size() - Iter1);
				}
				if(pSD.find(_T('(')) > pSD.size() && pSD.find(_T(')')) < pSD1st.size())
				{/* One has ACEs, but the other doesn't: they're different. */
					psi = psi | SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION;
					break;
				}


				if(AreAclsEqual(pSD, pSD1st) != TRUE || AreAclsEqual(pSD1st, pSD) != TRUE)
				{/* Cause it's so tough, we're sending it to another function to compare the ACLs. */
					psi = psi | SACL_SECURITY_INFORMATION | PROTECTED_SACL_SECURITY_INFORMATION |
						UNPROTECTED_SACL_SECURITY_INFORMATION;
					break;
				}
				break;
			}
			default:
				throw STD(_T("Unexpected token found in this security descriptor"));
		}
	}

	return psi;
}







#if 0
void SEFunc(unsigned int ExceptCode, EXCEPTION_POINTERS *ExceptPtrs)
{/* DLLs have little right to hijack the se_translator_function, so this function must be used sparingly. */
	DWORD dwErr = ::GetLastError();
	::SetLastError(ERROR_SUCCESS);

	switch(ExceptCode)
	{
		case VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND):
		{
			DelayLoadInfo *dli;
			if(! ::IsBadReadPtr(ExceptPtrs->ExceptionRecord->ExceptionInformation[0], sizeof(DelayLoadInfo))
			{
				dli = ;
			}
			throw STD(_T(""));
		}

		default:
			throw DWORD(ExceptCode);
	}
}
#endif /* SEFunc is not good in a DLL. */

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