windows-nt/Source/XPSP1/NT/ds/security/base/keymgr/systok.cpp

939 lines
21 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//
// systok.cpp
//
// Copyright (c) Microsoft Corp, 1998
//
// This file contains source code for testing protected storage's key
// backup and recovery capabilities under a real world scenario, by creating
// a local user account, performing a data protection operation, and then
// change the pwd, then performing data unprotect, and comparing the data.
//
// History:
//
// Todds 8/15/98 Created
//
#include <windows.h>
#include <winbase.h>
#include <stdio.h>
#include <stdlib.h>
#include <wincrypt.h>
#include <userenv.h>
#include <lm.h>
#include <psapi.h>
#include "switches.h"
#define TERROR(msg) LogError(__FILE__, __LINE__, msg)
#define TERRORVAL(msg, val) LogErrorVal(__FILE__, __LINE__, msg, val)
#define TCOMMENT(msg) LogComment(msg)
#define MyAlloc(cb) HeapAlloc(GetProcessHeap(), 0, cb)
#define MyFree(pv) HeapFree(GetProcessHeap(), 0, pv)
#define WSZ_BYTECOUNT(s) (2 * wcslen(s) + 2)
#define CHECK_NULL(s) if (s == NULL) \
LogError(__FILE__, __LINE__, L"## CHECK_NULL ##")
#define MAX_BLOBS 20
#define MAX_PROCESSES 200
#define MAX_SD 2048
#define BLOB_INCREMENT 0x4001 // 1 page + 1 byte...
//
// Error Logging Functions # defined as follows to include
// Line and FILE macros:
//
// TERROR - LogError()
// TERRORVAL - LogErrorVal()
//
//
void
LogError(LPSTR szFile,
int iLine,
LPWSTR wszMsg)
{
//
// Event log or testutil logging later...
//
WCHAR buffer[512];
swprintf(buffer, L"ERROR Line: %i -> %s\n", iLine, wszMsg);
OutputDebugStringW(buffer);
wprintf(buffer);
}
void
LogErrorVal(LPSTR szFile,
int iLine,
LPWSTR wszMsg,
DWORD dwVal)
{
WCHAR buffer[256]; // this should be adequate.
swprintf(buffer, L"%s Error:: %x", wszMsg, dwVal);
LogError(szFile, iLine, buffer);
}
void
LogComment(LPWSTR wszMsg)
{
//
// Event log or testutil logging later...
//
WCHAR buffer[512];
OutputDebugStringW(wszMsg);
wprintf(wszMsg);
}
void
DumpBin(CRYPT_DATA_BLOB hash)
{
WCHAR buff[256], out[256];
ULONG cb;
swprintf(out, L"");
while (hash.cbData > 0) {
cb = min(4, hash.cbData);
hash.cbData -= cb;
for (; cb > 0; cb--, hash.pbData++) {
swprintf(buff, L"%02X", *hash.pbData);
wcscat(out, buff);
}
wcscat(out, L" ");
}
wcscat(out, L"\n");
TCOMMENT(out);
}
//
// SetSidOnAcl
//
BOOL
SetSidOnAcl(
PSID pSid,
PACL pAclSource,
PACL *pAclDestination,
DWORD AccessMask,
BYTE AceFlags,
BOOL bAddSid
)
{
ACL_SIZE_INFORMATION AclInfo;
DWORD dwNewAclSize, dwErr = S_OK;
LPVOID pAce;
DWORD AceCounter;
BOOL bSuccess=FALSE; // assume this function will fail
//
// If we were given a NULL Acl, just provide a NULL Acl
//
if(pAclSource == NULL) {
*pAclDestination = NULL;
return TRUE;
}
if(!IsValidSid(pSid)) return FALSE;
if(!GetAclInformation(
pAclSource,
&AclInfo,
sizeof(ACL_SIZE_INFORMATION),
AclSizeInformation
)) return FALSE;
//
// compute size for new Acl, based on addition or subtraction of Ace
//
if(bAddSid) {
dwNewAclSize=AclInfo.AclBytesInUse +
sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(pSid) -
sizeof(DWORD) ;
}
else {
dwNewAclSize=AclInfo.AclBytesInUse -
sizeof(ACCESS_ALLOWED_ACE) -
GetLengthSid(pSid) +
sizeof(DWORD) ;
}
*pAclDestination = (PACL)MyAlloc(dwNewAclSize);
if(*pAclDestination == NULL)
{
TERRORVAL(L"Alloc failed!", E_OUTOFMEMORY);
return FALSE;
}
//
// initialize new Acl
//
if(!InitializeAcl(
*pAclDestination,
dwNewAclSize,
ACL_REVISION
)){
dwErr = GetLastError();
TERRORVAL(L"InitilizeAcl failed!", dwErr);
goto ret;
}
//
// if appropriate, add ace representing pSid
//
if(bAddSid) {
PACCESS_ALLOWED_ACE pNewAce;
if(!AddAccessAllowedAce(
*pAclDestination,
ACL_REVISION,
AccessMask,
pSid
)) {
dwErr = GetLastError();
TERRORVAL(L"AddAccessAllowedAce failed!", dwErr);
goto ret;
}
//
// get pointer to ace we just added, so we can change the AceFlags
//
if(!GetAce(
*pAclDestination,
0, // this is the first ace in the Acl
(void**) &pNewAce
)){
dwErr = GetLastError();
TERRORVAL(L"GetAce failed!", dwErr);
goto ret;
}
pNewAce->Header.AceFlags = AceFlags;
}
//
// copy existing aces to new Acl
//
for(AceCounter = 0 ; AceCounter < AclInfo.AceCount ; AceCounter++) {
//
// fetch existing ace
//
if(!GetAce(pAclSource, AceCounter, &pAce)){
dwErr = GetLastError();
TERRORVAL(L"GetAce failed!", dwErr);
goto ret;
}
//
// check to see if we are removing the Ace
//
if(!bAddSid) {
//
// we only care about ACCESS_ALLOWED aces
//
if((((PACE_HEADER)pAce)->AceType) == ACCESS_ALLOWED_ACE_TYPE) {
PSID pTempSid=(PSID)&((PACCESS_ALLOWED_ACE)pAce)->SidStart;
//
// if the Sid matches, skip adding this Sid
//
if(EqualSid(pSid, pTempSid)) continue;
}
}
//
// append ace to Acl
//
if(!AddAce(
*pAclDestination,
ACL_REVISION,
MAXDWORD, // maintain Ace order
pAce,
((PACE_HEADER)pAce)->AceSize
)) {
dwErr = GetLastError();
TERRORVAL(L"AddAccessAllowedAce failed!", dwErr);
goto ret;
}
}
bSuccess=TRUE; // indicate success
ret:
//
// free memory if an error occurred
//
if(!bSuccess) {
if(*pAclDestination != NULL)
MyFree(*pAclDestination);
}
return bSuccess;
}
//
// AddSIDToKernelObject()
//
// This function takes a given SID and dwAccess and adds it to a given token.
//
// ** Be sure to restore old kernel object
// ** using call to GetKernelObjectSecurity()
//
BOOL
AddSIDToKernelObjectDacl(PSID pSid,
DWORD dwAccess,
HANDLE OriginalToken,
PSECURITY_DESCRIPTOR* ppSDOld)
{
PSECURITY_DESCRIPTOR pSD = NULL;
SECURITY_DESCRIPTOR sdNew;
DWORD cbByte = MAX_SD, cbNeeded = 0, dwErr = 0;
PACL pOldDacl = NULL, pNewDacl = NULL;
BOOL fDaclPresent, fDaclDefaulted, fRet = FALSE;
pSD = (PSECURITY_DESCRIPTOR) MyAlloc(cbByte);
if (NULL == pSD) {
TERRORVAL(L"Alloc failed!", E_OUTOFMEMORY);
return FALSE;
}
if (!InitializeSecurityDescriptor(
&sdNew,
SECURITY_DESCRIPTOR_REVISION
)) {
dwErr = GetLastError();
TERRORVAL(L"InitializeSecurityDescriptor failed!", dwErr);
goto ret;
}
if (!GetKernelObjectSecurity(
OriginalToken,
DACL_SECURITY_INFORMATION,
pSD,
cbByte,
&cbNeeded
)) {
dwErr = GetLastError();
if (cbNeeded > MAX_SD && dwErr == ERROR_MORE_DATA) {
MyFree(pSD);
pSD = NULL;
pSD = (PSECURITY_DESCRIPTOR) MyAlloc(cbNeeded);
if (NULL == pSD) {
TERRORVAL(L"Alloc failed!", E_OUTOFMEMORY);
dwErr = E_OUTOFMEMORY;
goto ret;
}
dwErr = S_OK;
if (!GetKernelObjectSecurity(
OriginalToken,
DACL_SECURITY_INFORMATION,
pSD,
cbNeeded,
&cbNeeded
)) {
dwErr = GetLastError();
}
}
if (dwErr != S_OK) {
TERRORVAL(L"GetKernelObjectSecurity failed!", dwErr);
goto ret;
}
}
if (!GetSecurityDescriptorDacl(
pSD,
&fDaclPresent,
&pOldDacl,
&fDaclDefaulted
)) {
dwErr = GetLastError();
TERRORVAL(L"GetSecurityDescriptorDacl failed!", dwErr);
goto ret;
}
if (!SetSidOnAcl(
pSid,
pOldDacl,
&pNewDacl,
dwAccess,
0,
TRUE
)) {
goto ret;
}
if (!SetSecurityDescriptorDacl(
&sdNew,
TRUE,
pNewDacl,
FALSE
)) {
dwErr = GetLastError();
TERRORVAL(L"SetSecurityDescriptorDacl failed!", dwErr);
goto ret;
}
if (!SetKernelObjectSecurity(
OriginalToken,
DACL_SECURITY_INFORMATION,
&sdNew
)) {
dwErr = GetLastError();
TERRORVAL(L"SetKernelObjectSecurity failed!", dwErr);
goto ret;
}
*ppSDOld = pSD;
fRet = TRUE;
ret:
if (NULL != pNewDacl) {
MyFree(pNewDacl);
}
if (!fRet) {
if (NULL != pSD) {
MyFree(pSD);
*ppSDOld = NULL;
}
}
return fRet;
}
//
// DataFree()
//
// Utility for freeing array of DATA_BLOB structs
//
void
DataFree(DATA_BLOB* arDataBlob,
BOOL fCryptAlloc)
{
if (arDataBlob == NULL) return; // not alloc'd
for (DWORD i = 0; i < MAX_BLOBS;i++) {
if (arDataBlob[i].pbData != NULL) {
if (!fCryptAlloc) {
MyFree(arDataBlob[i].pbData);
} else { // Data member alloc'd by DataProtect call
LocalFree(arDataBlob[i].pbData);
}
}
}
MyFree(arDataBlob);
}
BOOL
SetPrivilege(
HANDLE hToken, // token handle
LPCTSTR Privilege, // Privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
TOKEN_PRIVILEGES tpPrevious;
DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);
if(!LookupPrivilegeValue( NULL, Privilege, &luid )) return FALSE;
//
// first pass. get current privilege setting
//
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
&tpPrevious,
&cbPrevious
);
if (GetLastError() != ERROR_SUCCESS) return FALSE;
//
// second pass. set privilege based on previous setting
//
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;
if(bEnablePrivilege) {
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
}
else {
tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
tpPrevious.Privileges[0].Attributes);
}
AdjustTokenPrivileges(
hToken,
FALSE,
&tpPrevious,
cbPrevious,
NULL,
NULL
);
if (GetLastError() != ERROR_SUCCESS) return FALSE;
return TRUE;
}
BOOL
SetCurrentPrivilege(
LPCTSTR Privilege, // Privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
BOOL bSuccess=FALSE; // assume failure
HANDLE hToken;
if (!OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
FALSE,
&hToken)){
if(!OpenProcessToken(
GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
&hToken
)) return FALSE;
}
if(SetPrivilege(hToken, Privilege, bEnablePrivilege)) bSuccess=TRUE;
CloseHandle(hToken);
return bSuccess;
}
//
// GetUserSid
//
// This function takes a token, and returns the user SID from that token.
//
// Note: SID must be freed by MyFree()
// hToken is optional... NULL means we'll grab it.
//
extern "C" BOOL
GetUserSid(HANDLE hClientToken,
PSID* ppSid,
DWORD* lpcbSid)
{
DWORD cbUserInfo = 0;
PTOKEN_USER pUserInfo = NULL;
PUCHAR pnSubAuthorityCount = 0;
DWORD cbSid = 0;
BOOL fRet = FALSE;
HANDLE hToken = hClientToken;
*ppSid = NULL;
if (NULL == hClientToken) {
if (!OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY,
FALSE,
&hToken
)) {
// not impersonating, use process token...
if (!OpenProcessToken(
GetCurrentProcess(),
TOKEN_QUERY,
&hToken
)) {
TERRORVAL(L"OpenProcessToken failed!", GetLastError());
return FALSE;
}
}
}
// this will fail, usually w/ ERROR_INSUFFICIENT_BUFFER
GetTokenInformation(
hToken,
TokenUser,
NULL,
0,
&cbUserInfo
);
pUserInfo = (PTOKEN_USER) MyAlloc(cbUserInfo);
if (NULL == pUserInfo) {
TERRORVAL(L"ALLOC FAILURE!", E_OUTOFMEMORY);
return FALSE;
}
if (!GetTokenInformation(
hToken,
TokenUser,
pUserInfo,
cbUserInfo,
&cbUserInfo
)) {
TERRORVAL(L"GetTokenInformation failed!", GetLastError());
goto ret;
}
//
// Now that we've got the SID AND ATTRIBUTES struct, get the SID lenght,
// alloc room, and return *just the SID*
//
if (!IsValidSid(pUserInfo->User.Sid)) goto ret;
pnSubAuthorityCount = GetSidSubAuthorityCount(pUserInfo->User.Sid);
cbSid = GetSidLengthRequired(*pnSubAuthorityCount);
*ppSid = (PSID) MyAlloc(cbSid);
if (NULL == *ppSid ) {
TERRORVAL(L"Alloc failed!", E_OUTOFMEMORY);
goto ret;
}
if (!CopySid(
cbSid,
*ppSid,
pUserInfo->User.Sid
)) {
TERRORVAL(L"CopySid failed!", GetLastError());
goto copyerr;
}
*lpcbSid = cbSid; // may be useful later on...
fRet = TRUE;
ret:
if (NULL == hClientToken && NULL != hToken) { // supplied our own
CloseHandle(hToken);
}
if (NULL != pUserInfo) {
MyFree(pUserInfo);
}
return fRet;
copyerr:
if (NULL != *ppSid) {
MyFree(*ppSid);
*ppSid = NULL;
}
goto ret;
}
//
// IsLocalSystem()
// This function makes the determination if the given process token
// is running as local system.
//
BOOL
IsLocalSystem(HANDLE hToken)
{
PSID pLocalSid = NULL, pTokenSid = NULL;
SID_IDENTIFIER_AUTHORITY IDAuthorityNT = SECURITY_NT_AUTHORITY;
DWORD cbSid = 0;
BOOL fRet = FALSE;
if (!GetUserSid(
hToken,
&pTokenSid,
&cbSid
)) {
goto ret;
}
if (!AllocateAndInitializeSid(
&IDAuthorityNT,
1,
SECURITY_LOCAL_SYSTEM_RID,
0,0,0,0,0,0,0,
&pLocalSid
)) {
TERRORVAL(L"AllocateAndInitializeSid failed!", GetLastError());
goto ret;
}
if (EqualSid(pLocalSid, pTokenSid)) {
fRet = TRUE; // got one!
}
ret:
if (NULL != pTokenSid) {
MyFree(pTokenSid);
}
if (NULL != pLocalSid) {
FreeSid(pLocalSid);
}
return fRet;
}
//
// GetLocalSystemToken()
//
// This function grabs a process token from a LOCAL SYSTEM process and uses it
// to run as local system for the duration of the test
//
extern "C" DWORD
GetLocalSystemToken(HANDLE* phRet)
{
HANDLE hProcess = NULL;
HANDLE hPToken = NULL, hPTokenNew = NULL, hPDupToken = NULL;
DWORD rgPIDs[MAX_PROCESSES], cbNeeded = 0, dwErr = S_OK, i = 0;
DWORD cbrgPIDs = sizeof(DWORD) * MAX_PROCESSES;
PSECURITY_DESCRIPTOR pSD = NULL;
PSID pSid = NULL;
DWORD cbSid = 0;
BOOL fSet = FALSE;
// SLOW BUFFERs
BYTE rgByte[MAX_SD], rgByte2[MAX_SD];
DWORD cbByte = MAX_SD, cbByte2 = MAX_SD;
*phRet = NULL;
if(!SetCurrentPrivilege(SE_DEBUG_NAME, TRUE)) {
TERROR(L"SetCurrentPrivilege (debug) failed!");
return E_FAIL;
}
if(!SetCurrentPrivilege(SE_TAKE_OWNERSHIP_NAME, TRUE)) {
TERROR(L"SetCurrentPrivilege (to) failed!");
return E_FAIL;
}
if (!EnumProcesses(
rgPIDs,
cbrgPIDs,
&cbNeeded
)) {
dwErr = GetLastError();
TERRORVAL(L"EnumProcesses failed!", dwErr);
goto ret;
}
//
// Get current user's sid for use in expanding SD.
//
if (!GetUserSid(
NULL,
&pSid,
&cbSid
)) {
goto ret;
}
//
// Walk processes until we find one that's running as
// local system
//
for (i = 1; i < (cbNeeded / sizeof(DWORD)); i++) {
hProcess = OpenProcess(
PROCESS_ALL_ACCESS,
FALSE,
rgPIDs[i]
);
if (NULL == hProcess) {
dwErr = GetLastError();
TERRORVAL(L"OpenProcess failed!", dwErr);
goto ret;
}
if (!OpenProcessToken(
hProcess,
READ_CONTROL | WRITE_DAC,
&hPToken
)) {
dwErr = GetLastError();
TERRORVAL(L"OpenProcessToken failed!", dwErr);
goto ret;
}
//
// We've got a token, but we can't use it for
// TOKEN_DUPLICATE access. So, instead, we'll go
// ahead and whack the DACL on the object to grant us
// this access, and get a new token.
// **** BE SURE TO RESTORE hProcess to Original SD!!! ****
//
if (!AddSIDToKernelObjectDacl(
pSid,
TOKEN_DUPLICATE,
hPToken,
&pSD
)) {
goto ret;
}
fSet = TRUE;
if (!OpenProcessToken(
hProcess,
TOKEN_DUPLICATE,
&hPTokenNew
)) {
dwErr = GetLastError();
TERRORVAL(L"OpenProcessToken failed!", dwErr);
goto ret;
}
//
// Duplicate the token
//
if (!DuplicateTokenEx(
hPTokenNew,
TOKEN_ALL_ACCESS,
NULL,
SecurityImpersonation,
TokenPrimary,
&hPDupToken
)) {
dwErr = GetLastError();
TERRORVAL(L"DuplicateToken failed!", dwErr);
goto ret;
}
if (IsLocalSystem(hPDupToken)) {
*phRet = hPDupToken;
break; // found a local system token
}
// Loop cleanup
if (!SetKernelObjectSecurity(
hPToken,
DACL_SECURITY_INFORMATION,
pSD
)) {
dwErr = GetLastError();
TERRORVAL(L"SetKernelObjectSecurity failed!", dwErr);
goto ret;
}
fSet = FALSE;
if (NULL != hPDupToken) {
CloseHandle(hPDupToken);
hPDupToken = NULL;
}
if (NULL != pSD) {
MyFree(pSD);
pSD = NULL;
}
if (NULL != hPToken) {
CloseHandle(hPToken);
hPToken = NULL;
}
if (NULL != hProcess) {
CloseHandle(hProcess);
hProcess = NULL;
}
} // ** FOR **
ret:
//***** REMEMBER TO RESTORE ORIGINAL SD TO OBJECT*****
if (fSet) {
if (!SetKernelObjectSecurity(
hPToken,
DACL_SECURITY_INFORMATION,
pSD
)) {
dwErr = GetLastError();
TERRORVAL(L"SetKernelObjectSecurity failed (cleanup)!", dwErr);
}
}
if (NULL != pSid) {
MyFree(pSid);
}
if (NULL != hPToken) {
CloseHandle(hPToken);
}
if (NULL != pSD) {
MyFree(pSD);
}
if (NULL != hProcess) {
CloseHandle(hProcess);
}
return dwErr;
}