Click here to Skip to main content
11,631,212 members (76,736 online)
Click here to Skip to main content
Add your own
alternative version

The Windows Access Control Model: Part 2

, 27 Jun 2005 CPOL 157.7K 5.8K 109
This second part of the Access Control series will program with the basic Access Control structures.
boost.zip
boost
config
compiler
platform
stdlib
detail
mpl
aux_
config
preprocessed
bcc
bcc551
gcc
msvc60
msvc70
mwcw
no_ctps
no_ttp
plain
preprocessor
range_c
preprocessor
arithmetic
detail
array
comparison
config
control
detail
edg
msvc
detail
facilities
iteration
detail
bounds
iter
list
detail
edg
logical
punctuation
repetition
detail
edg
msvc
selection
seq
detail
slot
detail
tuple
regex
v3
test
detail
included
type_traits
detail
utility
userfun.zip
ATL
release
UserFun.exe
LowLevel
release
LowLevel.exe
2000
release
2000.exe
whoami.zip
Whoami
release
Whoami.exe
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#define _WIN32_WINNT 0x400
#define _CRTDBG_MAP_ALLOC

#include<aclapi.h>
#include<windows.h>
#include<stdio.h>
#include<stdlib.h>
#include<crtdbg.h>
#include<tchar.h>



//***********************************************
BOOL GetTextualSid(PSID pSid, LPTSTR TextualSid, LPDWORD lpdwBufferLen)
{/* This is a helper function from http://www.codeproject.com/system/sid.asp */
	PSID_IDENTIFIER_AUTHORITY psia;
	DWORD dwSubAuthorities;
	DWORD dwSidRev=SID_REVISION;
	DWORD dwCounter;
	DWORD dwSidSize;

	// Validate the binary SID.

	if(!IsValidSid(pSid)) return FALSE;

	// Get the identifier authority value from the SID.

	psia = GetSidIdentifierAuthority(pSid);

	// Get the number of subauthorities in the SID.

	dwSubAuthorities = *GetSidSubAuthorityCount(pSid);

	// Compute the buffer length.
	// S-SID_REVISION- + IdentifierAuthority- + subauthorities- + NULL

	dwSidSize=(15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);

	// Check input buffer length.
	// If too small, indicate the proper size and set the last error.

	if (*lpdwBufferLen < dwSidSize || TextualSid == NULL)
	{
		*lpdwBufferLen = dwSidSize;
		SetLastError(ERROR_INSUFFICIENT_BUFFER);
		return FALSE;
	}

	// Add 'S' prefix and revision number to the string.

	dwSidSize=wsprintf(TextualSid, TEXT("S-%lu-"), dwSidRev );

	// Add a SID identifier authority to the string.

	if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) )
	{
		dwSidSize+=wsprintf(TextualSid + lstrlen(TextualSid),
			TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
			(USHORT)psia->Value[0],
			(USHORT)psia->Value[1],
			(USHORT)psia->Value[2],
			(USHORT)psia->Value[3],
			(USHORT)psia->Value[4],
			(USHORT)psia->Value[5]);
	}
	else
	{
		dwSidSize+=wsprintf(TextualSid + lstrlen(TextualSid),
			TEXT("%lu"),
			(ULONG)(psia->Value[5]      )   +
			(ULONG)(psia->Value[4] <<  8)   +
			(ULONG)(psia->Value[3] << 16)   +
			(ULONG)(psia->Value[2] << 24)   );
	}

	// Add SID subauthorities to the string.
	//
	for (dwCounter=0 ; dwCounter < dwSubAuthorities ; dwCounter++)
	{
		dwSidSize+=wsprintf(TextualSid + dwSidSize, TEXT("-%lu"),
			*GetSidSubAuthority(pSid, dwCounter) );
	}

	return TRUE;
}


