Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2119 lines
55 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: lsa.cxx
//
// Contents:
//
// Classes:
//
// Functions: None.
//
// History: 15-May-96 MarkBl Created
//
//----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#pragma hdrstop
#include <ntsecapi.h>
#include <mstask.h>
#include <msterr.h>
#include "lsa.hxx"
#include "debug.hxx"
BYTE grgbDeletedEntryMarker[] =
{ 'D', 'E', 'L', 'E', 'T', 'E', 'D', '_', 'E', 'N', 'T', 'R', 'Y' };
static WCHAR gwszSAI[] = L"SAI";
static WCHAR gwszSAC[] = L"SAC";
//+---------------------------------------------------------------------------
//
// Function: ReadSecurityDBase
//
// Synopsis:
//
// Arguments: [pcbSAI] --
// [ppbSAI] --
// [pcbSAC] --
// [ppbSAC] --
//
// Notes: None.
//
//----------------------------------------------------------------------------
HRESULT
ReadSecurityDBase(
DWORD * pcbSAI,
BYTE ** ppbSAI,
DWORD * pcbSAC,
BYTE ** ppbSAC)
{
HRESULT hr;
*ppbSAI = *ppbSAC = NULL;
// Read the SAC.
//
hr = ReadLsaData(sizeof(gwszSAC), gwszSAC, pcbSAC, ppbSAC);
if (SUCCEEDED(hr))
{
// Read the SAI.
//
hr = ReadLsaData(sizeof(gwszSAI), gwszSAI, pcbSAI, ppbSAI);
}
if (SUCCEEDED(hr))
{
//
// Check the sizes. For sizes greater than zero, but less than the
// header size, deallocate the memory and zero the returned sizes,
// ptrs.
//
// This seems inefficient, but it saves quite a few checks in the
// SAC/SAI API.
//
if (*pcbSAI && *pcbSAI <= SAI_HEADER_SIZE)
{
*pcbSAI = 0;
LocalFree(*ppbSAI);
*ppbSAI = NULL;
}
if (*pcbSAC && *pcbSAC <= SAC_HEADER_SIZE)
{
*pcbSAC = 0;
LocalFree(*ppbSAC);
*ppbSAC = NULL;
}
//
// Ensure the databases are in sync. The first DWORD is a USN (Update
// Sequence Number). Its value increases monotonically for every
// write to the LSA. The SAI & SAC USN values must be equal. If not,
// they are out of sync with each other - an unrecoverable problem.
// Also check the SAI SetArrayCount vs. the SAC CredentialCount as
// these values must also be equal or the they are out of sync.
//
if (((*ppbSAI != NULL && *ppbSAC == NULL) ||
(*ppbSAI == NULL && *ppbSAC != NULL)) ||
(*ppbSAI != NULL && *ppbSAC != NULL &&
((DWORD)**ppbSAI != (DWORD)**ppbSAC || (DWORD)*(*ppbSAI + USN_SIZE) != (DWORD)*(*ppbSAC + USN_SIZE))
)
)
{
schAssert(0 && "Scheduling Agent security database out of sync!");
hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
}
}
if (FAILED(hr))
{
if (*ppbSAI != NULL) LocalFree(*ppbSAI);
if (*ppbSAC != NULL) LocalFree(*ppbSAC);
*ppbSAI = *ppbSAC = NULL;
}
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: WriteSecurityDBase
//
// Synopsis:
//
// Arguments: [cbSAI] --
// [pbSAI] --
// [cbSAC] --
// [pbSAC] --
//
// Notes: None.
//
//----------------------------------------------------------------------------
HRESULT
WriteSecurityDBase(
DWORD cbSAI,
BYTE * pbSAI,
DWORD cbSAC,
BYTE * pbSAC)
{
HRESULT hr;
//
// Just in case...
//
if (!pbSAI || !pbSAC)
{
schAssert(0 && "NULL pointers passed to WriteSecurityDBase!");
return(E_FAIL);
}
//
// If the secrets are out-of-sync, don't write them -- this will help preserve the integrity of the db.
// Assert on checked builds so we know there still is a problem that leads to out-of-sync condition.
//
if ((DWORD)*(pbSAI + USN_SIZE) != (DWORD)*(pbSAC + USN_SIZE))
{
schAssert(0 && "Scheduling Agent security database SetArrayCount and CredentialCount out of sync!");
return(E_FAIL);
}
// read the previous SAI so that we can put it back in case of failure
DWORD cbSAIold;
BYTE* pbSAIold = NULL;
hr = ReadLsaData(sizeof(gwszSAI), gwszSAI, &cbSAIold, &pbSAIold);
if (FAILED(hr))
return hr;
//
// Advance the USN (Update Sequence Numbers) on the SAI & SAC. They
// should always remain equal. Otherwise, they'll be out of sync
// with each other - an unrecoverable problem.
//
(DWORD)(*pbSAI)++;
(DWORD)(*pbSAC)++;
// Write the SAI.
//
hr = WriteLsaData(sizeof(gwszSAI), gwszSAI, cbSAI, pbSAI);
if (SUCCEEDED(hr))
{
// Write the SAC.
//
hr = WriteLsaData(sizeof(gwszSAC), gwszSAC, cbSAC, pbSAC);
// attempt to put it back in case of failure.
// if this fails, there's not much we can do; the db is invalid either way
// if it succeeds, then we've got a good db again
// even though this *function* has failed to record the updated db
if (FAILED(hr))
WriteLsaData(sizeof(gwszSAI), gwszSAI, cbSAIold, pbSAIold);
}
if (pbSAIold)
LocalFree(pbSAIold);
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: ReadLsaData
//
// Synopsis:
//
// Arguments: [cbKey] --
// [pwszKey] --
// [pcbData] --
// [ppbData] --
//
// Notes: None.
//
//----------------------------------------------------------------------------
HRESULT
ReadLsaData(WORD cbKey, LPCWSTR pwszKey, DWORD * pcbData, BYTE ** ppbData)
{
LSA_OBJECT_ATTRIBUTES ObjectAttributes = {
sizeof(LSA_OBJECT_ATTRIBUTES),
NULL,
NULL,
0L,
NULL,
NULL
};
HANDLE hPolicy = NULL;
LSA_UNICODE_STRING sKey;
PLSA_UNICODE_STRING psData;
NTSTATUS Status;
//
// UNICODE_STRING length fields are in bytes and include the NULL
// terminator
//
sKey.Length = cbKey;
sKey.MaximumLength = cbKey;
sKey.Buffer = (LPWSTR)pwszKey;
//
// Open the LSA.
//
Status = LsaOpenPolicy(NULL,
&ObjectAttributes,
POLICY_GET_PRIVATE_INFORMATION,
&hPolicy);
if (!(Status >= 0))
{
return(E_FAIL);
}
//
// Retrieve the LSA data associated with the key passed.
//
Status = LsaRetrievePrivateData(hPolicy, &sKey, &psData);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND ||
(Status >= 0 && psData == NULL))
{
LsaClose(hPolicy);
*pcbData = 0;
*ppbData = NULL;
return(S_FALSE);
}
else if (!(Status >= 0))
{
LsaClose(hPolicy);
return(E_FAIL);
}
LsaClose(hPolicy);
//
// Create a copy of the LSA data to return. Why? The LSA private data
// is callee allocated, so we are not free to reallocate the memory
// as-needed.
//
BYTE * pbData = (BYTE *)LocalAlloc(LMEM_FIXED, psData->Length);
if (pbData == NULL)
{
LsaFreeMemory(psData);
return(E_OUTOFMEMORY);
}
HRESULT hr = S_OK;
//
// Wrapping in a try/except in case the data read from the LSA is bad.
//
__try
{
CopyMemory(pbData, psData->Buffer, psData->Length);
//
// Update out ptrs.
//
// NB : Making the assignment in here to save on an rc check.
//
*pcbData = psData->Length;
*ppbData = pbData;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
schAssert(0 &&
"Exception reading Scheduling Agent security database!");
hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
}
LsaFreeMemory(psData);
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: WriteLsaData
//
// Synopsis:
//
// Arguments: [cbKey] --
// [pwszKey] --
// [cbData] --
// [pbData] --
//
// Notes: None.
//
//----------------------------------------------------------------------------
HRESULT
WriteLsaData(WORD cbKey, LPCWSTR pwszKey, DWORD cbData, BYTE * pbData)
{
LSA_OBJECT_ATTRIBUTES ObjectAttributes = {
sizeof(LSA_OBJECT_ATTRIBUTES),
NULL,
NULL,
0L,
NULL,
NULL
};
HANDLE hPolicy = NULL;
LSA_UNICODE_STRING sKey;
LSA_UNICODE_STRING sData;
NTSTATUS Status;
//
// UNICODE_STRING length fields are in bytes and include the NULL
// terminator
//
sKey.Length = cbKey;
sKey.MaximumLength = cbKey;
sKey.Buffer = (LPWSTR)pwszKey;
sData.Length = (WORD)cbData;
sData.MaximumLength = (WORD)cbData;
sData.Buffer = (WCHAR *)pbData;
//
// Open the LSA.
//
Status = LsaOpenPolicy(NULL,
&ObjectAttributes,
POLICY_CREATE_SECRET,
&hPolicy);
if (!(Status >= 0))
{
return(E_FAIL);
}
//
// Write the LSA data associated with the key passed.
//
Status = LsaStorePrivateData(hPolicy, &sKey, &sData);
if (!(Status >= 0))
{
LsaClose(hPolicy);
return(E_FAIL);
}
LsaClose(hPolicy);
return(S_OK);
}
//+---------------------------------------------------------------------------
//
// Function: DeleteLsaData
//
// Synopsis:
//
// Arguments: [cbKey] --
// [pwszKey] --
//
//----------------------------------------------------------------------------
HRESULT
DeleteLsaData(WORD cbKey, LPCWSTR pwszKey)
{
LSA_OBJECT_ATTRIBUTES ObjectAttributes = {
sizeof(LSA_OBJECT_ATTRIBUTES),
NULL,
NULL,
0L,
NULL,
NULL
};
HANDLE hPolicy = NULL;
LSA_UNICODE_STRING sKey;
NTSTATUS Status;
//
// UNICODE_STRING length fields are in bytes and include the NULL
// terminator
//
sKey.Length = cbKey;
sKey.MaximumLength = cbKey;
sKey.Buffer = (LPWSTR)pwszKey;
//
// Open the LSA.
//
Status = LsaOpenPolicy(NULL,
&ObjectAttributes,
POLICY_CREATE_SECRET,
&hPolicy);
if (!(Status >= 0))
{
return(E_FAIL);
}
//
// Specifying NULL as the data causes LSA to delete the secret for that key
//
Status = LsaStorePrivateData(hPolicy, &sKey, NULL);
if (!(Status >= 0))
{
LsaClose(hPolicy);
return(E_FAIL);
}
LsaClose(hPolicy);
return(S_OK);
}
//+---------------------------------------------------------------------------
//
// Function: SACAddCredential
//
// Synopsis:
//
// Arguments: [pbCredentialIdentity] --
// [cbCredential] --
// [pbCredential] --
// [pcbSAC] --
// [ppbSAC] --
//
// Notes: try/except unnecessary here. Memory writes are guaranteed to
// remain within the buffer allocated.
//
//----------------------------------------------------------------------------
HRESULT
SACAddCredential(
BYTE * pbCredentialIdentity,
DWORD cbEncryptedData,
BYTE * pbEncryptedData,
DWORD * pcbSAC,
BYTE ** ppbSAC)
{
DWORD dwCredentialCount = 1;
DWORD cbCredentialSize = HASH_DATA_SIZE + cbEncryptedData;
//
// Make room for the new credential.
//
DWORD cbSACNew;
BYTE * pbSACNew;
cbSACNew = *pcbSAC + sizeof(cbCredentialSize) + cbCredentialSize;
//
// Check for maximum size. The LSA handles at most 64K.
//
if (cbSACNew > MAX_SECRET_SIZE)
{
// BUGBUG : Create a new error code for this error, something like
// SCHED_E_CRED_LIMIT_EXCEEDED: "The system limit on the storage space
// for task account information has been reached."
return(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
}
if (*pcbSAC == 0)
{
cbSACNew += SAC_HEADER_SIZE;
pbSACNew = (BYTE *)LocalAlloc(LMEM_FIXED, cbSACNew);
if (pbSACNew == NULL)
{
return(E_OUTOFMEMORY);
}
//
// Zero out the header.
//
SecureZeroMemory(pbSACNew, SAC_HEADER_SIZE);
}
else
{
pbSACNew = (BYTE *)LocalReAlloc(*ppbSAC, cbSACNew, LMEM_MOVEABLE);
if (pbSACNew == NULL)
{
return(E_OUTOFMEMORY);
}
}
//
// Adjust total credential count & prepare to write the credential.
//
BYTE * pbCredentialSizePos;
if (*pcbSAC == 0)
{
//
// First entry.
// - Write entry after header.
// - Initialize credential count to one (in declaration above).
//
pbCredentialSizePos = pbSACNew + SAC_HEADER_SIZE;
}
else
{
//
// Append entry.
// - Append after last credential entry.
// - Increase credential count by one.
//
pbCredentialSizePos = pbSACNew + *pcbSAC;
CopyMemory(&dwCredentialCount, pbSACNew + USN_SIZE,
sizeof(dwCredentialCount));
dwCredentialCount++;
}
BYTE * pbCredentialIdentityPos;
pbCredentialIdentityPos = pbCredentialSizePos + sizeof(cbCredentialSize);
//
// Update total credential count.
//
CopyMemory(pbSACNew + USN_SIZE, &dwCredentialCount,
sizeof(dwCredentialCount));
// Write total credential size, excluding the size value itself.
//
CopyMemory(pbCredentialSizePos, &cbCredentialSize,
sizeof(cbCredentialSize));
// Write credential identity.
//
CopyMemory(pbCredentialIdentityPos, pbCredentialIdentity,
HASH_DATA_SIZE);
// Finally, write encrypted credentials.
//
CopyMemory(pbCredentialIdentityPos + HASH_DATA_SIZE, pbEncryptedData,
cbEncryptedData);
// Update out pointers.
//
*pcbSAC = cbSACNew;
*ppbSAC = pbSACNew;
return(S_OK);
}
//+---------------------------------------------------------------------------
//
// Function: SACIndexCredential
//
// Synopsis:
//
// Arguments: [dwCredentialIndex] --
// [cbSAC] --
// [pbSAC] --
// [pcbCredential] --
// [ppbFoundCredential] --
//
// Notes: try/except unnecesssary here as checks exist to ensure we
// remain within the buffer passed.
//
//----------------------------------------------------------------------------
HRESULT
SACIndexCredential(
DWORD dwCredentialIndex,
DWORD cbSAC,
BYTE * pbSAC,
DWORD * pcbCredential,
BYTE ** ppbFoundCredential)
{
HRESULT hr = S_FALSE;
if (ppbFoundCredential != NULL) *ppbFoundCredential = NULL;
if (cbSAC <= SAC_HEADER_SIZE || pbSAC == NULL)
{
return(hr);
}
BYTE * pbSACEnd = pbSAC + cbSAC;
BYTE * pb = pbSAC + USN_SIZE; // Advance past USN.
//
// Read credential count.
//
DWORD dwCredentialCount;
CopyMemory(&dwCredentialCount, pb, sizeof(dwCredentialCount));
pb += sizeof(dwCredentialCount);
//
// Seek to credential index within the credential array.
//
DWORD cbCredentialSize;
for (DWORD i = 0; (i < dwCredentialIndex) && (i < dwCredentialCount) &&
((DWORD)(pb - pbSAC) < cbSAC); i++)
{
//
// Advance to next credential.
//
// First, ensure sufficient space remains in the buffer.
//
if ((pb + sizeof(cbCredentialSize)) > pbSACEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
CopyMemory(&cbCredentialSize, pb, sizeof(cbCredentialSize));
pb += sizeof(cbCredentialSize) + cbCredentialSize;
}
if ((i == dwCredentialIndex) && (i < dwCredentialCount) &&
((DWORD)(pb - pbSAC) < cbSAC))
{
//
// Found it, but ensure the contents referenced are valid.
// Do so by checking remaining size.
//
CopyMemory(&cbCredentialSize, pb, sizeof(cbCredentialSize));
pb += sizeof(cbCredentialSize);
if ((pb + cbCredentialSize) <= (pbSAC + cbSAC))
{
// Set the credential & credential size return ptrs.
//
*pcbCredential = cbCredentialSize;
// Optionally return a ptr to the credential.
//
if (ppbFoundCredential != NULL)
{
*ppbFoundCredential = pb;
}
hr = S_OK;
}
else
{
ASSERT_SECURITY_DBASE_CORRUPT();
hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
}
}
else if ((i != dwCredentialCount) || ((DWORD)(pb - pbSAC) != cbSAC))
{
//
// The database appears to be truncated.
//
ASSERT_SECURITY_DBASE_CORRUPT();
hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
}
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: SACFindCredential
//
// Synopsis:
//
// Arguments: [pbCredentialIdentity] --
// [cbSAC] --
// [pbSAC] --
// [pdwCredentialIndex] --
// [pcbEncryptedData] --
// [ppbFoundCredential] --
//
// Notes: try/except unnecesssary here as checks exist to ensure we
// remain within the buffer passed.
//
//----------------------------------------------------------------------------
HRESULT
SACFindCredential(
BYTE * pbCredentialIdentity,
DWORD cbSAC,
BYTE * pbSAC,
DWORD * pdwCredentialIndex,
DWORD * pcbEncryptedData,
BYTE ** ppbFoundCredential)
{
HRESULT hr = S_FALSE;
if (ppbFoundCredential != NULL) *ppbFoundCredential = NULL;
if (cbSAC <= SAC_HEADER_SIZE || pbSAC == NULL)
{
return(hr);
}
BYTE * pbSACEnd = pbSAC + cbSAC;
BYTE * pb = pbSAC + USN_SIZE; // Advance past USN.
//
// Read credential count.
//
DWORD dwCredentialCount;
CopyMemory(&dwCredentialCount, pb, sizeof(dwCredentialCount));
pb += sizeof(dwCredentialCount);
//
// Iterate the SAC credentials for a match against the passed credential
// identity.
//
DWORD cbCredentialSize;
for (DWORD i = 0; (i < dwCredentialCount) &&
((DWORD)(pb - pbSAC) < cbSAC); i++)
{
//
// Ensure sufficient space remains in the buffer.
//
if ((pb + sizeof(cbCredentialSize)) > pbSACEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
CopyMemory(&cbCredentialSize, pb, sizeof(cbCredentialSize));
pb += sizeof(cbCredentialSize);
//
// Check remaining buffer size prior to the comparison.
//
if ((pb + HASH_DATA_SIZE) > pbSACEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
BOOL fFound;
fFound = (memcmp(pb, pbCredentialIdentity, HASH_DATA_SIZE) == 0);
pb += HASH_DATA_SIZE;
cbCredentialSize -= HASH_DATA_SIZE; // Subtract identity size.
// Equals the encrypted data
// size.
if (fFound)
{
//
// Found it, but ensure the contents referenced are valid.
// Do so by checking remaining size.
//
if ((pb + cbCredentialSize) > pbSACEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
*pcbEncryptedData = cbCredentialSize;
*pdwCredentialIndex = i;
if (ppbFoundCredential != NULL)
{
*ppbFoundCredential = pb;
}
return(S_OK);
}
//
// Advance to next credential.
//
pb += cbCredentialSize;
}
if ((i == dwCredentialCount) && ((DWORD)(pb - pbSAC) != cbSAC) ||
(i != dwCredentialCount) && ((DWORD)(pb - pbSAC) > cbSAC))
{
//
// The database appears to be truncated.
//
ASSERT_SECURITY_DBASE_CORRUPT();
hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
}
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: SACRemoveCredential
//
// Synopsis:
//
// Arguments: [CredentialIndex] --
// [pcbSAC] --
// [ppbSAC] --
//
// Returns: TBD
//
// Notes: try/except unnecessary here since SACIndexCredential will
// return only valid buffer ptrs.
//
//----------------------------------------------------------------------------
HRESULT
SACRemoveCredential(
DWORD CredentialIndex,
DWORD * pcbSAC,
BYTE ** ppbSAC)
{
DWORD cbCredential;
BYTE * pbCredential;
HRESULT hr;
//
// Index the credential in the SAC.
//
hr = SACIndexCredential(CredentialIndex, *pcbSAC, *ppbSAC, &cbCredential,
&pbCredential);
if (hr == S_FALSE)
{
return(SCHED_E_ACCOUNT_INFORMATION_NOT_SET);
}
else if (FAILED(hr))
{
return(hr);
}
// Overwrite credential with SAC remaining buffer.
//
BYTE * pbDest = pbCredential - sizeof(cbCredential);
BYTE * pbSrc = pbCredential + cbCredential;
MoveMemory(pbDest, pbSrc, (*ppbSAC + *pcbSAC) - pbSrc);
// Decrement SAC credential count.
//
DWORD dwCredentialCount;
CopyMemory(&dwCredentialCount, *ppbSAC + USN_SIZE,
sizeof(dwCredentialCount));
--dwCredentialCount;
CopyMemory(*ppbSAC + USN_SIZE, &dwCredentialCount,
sizeof(dwCredentialCount));
DWORD cbSACNew = *pcbSAC - (cbCredential + sizeof(cbCredential));
// Shrink SAC buffer memory with a realloc.
//
BYTE * pbSACNew = (BYTE *)LocalReAlloc(*ppbSAC, cbSACNew, LMEM_MOVEABLE);
if (pbSACNew == NULL)
{
return(E_OUTOFMEMORY);
}
// Update return ptrs.
//
*pcbSAC = cbSACNew;
*ppbSAC = pbSACNew;
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: SACUpdateCredential
//
// Synopsis:
//
// Arguments: [cbEncryptedData] --
// [pbEncryptedData] --
// [cbPrevCredential] --
// [pbPrevCredential] --
// [pcbSAC] --
// [ppbSAC] --
//
// Notes: try/except unnecesssary here as checks exist to ensure we
// remain within the buffer passed.
//
//----------------------------------------------------------------------------
HRESULT
SACUpdateCredential(
DWORD cbEncryptedData,
BYTE * pbEncryptedData,
DWORD cbPrevCredential,
BYTE * pbPrevCredential, // Indexes *ppbSAC.
DWORD * pcbSAC,
BYTE ** ppbSAC)
{
DWORD cbNewCredential = HASH_DATA_SIZE + cbEncryptedData;
BYTE * pbSACNew;
//
// Ensure the prev credential ptr is within the buffer boundaries.
// This is probably a redundant check since this ptr was most likely
// obtained from a call to SACIndex/FindCredential.
//
if (*pcbSAC < SAC_HEADER_SIZE ||
pbPrevCredential < (*ppbSAC + SAC_HEADER_SIZE +
sizeof(cbNewCredential)) ||
(pbPrevCredential + cbPrevCredential) > (*ppbSAC + *pcbSAC))
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
if (cbNewCredential != cbPrevCredential)
{
//
// Reallocate to either shrink or grow the SAC data.
//
DWORD cbSACNew;
BYTE * pbDest;
BYTE * pbSrc;
if (cbNewCredential > cbPrevCredential)
{
//
// Credential is larger than the previous. Grow the
// buffer. Must reallocate the buffer FIRST, then
// relocate contents.
//
cbSACNew = *pcbSAC + (cbNewCredential - cbPrevCredential);
//
// Keep SAC size in check.
//
if (cbSACNew > MAX_SECRET_SIZE)
{
// BUGBUG : use new SCHED_E_CRED_LIMIT_EXCEEDED, as above
return(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
}
//
// Save the linear offset to the previous credential
// from SAC start, in case realloc changes our ptr.
//
DWORD cbPrevCredentialOffset;
cbPrevCredentialOffset = (DWORD)(pbPrevCredential - *ppbSAC);
pbSACNew = (BYTE *)LocalReAlloc(*ppbSAC, cbSACNew, LMEM_MOVEABLE);
if (pbSACNew == NULL)
{
return(E_OUTOFMEMORY);
}
pbPrevCredential = pbSACNew + cbPrevCredentialOffset;
//
// Compute start and ending block ptrs for subsequent
// move.
//
pbDest = pbPrevCredential + cbNewCredential;
pbSrc = pbPrevCredential + cbPrevCredential;
//
// Move remaining buffer up.
//
BYTE * pbSACEnd = pbSACNew + *pcbSAC;
if (pbDest < pbSACEnd)
{
MoveMemory(pbDest, pbSrc, pbSACEnd - pbSrc);
}
}
else
{
//
// Credential is smaller than the previous. Shrink the
// buffer. Must relocate buffer contents FIRST, then
// realloc.
//
cbSACNew = *pcbSAC - (cbPrevCredential - cbNewCredential);
//
// Compute start and ending block ptrs for subsequent
// move.
//
pbDest = pbPrevCredential + cbNewCredential;
pbSrc = pbPrevCredential + cbPrevCredential;
//
// Move remaining buffer down.
//
MoveMemory(pbDest, pbSrc, (*ppbSAC + *pcbSAC) - pbSrc);
pbSACNew = (BYTE *)LocalReAlloc(*ppbSAC, cbSACNew, LMEM_MOVEABLE);
if (pbSACNew == NULL)
{
return(E_OUTOFMEMORY);
}
}
// Update out pointers.
//
*pcbSAC = cbSACNew;
*ppbSAC = pbSACNew;
}
//
// Finally, update the credential.
//
// Write the credential size.
//
CopyMemory(pbPrevCredential - sizeof(cbNewCredential), &cbNewCredential,
sizeof(cbNewCredential));
// No need to update the credential identity. It has not changed.
//
// Write the encrypted bits.
//
CopyMemory(pbPrevCredential + HASH_DATA_SIZE, pbEncryptedData,
cbEncryptedData);
return(S_OK);
}
//+---------------------------------------------------------------------------
//
// Function: SAIAddIdentity
//
// Synopsis:
//
// Arguments: [pbIdentity] --
// [pcbSAI] --
// [ppbSAI] --
//
// Notes: try/except unnecessary here. Memory writes are guaranteed to
// remain within the buffer allocated.
//
//----------------------------------------------------------------------------
HRESULT
SAIAddIdentity(
BYTE * pbIdentity,
DWORD * pcbSAI,
BYTE ** ppbSAI)
{
//
// Make room for the new identity.
//
DWORD dwSetArrayCount = 1;
DWORD dwSetSubCount = 1; // Equal to one in the case of addition.
DWORD cbSAINew;
BYTE * pbSAINew;
cbSAINew = *pcbSAI + sizeof(dwSetSubCount) + HASH_DATA_SIZE;
//
// Check for maximum size. The LSA handles at most 64K.
//
if (cbSAINew > MAX_SECRET_SIZE)
{
// BUGBUG : use new SCHED_E_CRED_LIMIT_EXCEEDED, as above
return(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
}
if (*pcbSAI == 0)
{
cbSAINew += SAI_HEADER_SIZE;
pbSAINew = (BYTE *)LocalAlloc(LMEM_FIXED, cbSAINew);
if (pbSAINew == NULL)
{
return(E_OUTOFMEMORY);
}
//
// Zero out the header.
//
SecureZeroMemory(pbSAINew, SAI_HEADER_SIZE);
}
else
{
pbSAINew = (BYTE *)LocalReAlloc(*ppbSAI, cbSAINew, LMEM_MOVEABLE);
if (pbSAINew == NULL)
{
return(E_OUTOFMEMORY);
}
}
//
// Write identity set subcount of one & write identity.
//
BYTE * pbSetSubCount;
if (*pcbSAI == 0)
{
//
// First entry.
// - Write entry after header.
// - Initialize set array count to one.
//
pbSetSubCount = pbSAINew + SAI_HEADER_SIZE;
CopyMemory(pbSAINew + USN_SIZE, &dwSetArrayCount,
sizeof(dwSetArrayCount));
}
else
{
//
// Append entry.
// - Append after last identity array entry.
// - Increase set array count by one.
//
pbSetSubCount = pbSAINew + *pcbSAI;
CopyMemory(&dwSetArrayCount, pbSAINew + USN_SIZE,
sizeof(dwSetArrayCount));
dwSetArrayCount++;
CopyMemory(pbSAINew + USN_SIZE, &dwSetArrayCount,
sizeof(dwSetArrayCount));
}
CopyMemory(pbSetSubCount, &dwSetSubCount, sizeof(dwSetSubCount));
CopyMemory(pbSetSubCount + sizeof(dwSetSubCount), pbIdentity,
HASH_DATA_SIZE);
// Update out ptrs.
//
*pcbSAI = cbSAINew;
*ppbSAI = pbSAINew;
return(S_OK);
}
//+---------------------------------------------------------------------------
//
// Function: SAIFindIdentity
//
// Synopsis:
//
// Arguments: [pbIdentity] --
// [cbSAI] --
// [pbSAI] --
// [pdwCredentialIndex] --
// [pfIsPasswordNull] --
// [ppbFoundIdentity] --
// [pdwSetSubCount] --
// [ppbSet] --
//
// Notes: try/except unnecesssary here as checks exist to ensure we
// remain within the buffer passed.
//
//----------------------------------------------------------------------------
HRESULT
SAIFindIdentity(
BYTE * pbIdentity,
DWORD cbSAI,
BYTE * pbSAI,
DWORD * pdwCredentialIndex,
BOOL * pfIsPasswordNull,
BYTE ** ppbFoundIdentity,
DWORD * pdwSetSubCount,
BYTE ** ppbSet)
{
HRESULT hr = S_FALSE;
if (ppbFoundIdentity != NULL) *ppbFoundIdentity = NULL;
if (cbSAI <= SAI_HEADER_SIZE || pbSAI == NULL)
{
return(hr);
}
*pdwCredentialIndex = 0;
if (pdwSetSubCount != NULL) *pdwSetSubCount = 0;
if (ppbSet != NULL) *ppbSet = NULL;
BYTE * pbSAIEnd = pbSAI + cbSAI;
BYTE * pb = pbSAI + USN_SIZE; // Advance past USN.
//
// Read identity set array count.
//
DWORD dwSetArrayCount;
CopyMemory(&dwSetArrayCount, pb, sizeof(dwSetArrayCount));
pb += sizeof(dwSetArrayCount);
//
// Iterative identity comparison.
//
DWORD dwSetSubCount;
for (DWORD i = 0;
(i < dwSetArrayCount) && ((DWORD)(pb - pbSAI) < cbSAI);
i++)
{
//
// Read identity set subcount.
//
// First, ensure sufficient space remains in the buffer.
//
if ((pb + sizeof(dwSetSubCount)) > pbSAIEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
BYTE * pbSet;
CopyMemory(&dwSetSubCount, pb, sizeof(dwSetSubCount));
pbSet = (pb += sizeof(dwSetSubCount));
for (DWORD j = 0;
(j < dwSetSubCount) && ((DWORD)(pb - pbSAI) < cbSAI);
j++, pb += HASH_DATA_SIZE)
{
//
// Check remaining buffer size prior to the comparison.
//
if ((pb + HASH_DATA_SIZE) > pbSAIEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
//
// We consider the hashed data to be equal even if the last bit
// is different
//
if (memcmp(pb, pbIdentity, HASH_DATA_SIZE - 1) == 0 &&
((LAST_HASH_BYTE(pb) ^ LAST_HASH_BYTE(pbIdentity)) & 0xFE) == 0)
{
//
// Found it. No need to further check return ptrs. The
// buffer size check above accomplished this.
//
*pdwCredentialIndex = i;
if (pfIsPasswordNull != NULL)
{
// Unequal last bits denote a NULL password
*pfIsPasswordNull = LAST_HASH_BYTE(pb) ^ LAST_HASH_BYTE(pbIdentity);
}
if (pdwSetSubCount != NULL)
{
*pdwSetSubCount = dwSetSubCount;
}
if (ppbSet != NULL)
{
*ppbSet = pbSet;
}
if (ppbFoundIdentity != NULL)
{
*ppbFoundIdentity = pb;
}
return(S_OK);
}
}
//
// Check for database truncation.
//
if ((j != dwSetSubCount) || ((DWORD)(pb - pbSAI) > cbSAI))
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
}
//
// Check for database truncation.
//
if ((i != dwSetArrayCount) || ((DWORD)(pb - pbSAI) != cbSAI))
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: SAIIndexIdentity
//
// Synopsis:
//
// Arguments: [cbSAI] --
// [pbSAI] --
// [dwSetArrayIndex] --
// [dwSetIndex] --
// [pdwSetSubCount] --
// [ppbSet] --
//
// Notes: try/except unnecesssary here as checks exist to ensure we
// remain within the buffer passed.
//
//----------------------------------------------------------------------------
HRESULT
SAIIndexIdentity(
DWORD cbSAI,
BYTE * pbSAI,
DWORD dwSetArrayIndex,
DWORD dwSetIndex,
BYTE ** ppbFoundIdentity,
DWORD * pdwSetSubCount,
BYTE ** ppbSet)
{
HRESULT hr = S_FALSE;
if (ppbFoundIdentity != NULL) *ppbFoundIdentity = NULL;
if (cbSAI <= SAI_HEADER_SIZE || pbSAI == NULL)
{
return(hr);
}
if (pdwSetSubCount != NULL) *pdwSetSubCount = 0;
if (ppbSet != NULL) *ppbSet = NULL;
BYTE * pbSAIEnd = pbSAI + cbSAI;
BYTE * pb = pbSAI + USN_SIZE; // Advance past USN.
//
// Read identity array count.
//
DWORD dwSetArrayCount;
CopyMemory(&dwSetArrayCount, pb, sizeof(dwSetArrayCount));
pb += sizeof(dwSetArrayCount);
//
// Iterative identity comparison.
//
for (DWORD i = 0; (i < dwSetArrayCount) &&
((DWORD)(pb - pbSAI) < cbSAI); i++)
{
DWORD dwSetSubCount;
//
// Read identity set subcount.
// Note, this value may not be on an aligned boundary.
//
// First, ensure sufficient space remains in the buffer.
//
if ((pb + sizeof(dwSetSubCount)) > pbSAIEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
BYTE * pbSet;
CopyMemory(&dwSetSubCount, pb, sizeof(dwSetSubCount));
pbSet = (pb += sizeof(dwSetSubCount));
DWORD j;
for (j = 0; (j < dwSetSubCount) && ((DWORD)(pb - pbSAI) < cbSAI);
j++, pb += HASH_DATA_SIZE)
{
if (i == dwSetArrayIndex && j == dwSetIndex)
{
//
// Found it, but ensure the contents referenced are valid.
// Do so by checking remaining size.
//
if ((pb + HASH_DATA_SIZE) > pbSAIEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
if (pdwSetSubCount != NULL)
{
*pdwSetSubCount = dwSetSubCount;
}
if (ppbSet != NULL)
{
*ppbSet = pbSet;
}
if (ppbFoundIdentity != NULL)
{
*ppbFoundIdentity = pb;
}
return(S_OK);
}
}
//
// Check for database truncation.
//
if ((j != dwSetSubCount) || ((DWORD)(pb - pbSAI) > cbSAI))
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
}
//
// Check for database truncation.
//
if ((i != dwSetArrayCount) || ((DWORD)(pb - pbSAI) != cbSAI))
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: SAIInsertIdentity
//
// Synopsis:
//
// Arguments: [pbIdentity] --
// [pbSAIIndex] --
// [pcbSAI] --
// [ppbSAI] --
//
// Notes: try/except unnecesssary here as checks exist to ensure we
// remain within the buffer passed.
//
//----------------------------------------------------------------------------
HRESULT
SAIInsertIdentity(
BYTE * pbIdentity,
BYTE * pbSAIIndex, // Indexes *ppbSAI.
DWORD * pcbSAI,
BYTE ** ppbSAI)
{
DWORD dwSetSubCount;
//
// Check index boundary.
//
if (pbSAIIndex < (*ppbSAI + SAI_HEADER_SIZE + sizeof(dwSetSubCount)) ||
(pbSAIIndex + HASH_DATA_SIZE) > (*ppbSAI + *pcbSAI))
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
//
// Save the linear offset to the identity insertion point from SAI start,
// in case realloc changes our ptr.
//
DWORD cbInsertionOffset = (DWORD)(pbSAIIndex - *ppbSAI);
//
// Make room for the new identity.
//
DWORD cbSAINew = *pcbSAI + HASH_DATA_SIZE;
BYTE * pbSAINew;
//
// Check for maximum size. The LSA handles at most 64K.
//
if (cbSAINew > MAX_SECRET_SIZE)
{
// BUGBUG : use new SCHED_E_CRED_LIMIT_EXCEEDED, as above
return(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
}
if (*pcbSAI == 0)
{
cbSAINew += SAI_HEADER_SIZE;
pbSAINew = (BYTE *)LocalAlloc(LMEM_FIXED, cbSAINew);
if (pbSAINew == NULL)
{
return(E_OUTOFMEMORY);
}
//
// Zero out the header.
//
SecureZeroMemory(pbSAINew, SAI_HEADER_SIZE);
}
else
{
pbSAINew = (BYTE *)LocalReAlloc(*ppbSAI, cbSAINew, LMEM_MOVEABLE);
if (pbSAINew == NULL)
{
return(E_OUTOFMEMORY);
}
}
pbSAIIndex = pbSAINew + cbInsertionOffset;
//
// Move buffer content down.
//
BYTE * pbSetSubCount = pbSAIIndex - sizeof(dwSetSubCount);
BYTE * pbIdentityStart = pbSAIIndex;
MoveMemory(pbIdentityStart + HASH_DATA_SIZE, pbIdentityStart,
(pbSAINew + *pcbSAI) - pbIdentityStart);
//
// Update identity count & write new identity.
//
CopyMemory(&dwSetSubCount, pbSetSubCount, sizeof(dwSetSubCount));
dwSetSubCount++;
CopyMemory(pbSetSubCount, &dwSetSubCount, sizeof(dwSetSubCount));
CopyMemory(pbIdentityStart, pbIdentity, HASH_DATA_SIZE);
// Update out ptrs.
//
*pcbSAI = cbSAINew;
*ppbSAI = pbSAINew;
return(S_OK);
}
//+---------------------------------------------------------------------------
//
// Function: SAIRemoveIdentity
//
// Synopsis:
//
// Arguments: [pbIdentity] --
// [pbSet] --
// [pcbSAI] --
// [ppbSAI] --
// [CredentialIndex] --
// [pcbSAC] --
// [ppbSAC] --
//
// Returns: TBD
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT
SAIRemoveIdentity(
BYTE * pbIdentity,
BYTE * pbSet,
DWORD * pcbSAI,
BYTE ** ppbSAI,
DWORD CredentialIndex,
DWORD * pcbSAC,
BYTE ** ppbSAC)
{
HRESULT hr = S_OK;
DWORD dwSetSubCount;
//
// Check identity, set ptr values. If this fails, it is either a developer
// error (hence, the assertion) or the database is hosed. In either case,
// return an error vs. writing blindly to memory.
//
if ((pbSet > pbIdentity) ||
(pbSet < (*ppbSAI + SAI_HEADER_SIZE + sizeof(dwSetSubCount))) ||
((pbIdentity + HASH_DATA_SIZE) > (*ppbSAI + *pcbSAI)))
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
BYTE * pbSetSubCount;
//
// Read and decrement identity array count.
//
pbSetSubCount = pbSet - sizeof(dwSetSubCount);
CopyMemory(&dwSetSubCount, pbSetSubCount, sizeof(dwSetSubCount));
--dwSetSubCount;
//
// If this is the last identity in the set,
// overwrite the set count value & the identity with remaining SAI
// buffer content;
// remove associated credential from the SAC.
// If this is not the last entry,
// overwrite the identity with the remaining SAI buffer content &
// decrement the identity set count.
//
BYTE * pbDest, * pbSrc;
DWORD cbSAINew;
if (dwSetSubCount == 0) // Last entry.
{
// Remove associated credential in the SAC.
//
hr = SACRemoveCredential(CredentialIndex, pcbSAC, ppbSAC);
if (SUCCEEDED(hr))
{
// Overwrite identity set with SAI remaining buffer.
// Includes the set array count and the single identity
// element. Actual move accomplished following this condition.
//
pbDest = pbSetSubCount;
pbSrc = pbSet + HASH_DATA_SIZE;
cbSAINew = *pcbSAI - (HASH_DATA_SIZE + sizeof(dwSetSubCount));
// Decrement SAI identity set count. That is, the count of
// identity sets in the SAI. Note, overloading dwSetSubCount.
//
CopyMemory(&dwSetSubCount, *ppbSAI + USN_SIZE,
sizeof(dwSetSubCount));
--dwSetSubCount;
CopyMemory(*ppbSAI + USN_SIZE, &dwSetSubCount,
sizeof(dwSetSubCount));
}
}
else // More entries remain.
{
// Overwrite identity with SAI remaining buffer.
// Actual move accomplished following this condition.
//
pbDest = pbIdentity;
pbSrc = pbIdentity + HASH_DATA_SIZE;
cbSAINew = *pcbSAI - HASH_DATA_SIZE;
// Update identity set array count to reflect removed entry.
//
CopyMemory(pbSetSubCount, &dwSetSubCount, sizeof(dwSetSubCount));
}
if (SUCCEEDED(hr))
{
MoveMemory(pbDest, pbSrc, (*ppbSAI + *pcbSAI) - pbSrc);
// Shrink SAI buffer memory with a realloc.
//
BYTE * pbSAINew = (BYTE *)LocalReAlloc(*ppbSAI, cbSAINew,
LMEM_MOVEABLE);
if (pbSAINew != NULL)
{
// Update return ptrs.
//
*pcbSAI = cbSAINew;
*ppbSAI = pbSAINew;
}
else
{
hr = E_OUTOFMEMORY;
}
}
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: SAIUpdateIdentity
//
// Synopsis: Updates the hash data stored for a job in place.
//
// Arguments: [pbNewIdentity] -- the new hash data to be stored
// [pbFoundIdentity] -- pointer to the previously found hash data
// [cbSAI] --
// [pbSAI] --
//
// Returns: S_OK
// E_OUTOFMEMORY
// SCHED_E_ACCOUNT_DBASE_CORRUPT
//
// Notes: None.
//
//----------------------------------------------------------------------------
HRESULT
SAIUpdateIdentity(
const BYTE * pbNewIdentity,
BYTE * pbFoundIdentity,
DWORD cbSAI,
BYTE * pbSAI)
{
schAssert(pbSAI <= pbFoundIdentity && pbFoundIdentity < pbSAI + cbSAI);
UNREFERENCED_PARAMETER(cbSAI);
UNREFERENCED_PARAMETER(pbSAI);
CopyMemory(pbFoundIdentity, pbNewIdentity, HASH_DATA_SIZE);
return S_OK;
}
//+---------------------------------------------------------------------------
//
// Function: SAICoalesceDeletedEntries
//
// Synopsis: Removed entries marked for deletion and reallocate the buffer.
//
// Arguments: [pcbSAI] --
// [ppbSAI] --
//
// Returns: S_OK
// E_OUTOFMEMORY
// SCHED_E_ACCOUNT_DBASE_CORRUPT
//
// Notes: None.
//
//----------------------------------------------------------------------------
HRESULT
SAICoalesceDeletedEntries(
DWORD * pcbSAI,
BYTE ** ppbSAI)
{
schAssert(pcbSAI != NULL && ppbSAI != NULL && *ppbSAI != NULL);
if (*pcbSAI <= SAI_HEADER_SIZE)
{
//
// Nothing to do.
//
return(S_OK);
}
//
// Read set array count.
//
DWORD cbSAINew = *pcbSAI;
DWORD cSetsRemoved = 0;
DWORD dwSetArrayCount;
DWORD dwSetSubCount;
DWORD cEntriesDeleted;
BYTE * pb;
BYTE * pbSetArrayCount;
BYTE * pbSAIEnd = *ppbSAI + *pcbSAI;
BYTE * pbSAINew;
BYTE * pbDest;
BYTE * pbSrc;
pb = pbSetArrayCount = *ppbSAI + USN_SIZE;
CopyMemory(&dwSetArrayCount, pbSetArrayCount, sizeof(dwSetArrayCount));
pb += sizeof(dwSetArrayCount);
for (DWORD i = 0; i < dwSetArrayCount && pb < pbSAIEnd; i++)
{
if ((pb + sizeof(dwSetSubCount)) > pbSAIEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
CopyMemory(&dwSetSubCount, pb, sizeof(dwSetSubCount));
pbDest = pb;
pb += sizeof(dwSetSubCount);
pbSrc = pb;
//
// Must scan the set to see if all entries are to be removed.
// To know if the set subcount can be removed as well.
//
cEntriesDeleted = 0;
for (DWORD j = 0; j < dwSetSubCount && pbSrc < pbSAIEnd; j++)
{
//
// Deleted entry marker size is less than HASH_DATA_SIZE.
//
if ((pbSrc + HASH_DATA_SIZE) > pbSAIEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
if (DELETED_ENTRY(pbSrc))
{
cEntriesDeleted++;
}
pbSrc += HASH_DATA_SIZE;
}
//
// Anything to remove?
//
if (cEntriesDeleted != 0)
{
//
// Reduce SAI size by the total no. of deleted entries.
// After the above, we can safely dispense with buffer boundary
// checks.
//
DWORD cbBytesDeleted = (HASH_DATA_SIZE * cEntriesDeleted);
if (cEntriesDeleted == dwSetSubCount)
{
//
// Removing entire set.
// Update total no. of sets removed.
//
cSetsRemoved++;
cbBytesDeleted += sizeof(dwSetSubCount);
MoveMemory(pbDest, pbSrc, pbSAIEnd - pbSrc);
}
else
{
//
// Removing individual set entries.
// First, update the set array count.
// pbDest is positioned on the set subcount, pb just after it.
//
dwSetSubCount -= cEntriesDeleted;
CopyMemory(pbDest, &dwSetSubCount, sizeof(dwSetSubCount));
pbDest = pbSrc = pb;
for ( ; cEntriesDeleted && pbSrc < pbSAIEnd; )
{
pbSrc += HASH_DATA_SIZE;
if (DELETED_ENTRY(pbDest))
{
cEntriesDeleted--;
MoveMemory(pbDest, pbSrc, pbSAIEnd - pbSrc);
pbSrc = pbDest;
}
pbDest = pbSrc;
}
//
// Advance to next set.
//
pbDest = pb + (HASH_DATA_SIZE * dwSetSubCount);
}
cbSAINew -= cbBytesDeleted;
pbSAIEnd -= cbBytesDeleted;
}
else
{
//
// Advance to next set.
//
pbDest += (HASH_DATA_SIZE * dwSetSubCount) +
sizeof(dwSetSubCount);
}
pb = pbDest;
}
//
// Fix up set array count to reflect removed sets.
//
dwSetArrayCount -= cSetsRemoved;
CopyMemory(pbSetArrayCount, &dwSetArrayCount, sizeof(dwSetArrayCount));
//
// Finally, reallocate the array. That is, if it changed.
//
if (*pcbSAI != cbSAINew)
{
pbSAINew = (BYTE *)LocalReAlloc(*ppbSAI, cbSAINew, LMEM_MOVEABLE);
if (pbSAINew != NULL)
{
// Update return ptrs.
//
*pcbSAI = cbSAINew;
*ppbSAI = pbSAINew;
}
else
{
return(E_OUTOFMEMORY);
}
}
return(S_OK);
}
//+---------------------------------------------------------------------------
//
// Function: SACCoalesceDeletedEntries
//
// Synopsis: Removed entries marked for deletion and reallocate the buffer.
//
// Arguments: [pcbSAC] --
// [ppbSAC] --
//
// Returns: S_OK
// E_OUTOFMEMORY
// SCHED_E_ACCOUNT_DBASE_CORRUPT
//
// Notes: None.
//
//----------------------------------------------------------------------------
HRESULT
SACCoalesceDeletedEntries(
DWORD * pcbSAC,
BYTE ** ppbSAC)
{
schAssert(pcbSAC != NULL && ppbSAC != NULL && *ppbSAC != NULL);
if (*pcbSAC <= SAC_HEADER_SIZE)
{
//
// Nothing to do.
//
return(S_OK);
}
BYTE * pb;
BYTE * pbCredentialCount;
DWORD dwCredentialCount;
DWORD cbCredentialSize;
DWORD cCredentialsRemoved = 0;
DWORD cbSACNew = *pcbSAC;
BYTE * pbSACNew;
BYTE * pbSACEnd = *ppbSAC + *pcbSAC;
BYTE * pbSrc;
BYTE * pbDest;
BYTE * pbNext;
DWORD cbBytesDeleted;
//
// Read credential count.
//
pb = pbCredentialCount = *ppbSAC + USN_SIZE;
CopyMemory(&dwCredentialCount, pbCredentialCount,
sizeof(dwCredentialCount));
pb += sizeof(dwCredentialCount);
for (DWORD i = 0; i < dwCredentialCount && pb < pbSACEnd; i++)
{
if ((pb + sizeof(cbCredentialSize)) > pbSACEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
cbBytesDeleted = 0;
pbDest = pbSrc = pb;
//
// Move consecutive entries.
//
for ( ; i < dwCredentialCount && pb < pbSACEnd; i++)
{
CopyMemory(&cbCredentialSize, pb, sizeof(cbCredentialSize));
pb += sizeof(cbCredentialSize);
pbNext = pb + cbCredentialSize;
//
// A credential could never be less than the deleted entry size,
// unless it is bogus.
//
if (cbCredentialSize < DELETED_ENTRY_MARKER_SIZE ||
(pb + DELETED_ENTRY_MARKER_SIZE) > pbSACEnd)
{
ASSERT_SECURITY_DBASE_CORRUPT();
return(SCHED_E_ACCOUNT_DBASE_CORRUPT);
}
if (DELETED_ENTRY(pb))
{
//
// Update the new SAC size to reflect the removed entry.
// Also update the total no. of credentials removed.
//
cbBytesDeleted += sizeof(cbCredentialSize) + cbCredentialSize;
cCredentialsRemoved++;
pbSrc = pb = pbNext;
}
else
{
pb = pbNext;
break;
}
}
if (pbDest != pbSrc)
{
MoveMemory(pbDest, pbSrc, pbSACEnd - pbSrc);
pb = pbDest + sizeof(cbCredentialSize) + cbCredentialSize;
cbSACNew -= cbBytesDeleted;
pbSACEnd -= cbBytesDeleted;
}
}
//
// Fix up credential count to reflect removed sets.
//
dwCredentialCount -= cCredentialsRemoved;
CopyMemory(pbCredentialCount, &dwCredentialCount,
sizeof(dwCredentialCount));
//
// Finally, reallocate the array. That is, if it changed.
//
if (*pcbSAC != cbSACNew)
{
pbSACNew = (BYTE *)LocalReAlloc(*ppbSAC, cbSACNew, LMEM_MOVEABLE);
if (pbSACNew != NULL)
{
// Update return ptrs.
//
*pcbSAC = cbSACNew;
*ppbSAC = pbSACNew;
}
else
{
return(E_OUTOFMEMORY);
}
}
return(S_OK);
}