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
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);
|
|
}
|