int WellKnownSid2Trustee(void)
{/* Q1. */
	const TCHAR SystemSid[] = _T("SYSTEM");	/* SYSTEM */
	PSID SidUser = NULL;	/* This is where the resultant SID is returned */
	DWORD cchName = 0, cchReferencedDomainName = 0;	/* Will hold buffer sizes */
	LPTSTR SidText = NULL;	/* Will hold the final SID */
	LPTSTR ReferencedDomainName = NULL;	/* Dummy variable */
	SID_NAME_USE peUse = SidTypeUser;	/* Dummy variable */


	/* First Call LookupAccountName() */
	LookupAccountName(NULL, SystemSid, SidUser, &cchName, NULL, &cchReferencedDomainName, &peUse);
	/* This was expected to fail. We should have allocated large enough buffers */

	SidUser = (PSID)calloc(cchName, 1);
	if(SidUser == NULL)
	{
		return 1;
	}

	ReferencedDomainName = (LPTSTR)calloc(cchReferencedDomainName, sizeof(TCHAR));
	if(ReferencedDomainName == NULL)
	{
		free(SidUser); SidUser = NULL;
		return 1;
	}

	/* Now let's try that again. */
	if(!LookupAccountName(NULL, SystemSid, SidUser, &cchName, ReferencedDomainName, &cchReferencedDomainName, &peUse))
	{
		free(SidUser); SidUser = NULL;
		free(ReferencedDomainName); ReferencedDomainName = NULL;
		return 1;
	}
	free(ReferencedDomainName); ReferencedDomainName = NULL;

	/* Now we have the SID: return the textual representation of that. */
	GetTextualSid(SidUser, NULL, &cchName);

	/* Did we forget something? */
	SidText = (TCHAR *)calloc(cchName, sizeof(TCHAR));
	if(SidText == NULL)
	{
		free(SidUser); SidUser = NULL;
		return 1;
	}
	GetTextualSid(SidUser, SidText, &cchName);

	/* Why would you need a TRUSTEE structure? It's rather useless to you. */
	{
		TRUSTEE TrusteeSid = {0};
		TrusteeSid.pMultipleTrustee = NULL;
		TrusteeSid.ptstrName = (LPTSTR)(SidUser);
		TrusteeSid.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
		TrusteeSid.TrusteeForm = TRUSTEE_IS_SID;
		TrusteeSid.TrusteeType = TRUSTEE_IS_UNKNOWN;
	}


	free(SidUser); SidUser = NULL;

	/* That should be it. */
	_tprintf(_T("%s"), SidText);
	free(SidText); SidText = NULL;


	return 0;
}


#define MAX_USERNAME_LENGTH 256

int Sid2UserName(const PSID UserSid, OUT LPTSTR ReferencedDomainName, OUT LPTSTR UserName)
{
	DWORD cchName = MAX_USERNAME_LENGTH, cchReferencedDomainName = MAX_USERNAME_LENGTH;
	SID_NAME_USE peUse = SidTypeUser;


	if(UserName==NULL)
	{
		SetLastError(ERROR_INVALID_PARAMETER);
		return 1;
	}

	/* Get the username */
	LookupAccountSid(NULL, UserSid, UserName, &cchName, ReferencedDomainName, &cchReferencedDomainName, &peUse);

	return 0;
}



int Sid2UserNamePrepare(void)
{
	/* Setup the current thread token. */
	HANDLE hToken = NULL;
	DWORD TokenInformationLength = 0, ReturnLength = 0;
	TOKEN_USER *TokenInformation = NULL;
	TCHAR UserName[MAX_USERNAME_LENGTH] = _T("");

	if(!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &hToken))
	{
		OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken);
	}

	/* Get the current user from GetTokenInformation(TokenUser) */
	GetTokenInformation(hToken, TokenUser, NULL, TokenInformationLength, &ReturnLength);
	TokenInformation = (TOKEN_USER *)calloc(ReturnLength, 1);
	if(TokenInformation == NULL)
	{
		CloseHandle(hToken); hToken = NULL;
		return 1;
	}
	TokenInformationLength = ReturnLength;

	if(GetTokenInformation(hToken, TokenUser, TokenInformation, TokenInformationLength, &ReturnLength))
	{/* Now that we have the SID, convert it to a user name */
		TCHAR ReferencedDomainName[MAX_USERNAME_LENGTH] = _T("");
		Sid2UserName(TokenInformation->User.Sid, ReferencedDomainName, UserName);
		_tprintf(_T("%s\\%s"), ReferencedDomainName, UserName);
	}

	free(TokenInformation); TokenInformation = NULL;
	CloseHandle(hToken); hToken = NULL;
	return 0;
}




