windows-nt/Source/XPSP1/NT/ds/security/csps/pincache/pincache.c

399 lines
11 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*
* PinCache.c
*/
#include <windows.h>
#include "pincache.h"
#if defined(DBG) || defined(DEBUG)
#define DebugPrint(a) (OutputDebugString(a))
#if TEST_DEBUG
#include <stdio.h>
#define CROW 8
void PCPrintBytes(LPSTR pszHdr, BYTE *pb, DWORD cbSize)
{
ULONG cb, i;
CHAR rgsz[1024];
sprintf(rgsz, "\n %s, %d bytes ::\n", pszHdr, cbSize);
DebugPrint(rgsz);
while (cbSize > 0)
{
// Start every row with an extra space
DebugPrint(" ");
cb = min(CROW, cbSize);
cbSize -= cb;
for (i = 0; i < cb; i++)
sprintf(rgsz + (3*i), " %02x", pb[i]);
DebugPrint(rgsz);
for (i = cb; i < CROW; i++)
DebugPrint(" ");
DebugPrint(" '");
for (i = 0; i < cb; i++)
{
if (pb[i] >= 0x20 && pb[i] <= 0x7f)
sprintf(rgsz+i, "%c", pb[i]);
else
sprintf(rgsz+i, ".");
}
sprintf(rgsz+i, "\n");
DebugPrint(rgsz);
pb += cb;
}
}
BOOL MyGetTokenInformation(
HANDLE TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
LPVOID TokenInformation,
DWORD TokenInformationLength,
PDWORD ReturnLength);
#define GetTokenInformation(A, B, C, D, E) MyGetTokenInformation(A, B, C, D, E)
#define TestDebugPrint(a) (OutputDebugString(a))
#else
#define TestDebugPrint(a)
#endif // TEST_DEBUG
#else
#define DebugPrint(a)
#define TestDebugPrint(a)
#endif // DBG || DEBUG
typedef struct _PINCACHEITEM
{
LUID luid;
PBYTE pbPin;
DWORD cbPin;
} PINCACHEITEM, *PPINCACHEITEM;
#define CacheAlloc(X) (HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, X))
#define CacheFree(X) (HeapFree(GetProcessHeap(), 0, X))
#define INIT_PIN_ATTACK_SLEEP 3000 // milliseconds
#define MAX_PIN_ATTACK_SLEEP 24000 // milliseconds
#define MAX_FREE_BAD_TRIES 3
/**
* Function: PinCacheFlush
*/
void PinCacheFlush(
IN OUT PINCACHE_HANDLE *phCache)
{
PPINCACHEITEM pCache = (PPINCACHEITEM) *phCache;
if (NULL == pCache)
return;
TestDebugPrint(("PinCacheFlush: deleting cache\n"));
ZeroMemory(pCache->pbPin, pCache->cbPin);
ZeroMemory(pCache, sizeof(PINCACHEITEM));
CacheFree(pCache->pbPin);
CacheFree(pCache);
*phCache = NULL;
}
/**
* Function: PinCacheAdd
*/
DWORD PinCacheAdd(
IN PINCACHE_HANDLE *phCache,
IN PPINCACHE_PINS pPins,
IN PFN_VERIFYPIN_CALLBACK pfnVerifyPinCallback,
IN PVOID pvCallbackCtx)
{
HANDLE hThreadToken = 0;
TOKEN_STATISTICS stats;
DWORD dwError = ERROR_SUCCESS;
DWORD cb = 0;
PPINCACHEITEM pCache = (PPINCACHEITEM) *phCache;
DWORD cbPinToCache = 0;
PBYTE pbPinToCache = NULL;
BOOL fRefreshPin = FALSE;
static DWORD dwSleep = INIT_PIN_ATTACK_SLEEP;
static DWORD dwBadTries = 0;
if (NULL != pCache &&
(0 != memcmp(pCache->pbPin, pPins->pbCurrentPin, pCache->cbPin)
|| pPins->cbCurrentPin != pCache->cbPin))
{
// The caller hasn't supplied the correct Pin, according to the current
// cache state. Perhaps the user accidently typed the wrong pin, in which
// case the caller's logon LUID should be the same as the cached LUID.
// If the LUID's don't match, this could still be an attack or a legitimate
// attempt from a different logon with a mis-typed pin.
if (! OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken))
{
if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThreadToken))
{
dwError = GetLastError();
goto Ret;
}
}
if (! GetTokenInformation(
hThreadToken, TokenStatistics, &stats, sizeof(stats), &cb))
{
dwError = GetLastError();
goto Ret;
}
if (0 != memcmp(&stats.AuthenticationId, &pCache->luid, sizeof(LUID)) &&
++dwBadTries > MAX_FREE_BAD_TRIES)
{
// Current caller is a different luid from the cached one,
// and it's happened a few times already, so this call is suspicious.
// Start delaying.
DebugPrint(("PinCacheAdd: error - calling SleepEx(). Currently cached pin doesn't match\n"));
SleepEx(dwSleep, FALSE);
if (dwSleep < MAX_PIN_ATTACK_SLEEP)
dwSleep *= 2;
}
dwError = SCARD_W_WRONG_CHV;
goto Ret;
}
else if (0 != dwBadTries)
{
dwSleep = INIT_PIN_ATTACK_SLEEP;
dwBadTries = 0;
}
if (pPins->pbNewPin)
{
fRefreshPin = TRUE;
cbPinToCache = pPins->cbNewPin;
pbPinToCache = pPins->pbNewPin;
}
else
{
cbPinToCache = pPins->cbCurrentPin;
pbPinToCache = pPins->pbCurrentPin;
}
if (fRefreshPin || NULL == pCache)
{
// Check the pin
if (ERROR_SUCCESS != (dwError =
pfnVerifyPinCallback(pPins, pvCallbackCtx)))
{
TestDebugPrint(("PinCacheAdd: pfnVerifyPinCallback failed\n"));
return dwError;
}
}
if (! OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken))
{
if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThreadToken))
{
TestDebugPrint(("PinCacheAdd: failed to open thread or process token\n"));
dwError = GetLastError();
goto Ret;
}
}
if (! GetTokenInformation(
hThreadToken, TokenStatistics, &stats, sizeof(stats), &cb))
{
TestDebugPrint(("PinCacheAdd: GetTokenInformation failed\n"));
dwError = GetLastError();
goto Ret;
}
#if TEST_DEBUG
PCPrintBytes("PinCache LUID", (PBYTE) &stats.AuthenticationId, sizeof(LUID));
#endif
// Now the current ID is in stats.AuthenticationId
if (NULL == pCache)
{
TestDebugPrint(("PinCacheAdd: initializing new cache\n"));
// Initialize new cache
if (NULL == (pCache = (PPINCACHEITEM) CacheAlloc(sizeof(PINCACHEITEM))))
{
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto Ret;
}
CopyMemory(&pCache->luid, &stats.AuthenticationId, sizeof(LUID));
*phCache = (PINCACHE_HANDLE) pCache;
fRefreshPin = TRUE;
}
else
{
// Compare ID's
if (0 != memcmp(&stats.AuthenticationId, &pCache->luid, sizeof(LUID)))
{
// PIN's are the same, so cache the new ID
TestDebugPrint(("PinCacheAdd: same Pin, different Logon as cached values\n"));
CopyMemory(&pCache->luid, &stats.AuthenticationId, sizeof(LUID));
}
}
if (fRefreshPin)
{
if (pCache->pbPin)
CacheFree(pCache->pbPin);
pCache->cbPin = cbPinToCache;
if (NULL == (pCache->pbPin = (PBYTE) CacheAlloc(cbPinToCache)))
{
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto Ret;
}
CopyMemory(pCache->pbPin, pbPinToCache, cbPinToCache);
}
Ret:
if (hThreadToken)
CloseHandle(hThreadToken);
return dwError;
}
/**
* Function: PinCacheQuery
*/
DWORD PinCacheQuery(
IN PINCACHE_HANDLE hCache,
IN OUT PBYTE pbPin,
IN OUT PDWORD pcbPin)
{
HANDLE hThreadToken = 0;
TOKEN_STATISTICS stats;
DWORD dwError = ERROR_SUCCESS;
DWORD cb = 0;
PPINCACHEITEM pCache = (PPINCACHEITEM) hCache;
if (NULL == pCache)
{
*pcbPin = 0;
return ERROR_EMPTY;
}
if (! OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken))
{
if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThreadToken))
{
TestDebugPrint(("PinCacheQuery: failed to open thread or process token\n"));
dwError = GetLastError();
goto Ret;
}
}
if (! GetTokenInformation(
hThreadToken, TokenStatistics, &stats, sizeof(stats), &cb))
{
TestDebugPrint(("PinCacheQuery: GetTokenInformation failed\n"));
dwError = GetLastError();
goto Ret;
}
// Now the current ID is in stats.AuthenticationId
if (0 != memcmp(&stats.AuthenticationId, &pCache->luid, sizeof(LUID)))
{
// ID's are different, so ignore cache
TestDebugPrint(("PinCacheQuery: different Logon from cached value\n"));
*pcbPin = 0;
goto Ret;
}
// ID's are the same, so return cached PIN
TestDebugPrint(("PinCacheQuery: same Logon as cached value\n"));
if (NULL != pbPin)
{
if (*pcbPin >= pCache->cbPin)
CopyMemory(pbPin, pCache->pbPin, pCache->cbPin);
else
dwError = ERROR_MORE_DATA;
}
*pcbPin = pCache->cbPin;
Ret:
if (hThreadToken)
CloseHandle(hThreadToken);
return dwError;
}
/**
* Function: PinCachePresentPin
*/
DWORD PinCachePresentPin(
IN PINCACHE_HANDLE hCache,
IN PFN_VERIFYPIN_CALLBACK pfnVerifyPinCallback,
IN PVOID pvCallbackCtx)
{
HANDLE hThreadToken = 0;
TOKEN_STATISTICS stats;
DWORD cb = 0;
DWORD dwError = ERROR_SUCCESS;
PPINCACHEITEM pCache = (PPINCACHEITEM) hCache;
PINCACHE_PINS Pins;
if (NULL == pCache)
return ERROR_EMPTY;
if (! OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken))
{
if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThreadToken))
{
TestDebugPrint(("PinCachePresentPin: failed to open thread or process token\n"));
dwError = GetLastError();
goto Ret;
}
}
if (! GetTokenInformation(
hThreadToken, TokenStatistics, &stats, sizeof(stats), &cb))
{
TestDebugPrint(("PinCachePresentPin: GetTokenInformation failed\n"));
dwError = GetLastError();
goto Ret;
}
// Now the current ID is in stats.AuthenticationId
if (0 != memcmp(&stats.AuthenticationId, &pCache->luid, sizeof(LUID)))
{
// ID's are different, so ignore cache
TestDebugPrint(("PinCachePresentPin: different Logon from cached value\n"));
dwError = SCARD_W_CARD_NOT_AUTHENTICATED;
goto Ret;
}
// ID's are the same, so return cached PIN
TestDebugPrint(("PinCachePresentPin: same Logon as cached value\n"));
Pins.cbCurrentPin = pCache->cbPin;
Pins.pbCurrentPin = pCache->pbPin;
Pins.cbNewPin = 0;
Pins.pbNewPin = NULL;
dwError = (*pfnVerifyPinCallback)(&Pins, pvCallbackCtx);
Ret:
if (hThreadToken)
CloseHandle(hThreadToken);
return dwError;
}