//+--------------------------------------------------------------------------- // // 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 #include #include #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); }