BOOL IsAdminRunning(void)
{
	BOOL IsMember = FALSE;
	size_t i = 0;
	SID_IDENTIFIER_AUTHORITY peUse = SECURITY_NT_AUTHORITY;
	PSID SidUser = NULL;
	HANDLE ProcToken = NULL;
	LPVOID TokenInformation = NULL;
	TOKEN_GROUPS *TokenGroupList = NULL;
	DWORD TokenInformationLength = 0, ReturnLength = 0;

	/* Create a SID with the Admins RID */
	if(!AllocateAndInitializeSid(&peUse, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,
		0, 0, 0, 0, 0, &SidUser))
	{
		return FALSE;
	}

	if(!OpenThreadToken(GetCurrentThread(), TOKEN_READ | TOKEN_DUPLICATE, TRUE, &ProcToken))
	{
		if(!OpenProcessToken(GetCurrentProcess(), TOKEN_READ | TOKEN_DUPLICATE, &ProcToken))
		{
			FreeSid(SidUser); SidUser = NULL;
			return FALSE;
		}
	}
	// DuplicateToken(ProcToken, SecurityImpersonation, &ImpersonationToken);

	/* Call GetTokenInformation(TokenGroups) */
	GetTokenInformation(ProcToken, TokenGroups, TokenInformation, TokenInformationLength, &ReturnLength);
	TokenInformationLength = ReturnLength;
	TokenInformation = calloc(TokenInformationLength, sizeof(BYTE));
	if(TokenInformation == NULL)
	{
		FreeSid(SidUser); SidUser = NULL;
		CloseHandle(ProcToken); ProcToken = NULL;
		return FALSE;
	}

	GetTokenInformation(ProcToken, TokenGroups, TokenInformation, TokenInformationLength, &ReturnLength);
	TokenGroupList = (TOKEN_GROUPS *)(TokenInformation);

	for(i = 0; i < TokenGroupList->GroupCount ; i++)
	{/* Search for the SID in the Groups Array */
		if(EqualSid(TokenGroupList->Groups[i].Sid, SidUser))
		{/* If found, and is enabled, we are an admin */
			if(TokenGroupList->Groups[i].Attributes & SE_GROUP_ENABLED ||
				TokenGroupList->Groups[i].Attributes & SE_GROUP_ENABLED_BY_DEFAULT )
			{
				IsMember = TRUE;
				break;
			}
		}
	}


	FreeSid(SidUser); SidUser = NULL;
	CloseHandle(ProcToken); ProcToken = NULL;
	free(TokenInformation); TokenInformation = NULL;

	return IsMember;
}


BOOL DoWhoAmI(void)
{
	/* returns false on failure */
	// BOOL bResult = FALSE;
	size_t i = 0;
	HANDLE ProcToken = NULL;
	TOKEN_GROUPS_AND_PRIVILEGES *tp = NULL;
	DWORD ReturnLength = 0, TokenInformationLength = 0;
	TCHAR *PrivilegeName = NULL;

	if( !OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, TRUE, &ProcToken) &&
		!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &ProcToken))
	{
		return FALSE;
	}

	/* Call GetTokenInformation(TokenGroupsAndPrivileges) */
	GetTokenInformation(ProcToken, TokenGroupsAndPrivileges, tp, TokenInformationLength, &ReturnLength);
	tp = (TOKEN_GROUPS_AND_PRIVILEGES *)(calloc(ReturnLength, 1));
	if(!tp)
	{
		CloseHandle(ProcToken); ProcToken = NULL;
		return FALSE;
	}
	TokenInformationLength = ReturnLength;
	GetTokenInformation(ProcToken, TokenGroupsAndPrivileges, tp, TokenInformationLength, &ReturnLength);

	/* Start off with AuthenticationId */
	_tprintf(_T("\r\nAuthentication ID:%d%d\r\n"), (tp->AuthenticationId.HighPart), (tp->AuthenticationId.LowPart));
	_tprintf(_T("\r\nPrivileges\r\n"));
	for(i = 0; i < tp->SidCount; i++)
	{/* Groups */
		TCHAR UserName[MAX_USERNAME_LENGTH] = _T("");
		TCHAR ReferencedDomainName[MAX_USERNAME_LENGTH] = _T("");
		Sid2UserName(tp->Sids[i].Sid, ReferencedDomainName, UserName);
		_tprintf(_T("\r\n%s\\%s: %d"), ReferencedDomainName, UserName, tp->Sids[i].Attributes);
	}

	_tprintf(_T("\r\nRestricted Sids\r\n"));
	for(i = 0; i < tp->RestrictedSidCount; i++)
	{/* Restricted Sids */
		TCHAR UserName[MAX_USERNAME_LENGTH] = _T("");
		TCHAR ReferencedDomainName[MAX_USERNAME_LENGTH] = _T("");
		Sid2UserName(tp->Sids[i].Sid, ReferencedDomainName, UserName);
		_tprintf(_T("\r\n%s\\%s: %d"), UserName, ReferencedDomainName, tp->RestrictedSids[i].Attributes);
	}

	_tprintf(_T("\r\nPrivileges\r\n"));
	for(i = 0; i < tp->PrivilegeCount; i++)
	{/* Privileges */
		ReturnLength = 0;
		LookupPrivilegeName(NULL, &tp->Privileges[i].Luid, NULL, &ReturnLength);
		ReturnLength++;
		/* Docs are rather unclear on the null terminator ambiguity. Better safe than sorry. */
		PrivilegeName = (TCHAR *)calloc(ReturnLength, sizeof(TCHAR));
		if(PrivilegeName == NULL)
			break;
		LookupPrivilegeName(NULL, &tp->Privileges[i].Luid, PrivilegeName, &ReturnLength);

		_tprintf(_T("\r\n%s: %d"), PrivilegeName, tp->Privileges[i].Attributes);
		free(PrivilegeName); PrivilegeName = NULL;
	}


	CloseHandle(ProcToken); ProcToken = NULL;
	free(tp); tp = NULL;
	return TRUE;
}



BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
	/* returns false on failure */
	BOOL bResult = FALSE;
	HANDLE ProcToken = NULL;
	TOKEN_PRIVILEGES tp = {0};
	LUID luid = {0};

	if( !OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, TRUE, &ProcToken) &&
		!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &ProcToken))
	{
		return FALSE;
	}

	if(!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
	{
		CloseHandle(ProcToken); ProcToken = NULL;
		return FALSE;
	}

	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = luid;
	tp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : 0;

	/* Enable the privilege or disable all privileges. */
	SetLastError(ERROR_SUCCESS);
	bResult = (!AdjustTokenPrivileges(ProcToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL) ||
		GetLastError() != ERROR_SUCCESS) ;
	CloseHandle(ProcToken); ProcToken = NULL;
	return bResult;
}


PSECURITY_DESCRIPTOR GetSecurityDesc(LPCTSTR FileName)
{
	PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
	DWORD LengthNeeded = 0;
	/* Find the length needed for the security descriptor */
	GetFileSecurity(FileName, GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION
		| SACL_SECURITY_INFORMATION, NULL, 0, &LengthNeeded);
	pSecurityDescriptor = (PSECURITY_DESCRIPTOR)(LocalAlloc(LPTR, LengthNeeded));
	if(pSecurityDescriptor == NULL)
	{
		return NULL;
	}
	SetLastError(ERROR_SUCCESS);
	GetFileSecurity(FileName, GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION
		| SACL_SECURITY_INFORMATION, pSecurityDescriptor, LengthNeeded, &LengthNeeded);

	return pSecurityDescriptor;
}


PSECURITY_DESCRIPTOR ConvertSecurityDescriptor(PSECURITY_DESCRIPTOR *ppSDIn)
{
	PACL AbsDAcl = NULL, AbsSAcl = NULL ;
	PSID AbsOwner = NULL, AbsGroup = NULL ;
	DWORD dwSize = 0, dwSizes[5] = {0} ;
	size_t i = 0 ;
	PSECURITY_DESCRIPTOR ppSDOut = *ppSDIn;
	/* Transfer ppSD into this variable. */

	if(ppSDOut == NULL || !(((SECURITY_DESCRIPTOR*)ppSDOut)->Control & SE_SELF_RELATIVE))
	{/* What do you mean? An African or European swallow? */
		SetLastError(ERROR_INVALID_PARAMETER);
		return NULL;
	}

	MakeAbsoluteSD(*ppSDIn, NULL, &dwSizes[0], NULL, &dwSizes[4], NULL, &dwSizes[3],
		NULL, &dwSizes[2], NULL, &dwSizes[1]) ;
	for(i = 0 ; i < 5 ; i++)
		dwSize += dwSizes[i] ;
	ppSDOut = (PSECURITY_DESCRIPTOR)
		(LocalAlloc(LPTR, 2 * sizeof(ACL) + 2 * sizeof(SID) + dwSize)) ;
	if(ppSDOut == NULL)
	{/* HACKHACK: Allocate a 2D array with one LocalAlloc */
		LocalFree(*ppSDIn); *ppSDIn = NULL;
		return NULL;
	}

	/* Now set the pointers to the appropriate offsets. */
	dwSize = dwSizes[0] ;
	AbsOwner = (PSID)((BYTE *)(ppSDOut) + dwSize) ;
	dwSize += sizeof(SID) + dwSizes[1] ;
	AbsGroup = (PSID)((BYTE *)(ppSDOut) + dwSize) ;
	dwSize += sizeof(SID) + dwSizes[2] ;
	AbsSAcl = (PACL)((BYTE *)(ppSDOut) + dwSize) ;
	dwSize += sizeof(ACL) + dwSizes[3] ;
	AbsDAcl = (PACL)((BYTE *)(ppSDOut) + dwSize) ;

	SetLastError(ERROR_SUCCESS) ;
	MakeAbsoluteSD(*ppSDIn, ppSDOut, &dwSizes[0], AbsDAcl, &dwSizes[4], AbsSAcl,
		&dwSizes[3], AbsOwner, &dwSizes[2], AbsGroup, &dwSizes[1]) ;
	LocalFree(*ppSDIn); *ppSDIn = NULL;
	return ppSDOut;
}


int PrintSecurityDescriptor(SECURITY_DESCRIPTOR *ppSD)
{
	DWORD i = 0;
	PACL pDacl = NULL;
	ACE_HEADER *pAce = NULL;
	TCHAR *UserName = NULL, *ReferencedDomainName = NULL;
	if(ppSD == NULL || ppSD->Revision < SECURITY_DESCRIPTOR_REVISION || ppSD->Control & SE_SELF_RELATIVE)
	{/* Assume that the security descriptor is in absolute form. (the compiler will warn if this isn't the case). */
		SetLastError(ERROR_INVALID_PARAMETER);
		return 0;
	}
	ReferencedDomainName = (TCHAR *)calloc(MAX_USERNAME_LENGTH, sizeof(TCHAR));
	UserName = (TCHAR *)calloc(MAX_USERNAME_LENGTH, sizeof(TCHAR));
	if(!UserName || !ReferencedDomainName)
	{
		free(UserName); UserName = NULL;
		free(ReferencedDomainName); ReferencedDomainName = NULL;
	}

	if(ppSD->Control & SE_DACL_PRESENT)
	{
		pDacl = ppSD->Dacl;
		_tprintf(_T("DACL:"));
		for(i = 0; i < pDacl->AceCount; i++)
		{
			GetAce(pDacl, i, &pAce);
			_tprintf(_T("\r\n"));
			switch(pAce->AceType)
			{
				case ACCESS_ALLOWED_ACE_TYPE:
				{
					ACCESS_ALLOWED_ACE *TypedAce = (ACCESS_ALLOWED_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("allow: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case ACCESS_DENIED_ACE_TYPE:
				{
					ACCESS_DENIED_ACE *TypedAce = (ACCESS_DENIED_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("deny: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case SYSTEM_AUDIT_ACE_TYPE:
				{
					SYSTEM_AUDIT_ACE *TypedAce = (SYSTEM_AUDIT_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("audit: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case SYSTEM_ALARM_ACE_TYPE:
				{
					SYSTEM_ALARM_ACE *TypedAce = (SYSTEM_ALARM_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("alarm: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
				{
					ACCESS_ALLOWED_OBJECT_ACE *TypedAce = (ACCESS_ALLOWED_OBJECT_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("object allow: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case ACCESS_DENIED_OBJECT_ACE_TYPE:
				{
					ACCESS_DENIED_OBJECT_ACE *TypedAce = (ACCESS_DENIED_OBJECT_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("object deny: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
				{
					SYSTEM_AUDIT_OBJECT_ACE *TypedAce = (SYSTEM_AUDIT_OBJECT_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("object audit: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case SYSTEM_ALARM_OBJECT_ACE_TYPE:
				{
					SYSTEM_ALARM_OBJECT_ACE *TypedAce = (SYSTEM_ALARM_OBJECT_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("object alarm: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
				{
					ACCESS_ALLOWED_CALLBACK_ACE *TypedAce = (ACCESS_ALLOWED_CALLBACK_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("allow with callback: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case ACCESS_DENIED_CALLBACK_ACE_TYPE:
				{
					ACCESS_DENIED_CALLBACK_ACE *TypedAce = (ACCESS_DENIED_CALLBACK_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("deny with callback: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case SYSTEM_AUDIT_CALLBACK_ACE_TYPE:
				{
					SYSTEM_AUDIT_CALLBACK_ACE *TypedAce = (SYSTEM_AUDIT_CALLBACK_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("audit with callback: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case SYSTEM_ALARM_CALLBACK_ACE_TYPE:
				{
					SYSTEM_ALARM_CALLBACK_ACE *TypedAce = (SYSTEM_ALARM_CALLBACK_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("alarm with callback: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
				{
					ACCESS_ALLOWED_CALLBACK_OBJECT_ACE *TypedAce = (ACCESS_ALLOWED_CALLBACK_OBJECT_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("object allow with callback: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
				{
					ACCESS_DENIED_CALLBACK_OBJECT_ACE *TypedAce = (ACCESS_DENIED_CALLBACK_OBJECT_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("object deny with callback: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE:
				{
					SYSTEM_AUDIT_CALLBACK_OBJECT_ACE *TypedAce = (SYSTEM_AUDIT_CALLBACK_OBJECT_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("object audit with callback: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE:
				{
					SYSTEM_ALARM_CALLBACK_OBJECT_ACE *TypedAce = (SYSTEM_ALARM_CALLBACK_OBJECT_ACE *)pAce;
					Sid2UserName((PSID)&TypedAce->SidStart, ReferencedDomainName, UserName);
					_tprintf(_T("object alarm with callback: %s\\%s to %8x"), ReferencedDomainName, UserName, TypedAce->Mask);
					break;
				}
				case ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
				default:
				{
					_tprintf(_T("Unknown ACE"));
					break;
				}
			}
		}
	}
	free(UserName); UserName = NULL ;
	free(ReferencedDomainName); ReferencedDomainName = NULL ;
	return 1;
}


PACL RebuildAclFromScratch(SECURITY_DESCRIPTOR *pSecurityDescriptor, void *pAceAdd)
{/** Return a carbon copy of the pSecurityDescriptor's DACL, in LocalAlloc()'ed memory.
*	And we're not allowed to use memcpy().
**/
	PSID SidStart = NULL;
	PACL pOldDacl = pSecurityDescriptor->Dacl, pNewDacl = NULL;
	ACE_HEADER *pAce = NULL;
	DWORD i = 0, n = pSecurityDescriptor->Dacl->AceCount, m = 0, TotalSize = sizeof(ACL);
	/* You should know how many ACEs your DACL contains, otherwise you should reread part 1. */


	/**	First calculate how large your ACE is going to be. Note! We're making this unnecessarily
	*	hard for ourselves! We can just use pSecurityDescriptor->Dacl->AclSize as an alternative.
	**/
	for(i = 0; i < n; i++)
	{
		GetAce(pOldDacl, i, &pAce);
		if( pAce->AceType > ACCESS_MIN_MS_OBJECT_ACE_TYPE &&
			pAce->AceType < ACCESS_MAX_MS_OBJECT_ACE_TYPE)
		{/** HACKHACK: the ACCESS_*_OBJECT_ACEs structures have the same signatures, therefore we can assume that
		*	the ACEs are of the same size. We can use this to simplify the calculations a bit. Don't rely on this
		*	being the case with later Windows versions.
		**/
			SidStart = &((ACCESS_ALLOWED_OBJECT_ACE *)pAce)->SidStart;
			if(!IsValidSid(SidStart))
			{/* skip the entry if the SID is invalid. */
				continue;
			}
			TotalSize += sizeof(ACCESS_ALLOWED_OBJECT_ACE) - sizeof(DWORD);
			TotalSize += GetLengthSid(SidStart);
			m++;	/* increment the object ace count */
		}
		else
		{/** HACKHACK: We're also assuming that the other structures are of the same size.
		*	This is even less likely to be true
		**/
			SidStart = &((ACCESS_ALLOWED_ACE *)pAce)->SidStart;
			if(!IsValidSid(SidStart))
			{/* skip the entry if the SID is invalid. */
				continue;
			}
			TotalSize += sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD);
			TotalSize += GetLengthSid(SidStart);
		}
	}
	/* Add one more ACE type */
	TotalSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(SidStart) - sizeof(DWORD);


	/* Phew!! And all we did was just calculate the length of the ACL! */


	pNewDacl = (PACL)LocalAlloc(LPTR, TotalSize);
	if(pNewDacl == NULL)
	{/* Allocate and initialize a buffer for the ACL. */
		return FALSE;
	}
	InitializeAcl(pNewDacl, TotalSize, ACL_REVISION);

	for(i = 0; i < n; i++)
	{/* look up the deny ACEs */
		GetAce(pOldDacl, i, &pAce);
		switch(pAce->AceType)
		{
			case ACCESS_DENIED_ACE_TYPE:
			case ACCESS_DENIED_OBJECT_ACE_TYPE:
			case ACCESS_DENIED_CALLBACK_ACE_TYPE:
			case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
			{	/* For each deny ace found, add it to the end of the list */
				AddAce(pNewDacl, ACL_REVISION, MAXDWORD, pAce, sizeof(pAce->AceSize));
				break;
			}
			default:
				break;
		}
	}
	for(i = 0; i < n; i++)
	{/* look up the allow ACEs */
		GetAce(pOldDacl, i, &pAce);
		switch(pAce->AceType)
		{
			case ACCESS_DENIED_ACE_TYPE:
			case ACCESS_DENIED_OBJECT_ACE_TYPE:
			case ACCESS_DENIED_CALLBACK_ACE_TYPE:
			case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
				break;
			default:
			{	/* This time add the ACE to the end if it ISN'T a deny ACE */
				AddAce(pNewDacl, ACL_REVISION, MAXDWORD, pAce, pAce->AceSize);
				break;
			}
		}
	}

	/* Finally, append the last ACE onto the end of this ACL */
	AddAce(pNewDacl, ACL_REVISION, MAXDWORD, pAceAdd, pAce->AceSize);


	/* This is a nightmare... you should seriously consider one of the other techniques. */
	return pNewDacl;
}


ACCESS_ALLOWED_ACE *MakeAceToAdd(void)
{
	PSID SidStart = NULL;
	SID_IDENTIFIER_AUTHORITY peUse = SECURITY_NT_AUTHORITY;
	ACCESS_ALLOWED_ACE *pAceAdd = NULL;

	/* You should know how many ACEs your DACL contains, otherwise you should reread part 1. */
	if(!AllocateAndInitializeSid(&peUse, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,
		0, 0, 0, 0, 0, &SidStart))
	{/* We're going to need a Builtin Admins SID */
		return NULL;
	}

	/* We intend to add this ACE to the end of the list. Build up a low level ACE: Admins Full Control. */
	pAceAdd = (ACCESS_ALLOWED_ACE *)calloc(sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(SidStart), 1);
	if(!pAceAdd)
	{
		FreeSid(SidStart); SidStart = NULL;
		return NULL;
	}
	pAceAdd->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
	pAceAdd->Header.AceSize = (WORD)(sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(SidStart));
	pAceAdd->Header.AceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE ;
	pAceAdd->Mask = FILE_GENERIC_READ;
	CopySid(GetLengthSid(SidStart), &pAceAdd->SidStart, SidStart);
	FreeSid(SidStart); SidStart = NULL;

	return pAceAdd;
}


int DoAccessCheck(PSECURITY_DESCRIPTOR OutSecDesc)
{
	HANDLE ProcToken = NULL, ImpersonationToken = NULL;
	PRIVILEGE_SET *PrivilegeSet = NULL;
	BOOL AccessStatus = FALSE;
	DWORD GrantedAccess = 0, PrivilegeSetLength = 0, DesiredAccess = FILE_GENERIC_WRITE;
	GENERIC_MAPPING GenericMapping =
	{
		READ_CONTROL | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA,
		FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_WRITE_DATA | FILE_APPEND_DATA,
		READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_EXECUTE,
		FILE_ALL_ACCESS
	} ;

	if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, TRUE, &ProcToken))
	{
		if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &ProcToken))
		{
			return 1;
		}
	}
	if(!DuplicateToken(ProcToken, SecurityImpersonation, &ImpersonationToken))
	{
		CloseHandle(ProcToken); ProcToken = NULL;
		return 1;
	}

	AccessCheck(OutSecDesc, ImpersonationToken, DesiredAccess, &GenericMapping, NULL, &PrivilegeSetLength,
		&GrantedAccess, &AccessStatus);

	PrivilegeSet = (PPRIVILEGE_SET)calloc(PrivilegeSetLength, 1);
	if(PrivilegeSet == NULL)
	{
		CloseHandle(ProcToken); ProcToken = NULL;
		CloseHandle(ImpersonationToken); ImpersonationToken = NULL;
		return 1;
	}

	AccessCheck(OutSecDesc, ImpersonationToken, DesiredAccess, &GenericMapping,	PrivilegeSet, &PrivilegeSetLength,
		&GrantedAccess, &AccessStatus);
	if(AccessStatus == TRUE)
	{
		_tprintf(_T("%x"), GrantedAccess==DesiredAccess);
	}

	free(PrivilegeSet); PrivilegeSet = NULL;
	CloseHandle(ProcToken); ProcToken = NULL;
	CloseHandle(ImpersonationToken); ImpersonationToken = NULL;
	return 0;
}



int _tmain(void)
{
	PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
	PACL pDacl = NULL;
	ACCESS_ALLOWED_ACE *pAceAdd = NULL;

#ifdef _DEBUG
	_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF |
		_CRTDBG_ALLOC_MEM_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)) ;
#endif /* _DEBUG. This detects memory leaks */

	WellKnownSid2Trustee();
	_tprintf(_T("\r\n"));

	Sid2UserNamePrepare();
	_tprintf(_T("\r\n"));

	_tprintf(_T("%d\r\n"), IsAdminRunning());

	DoWhoAmI();
	_tprintf(_T("\r\n"));

	SetPrivilege(SE_SECURITY_NAME, TRUE);
	{
		pSecurityDescriptor = GetSecurityDesc(_T("G:\\Documents and Settings"));
		pSecurityDescriptor = ConvertSecurityDescriptor(&pSecurityDescriptor);
		PrintSecurityDescriptor(pSecurityDescriptor);
		_tprintf(_T("\r\n"));

		pAceAdd = MakeAceToAdd();
		_tprintf(_T("\r\n"));

		pDacl = RebuildAclFromScratch(pSecurityDescriptor, pAceAdd);
		_tprintf(_T("\r\n"));
		DoAccessCheck(pSecurityDescriptor);
		_tprintf(_T("\r\n"));

		/* Free the buffers */
		free(pAceAdd) ; pAceAdd = NULL;

		LocalFree(pDacl); pDacl = NULL;
		LocalFree(pSecurityDescriptor); pSecurityDescriptor = NULL;
	}
	SetPrivilege(SE_SECURITY_NAME, FALSE);
	return 0;
}

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)

Share

About the Author

oshah
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 | :) .

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150723.1 | Last Updated 27 Jun 2005
Article Copyright 2005 by oshah
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid