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.
580 lines
15 KiB
580 lines
15 KiB
#include <windows.h>
|
|
#include <winscard.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <guiddef.h>
|
|
#include "basecsp.h"
|
|
#include "pincache.h"
|
|
#include "helpers.h"
|
|
|
|
extern HINSTANCE ghInstance;
|
|
PCARD_DATA pCardData = NULL;
|
|
SCARDCONTEXT hSCardContext = 0;
|
|
|
|
// global array to hold the provider name
|
|
WCHAR gszProvider[MAX_PATH];
|
|
|
|
#ifdef DBG
|
|
#define ERROUT(x) ShowError(x)
|
|
#else
|
|
#define ERROUT(x)
|
|
#endif
|
|
|
|
void ShowError(DWORD dwErr)
|
|
{
|
|
WCHAR sz[200];
|
|
swprintf(sz,L"Returned status %x\n",dwErr);
|
|
OutputDebugString(sz);
|
|
}
|
|
|
|
void DoConvertWideStringToLowerCase(WCHAR *pwsz)
|
|
{
|
|
WCHAR c;
|
|
if (NULL == pwsz) return;
|
|
while (NULL != (c = *pwsz))
|
|
{
|
|
*pwsz++= towlower(c);
|
|
}
|
|
}
|
|
|
|
// Accept an input buffer containing text, and convert it to binary
|
|
// The binary buffer is allocated new and must be freed by the caller
|
|
//
|
|
// The incoming data consists of hex digits and spaces. Hex digits are
|
|
// assembled into bytes as pairs, with the first digit becoming the
|
|
// most significant nybble. Spaces in input are discarded with no effect
|
|
// so that "12 34 5" becomes 0x12 0x34 0x5, as soes "1 2 3 4 5."
|
|
|
|
DWORD DoConvertBufferToBinary(BYTE *pIn, DWORD dwcbIn,
|
|
BYTE **pOut, DWORD *dwcbOut)
|
|
{
|
|
WCHAR szTemp[10];
|
|
WCHAR *pInput = (WCHAR *) pIn;
|
|
BYTE *pAlloc = NULL;
|
|
BYTE *pOutput = NULL;
|
|
DWORD dwOut = 0;
|
|
DWORD dwRet = -1;
|
|
BOOL fInabyte = FALSE;
|
|
BOOL fErr = FALSE;
|
|
BYTE b;
|
|
BYTE b2;
|
|
WCHAR c;
|
|
|
|
// Bag it if no data or output ptrs obviously invalid
|
|
if ((NULL == pIn) || (dwcbIn == 0)) goto Ret;
|
|
if ((NULL == pOut) || (NULL == dwcbOut)) goto Ret;
|
|
|
|
// count input characters
|
|
int iLen = wcslen(pInput);
|
|
|
|
if (iLen == 0) goto Ret;
|
|
|
|
// guaranteed to contain the output
|
|
pAlloc = (BYTE *)CspAllocH((iLen / 2) + 2);
|
|
pOutput = pAlloc;
|
|
|
|
for (int i = 0;i<iLen;i++)
|
|
{
|
|
|
|
c = pInput[i];
|
|
if (c == 0) break;
|
|
|
|
// skip over whitespace in the input
|
|
c = towupper(c);
|
|
if (c <= L' ')
|
|
{
|
|
fInabyte = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (!fInabyte)
|
|
{
|
|
b = 0;
|
|
}
|
|
b2 = 0;
|
|
|
|
// error on not legal hex character
|
|
if ( ((c < L'0') || (c > L'F')) ||
|
|
((c > L'9') && (c < L'A')) )
|
|
{
|
|
dwRet = -1;
|
|
*pOut = 0;
|
|
*dwcbOut = 0;
|
|
if (pAlloc) CspFreeH(pAlloc);
|
|
goto Ret;
|
|
}
|
|
else if (c <= L'9')
|
|
b2 = c - L'0';
|
|
else
|
|
b2 = c - L'A' + 10;
|
|
|
|
if (fInabyte)
|
|
{
|
|
b = (b << 4) + b2;
|
|
fInabyte = FALSE;
|
|
dwOut += 1;
|
|
*pOutput++ = b;
|
|
}
|
|
else
|
|
{
|
|
b = b2;
|
|
fInabyte = TRUE;
|
|
}
|
|
}
|
|
|
|
// Permit writing an unpaired terminating hex character to the tail of the binary as a 0x byte.
|
|
if (fInabyte)
|
|
{
|
|
fInabyte = FALSE;
|
|
dwOut += 1;
|
|
*pOutput++ = b;
|
|
}
|
|
|
|
dwRet = 0;
|
|
*pOut = pAlloc;
|
|
*dwcbOut = dwOut;
|
|
|
|
Ret:
|
|
ERROUT(dwRet);
|
|
return dwRet;
|
|
}
|
|
|
|
DWORD DoConvertBinaryToBuffer(BYTE *pIn, DWORD dwcbIn,
|
|
BYTE **pOut, DWORD *dwcbOut)
|
|
{
|
|
WCHAR *pAlloc = NULL;
|
|
WCHAR *pOutput = NULL;
|
|
DWORD dwOut = 0;
|
|
DWORD dwRet = -1;
|
|
BOOL fErr = FALSE;
|
|
BYTE b;
|
|
|
|
// Bag it if no data or output ptrs obviously invalid
|
|
if ((NULL == pIn) || (dwcbIn == 0)) goto Ret;
|
|
if ((NULL == pOut) || (NULL == dwcbOut)) goto Ret;
|
|
|
|
pAlloc = (WCHAR *)CspAllocH(((dwcbIn * 3) + 1) * sizeof(WCHAR));
|
|
if (NULL == pAlloc) goto Ret;
|
|
pOutput = pAlloc;
|
|
|
|
for (DWORD i = 0 ; i<dwcbIn ; i++)
|
|
{
|
|
b = pIn[i];
|
|
b &= 0xf0;
|
|
b = b>> 4;
|
|
b += L'0';
|
|
if (b > L'9') b += 7;
|
|
*pOutput++ = b;
|
|
|
|
b = pIn[i];
|
|
b &= 0x0f;
|
|
b += L'0';
|
|
if (b > L'9') b += 7;
|
|
*pOutput++ = b;
|
|
dwOut += 2;
|
|
|
|
// a space every 4 characters
|
|
if ((i > 0) && (((i+1) % 2) == 0)) *pOutput++ = L' ';
|
|
}
|
|
*pOutput = 0;
|
|
|
|
dwRet = 0;
|
|
*pOut = (BYTE *) pAlloc;
|
|
*dwcbOut = (pOutput - pAlloc -1) * sizeof(WCHAR);
|
|
|
|
Ret:
|
|
ERROUT(dwRet);
|
|
return dwRet;
|
|
}
|
|
|
|
//
|
|
// Find any card present in an attached reader using "minimal" scarddlg UI
|
|
//
|
|
DWORD GetCardHandleViaUI(
|
|
IN SCARDCONTEXT hSCardContext,
|
|
OUT SCARDHANDLE *phSCardHandle,
|
|
IN DWORD cchMatchedCard,
|
|
OUT LPWSTR wszMatchedCard,
|
|
IN DWORD cchMatchedReader,
|
|
OUT LPWSTR wszMatchedReader)
|
|
{
|
|
OPENCARDNAME_EXW ocnx;
|
|
DWORD dwSts = ERROR_SUCCESS;
|
|
|
|
memset(&ocnx, 0, sizeof(ocnx));
|
|
|
|
ocnx.dwStructSize = sizeof(ocnx);
|
|
ocnx.hSCardContext = hSCardContext;
|
|
ocnx.lpstrCard = wszMatchedCard;
|
|
ocnx.nMaxCard = cchMatchedCard;
|
|
ocnx.lpstrRdr = wszMatchedReader;
|
|
ocnx.nMaxRdr = cchMatchedReader;
|
|
ocnx.dwShareMode = SCARD_SHARE_SHARED;
|
|
ocnx.dwPreferredProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
|
|
ocnx.dwFlags = SC_DLG_MINIMAL_UI;
|
|
|
|
dwSts = SCardUIDlgSelectCardW(&ocnx);
|
|
|
|
*phSCardHandle = ocnx.hCardHandle;
|
|
|
|
return dwSts;
|
|
}
|
|
|
|
// Acquire a context for the target smart card
|
|
|
|
DWORD DoAcquireCardContext(void)
|
|
{
|
|
DWORD dwSts = ERROR_SUCCESS;
|
|
PFN_CARD_ACQUIRE_CONTEXT pfnCardAcquireContext = NULL;
|
|
SCARDHANDLE hSCardHandle = 0;
|
|
LPWSTR mszReaders = NULL;
|
|
DWORD cchReaders = SCARD_AUTOALLOCATE;
|
|
LPWSTR mszCards = NULL;
|
|
DWORD cchCards = SCARD_AUTOALLOCATE;
|
|
DWORD dwActiveProtocol = 0;
|
|
DWORD dwState = 0;
|
|
BYTE rgbAtr [32];
|
|
DWORD cbAtr = sizeof(rgbAtr);
|
|
LPWSTR pszProvider = NULL;
|
|
DWORD cchProvider = SCARD_AUTOALLOCATE;
|
|
HMODULE hMod = 0;
|
|
WCHAR wszMatchedCard[MAX_PATH];
|
|
WCHAR wszMatchedReader[MAX_PATH];
|
|
HMODULE hThis = (HMODULE) ghInstance; // this executable
|
|
|
|
memset(rgbAtr, 0, sizeof(rgbAtr));
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
dwSts = SCardEstablishContext(
|
|
SCARD_SCOPE_USER, NULL, NULL, &hSCardContext);
|
|
|
|
if (FAILED(dwSts))
|
|
goto Ret;
|
|
|
|
dwSts = GetCardHandleViaUI(
|
|
hSCardContext,
|
|
&hSCardHandle,
|
|
MAX_PATH,
|
|
wszMatchedCard,
|
|
MAX_PATH,
|
|
wszMatchedReader);
|
|
|
|
if (FAILED(dwSts))
|
|
goto Ret;
|
|
|
|
mszReaders = NULL;
|
|
cchReaders = SCARD_AUTOALLOCATE;
|
|
|
|
dwSts = SCardStatusW(
|
|
hSCardHandle,
|
|
(LPWSTR) (&mszReaders),
|
|
&cchReaders,
|
|
&dwState,
|
|
&dwActiveProtocol,
|
|
rgbAtr,
|
|
&cbAtr);
|
|
|
|
if (FAILED(dwSts))
|
|
goto Ret;
|
|
|
|
dwSts = SCardListCardsW(
|
|
hSCardContext,
|
|
rgbAtr,
|
|
NULL,
|
|
0,
|
|
(LPWSTR) (&mszCards),
|
|
&cchCards);
|
|
|
|
if (FAILED(dwSts))
|
|
goto Ret;
|
|
|
|
dwSts = SCardGetCardTypeProviderNameW(
|
|
hSCardContext,
|
|
mszCards,
|
|
SCARD_PROVIDER_CARD_MODULE,
|
|
(LPWSTR) (&pszProvider),
|
|
&cchProvider);
|
|
|
|
if (FAILED(dwSts))
|
|
goto Ret;
|
|
|
|
// Load the card module for the selected card
|
|
// acquire context and trade initializations
|
|
|
|
hMod = LoadLibraryW(pszProvider);
|
|
|
|
if (INVALID_HANDLE_VALUE == hMod)
|
|
{
|
|
dwSts = GetLastError();
|
|
goto Ret;
|
|
}
|
|
|
|
// This fails for an unsupported card type (no card module)
|
|
|
|
pfnCardAcquireContext =
|
|
(PFN_CARD_ACQUIRE_CONTEXT) GetProcAddress(
|
|
hMod,
|
|
"CardAcquireContext");
|
|
|
|
if (NULL == pfnCardAcquireContext)
|
|
{
|
|
dwSts = GetLastError();
|
|
goto Ret;
|
|
}
|
|
|
|
pCardData = (PCARD_DATA) CspAllocH(sizeof(CARD_DATA));
|
|
|
|
if (NULL == pCardData)
|
|
{
|
|
dwSts = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Ret;
|
|
}
|
|
|
|
memset(pCardData,0,sizeof(CARD_DATA));
|
|
pCardData->pbAtr = rgbAtr;
|
|
pCardData->cbAtr = cbAtr;
|
|
pCardData->pwszCardName = mszCards;
|
|
pCardData->dwVersion = CARD_DATA_CURRENT_VERSION;
|
|
pCardData->pfnCspAlloc = CspAllocH;
|
|
pCardData->pfnCspReAlloc = CspReAllocH;
|
|
pCardData->pfnCspFree = CspFreeH;
|
|
pCardData->pfnCspCacheAddFile = CspCacheAddFile;
|
|
pCardData->pfnCspCacheDeleteFile = CspCacheDeleteFile;
|
|
pCardData->pfnCspCacheLookupFile = CspCacheLookupFile;
|
|
pCardData->hScard = hSCardHandle;
|
|
hSCardHandle = 0;
|
|
|
|
// First, connect to the card
|
|
dwSts = pfnCardAcquireContext(pCardData, 0);
|
|
|
|
Ret:
|
|
if (FAILED(dwSts))
|
|
{
|
|
CspFreeH(pCardData);
|
|
}
|
|
ERROUT(dwSts);
|
|
return dwSts;
|
|
}
|
|
|
|
|
|
void DoLeaveCardContext(void)
|
|
{
|
|
if (pCardData)
|
|
{
|
|
if (pCardData->hScard)
|
|
SCardDisconnect(pCardData->hScard,SCARD_RESET_CARD);
|
|
CspFreeH(pCardData);
|
|
}
|
|
if (hSCardContext)
|
|
SCardReleaseContext(hSCardContext);
|
|
}
|
|
|
|
// Get the CardID, returned in a new allocation as an SZ string. It must be freed by the
|
|
// user. Is returned NULL on error.
|
|
|
|
DWORD DoGetCardId(
|
|
WCHAR **pSz)
|
|
{
|
|
DWORD dwSts = ERROR_SUCCESS;
|
|
WCHAR *pString = NULL;
|
|
GUID *pGuid;
|
|
DWORD ccGuid = 0;
|
|
|
|
dwSts = pCardData->pfnCardReadFile(pCardData,
|
|
wszCARD_IDENTIFIER_FILE_FULL_PATH,
|
|
0,
|
|
(PBYTE *) &pGuid,
|
|
&ccGuid);
|
|
|
|
if (ERROR_SUCCESS != dwSts)
|
|
goto Ret;
|
|
|
|
pString = (WCHAR *) CspAllocH(40 * sizeof(WCHAR));
|
|
|
|
if (pString == NULL)
|
|
{
|
|
*pSz = NULL;
|
|
dwSts = -1;
|
|
goto Ret;
|
|
}
|
|
|
|
DWORD ccSz = 40;
|
|
|
|
_snwprintf(pString, ccSz,L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
|
|
// first copy...
|
|
pGuid->Data1, pGuid->Data2, pGuid->Data3,
|
|
pGuid->Data4[0], pGuid->Data4[1], pGuid->Data4[2], pGuid->Data4[3],
|
|
pGuid->Data4[4], pGuid->Data4[5], pGuid->Data4[6], pGuid->Data4[7]);
|
|
|
|
*pSz = pString;
|
|
Ret:
|
|
return dwSts;
|
|
|
|
}
|
|
|
|
|
|
// Get a challenge buffer from the card. Render it as upper case BASE 64, and return it as a
|
|
// string to the caller
|
|
/*
|
|
DWORD WINAPI CardChangePin(
|
|
IN CARD_DATA *pCardData,
|
|
IN LPWSTR pwszUserId,
|
|
IN BYTE *pbCurrentAuthenticator,
|
|
IN DWORD dwcbCurrentAuthenticator,
|
|
IN BYTE *pbNewAuthenticator,
|
|
IN DWORD dwcbNewAuthenticator,
|
|
IN DWORD dwcRetryCount,
|
|
IN DWORD dwFlags,
|
|
OUT OPTIONAL DWORD *pdwcAttemptsRemaining
|
|
);
|
|
|
|
*/
|
|
|
|
DWORD DoInvalidatePinCache(
|
|
void)
|
|
{
|
|
DWORD dwSts = ERROR_SUCCESS;
|
|
DATA_BLOB dbCacheFile;
|
|
CARD_CACHE_FILE_FORMAT *pCache = NULL;
|
|
|
|
memset(&dbCacheFile, 0, sizeof(dbCacheFile));
|
|
|
|
dwSts = pCardData->pfnCardReadFile(
|
|
pCardData,
|
|
wszCACHE_FILE_FULL_PATH,
|
|
0,
|
|
&dbCacheFile.pbData,
|
|
&dbCacheFile.cbData);
|
|
|
|
if (ERROR_SUCCESS != dwSts)
|
|
goto Ret;
|
|
|
|
if (sizeof(CARD_CACHE_FILE_FORMAT) != dbCacheFile.cbData)
|
|
{
|
|
dwSts = ERROR_BAD_LENGTH;
|
|
goto Ret;
|
|
}
|
|
|
|
// We have the cache file contents at dbCacheFile.pbData
|
|
// Update the PinsFreshness value, and write it back
|
|
|
|
pCache = (CARD_CACHE_FILE_FORMAT *) dbCacheFile.pbData;
|
|
BYTE bPinFreshness = pCache->bPinsFreshness;
|
|
pCache->bPinsFreshness = bPinFreshness + 1;
|
|
|
|
dwSts = pCardData->pfnCardWriteFile(
|
|
pCardData,
|
|
wszCACHE_FILE_FULL_PATH,
|
|
0,
|
|
dbCacheFile.pbData,
|
|
dbCacheFile.cbData);
|
|
|
|
if (ERROR_SUCCESS != dwSts)
|
|
goto Ret;
|
|
|
|
Ret:
|
|
|
|
if (dbCacheFile.pbData)
|
|
CspFreeH(dbCacheFile.pbData);
|
|
return dwSts;
|
|
}
|
|
|
|
|
|
DWORD DoChangePin(WCHAR *pOldPin, WCHAR *pNewPin)
|
|
{
|
|
char AnsiOldPin[64];
|
|
char AnsiNewPin[64];
|
|
|
|
WCHAR szName[] = wszCARD_USER_USER;
|
|
//DoConvertWideStringToLowerCase(szName);
|
|
|
|
// change WCHAR PINs to ANSI
|
|
WideCharToMultiByte(GetConsoleOutputCP(),
|
|
0,
|
|
(WCHAR *) pOldPin,
|
|
-1,
|
|
AnsiOldPin,
|
|
64,
|
|
NULL,
|
|
NULL);
|
|
|
|
WideCharToMultiByte(GetConsoleOutputCP(),
|
|
0,
|
|
(WCHAR *) pNewPin,
|
|
-1,
|
|
AnsiNewPin,
|
|
64,
|
|
NULL,
|
|
NULL);
|
|
|
|
DWORD dwcbOldPin = strlen(AnsiOldPin);
|
|
DWORD dwcbNewPin = strlen( AnsiNewPin);
|
|
|
|
if (dwcbOldPin == 0) return -1;
|
|
|
|
DWORD dwSts = pCardData->pfnCardChangeAuthenticator(pCardData, szName,
|
|
(BYTE *)AnsiOldPin, dwcbOldPin,
|
|
(BYTE *)AnsiNewPin, dwcbNewPin,
|
|
0,
|
|
NULL);
|
|
ERROUT(dwSts);
|
|
if (0 == dwSts) DoInvalidatePinCache();
|
|
return dwSts;
|
|
}
|
|
|
|
// Get a challenge buffer from the card. Render it as upper case BASE 64, and return it as a
|
|
// string to the caller
|
|
DWORD DoGetChallenge(BYTE **pChallenge, DWORD *dwcbChallenge)
|
|
{
|
|
DWORD dwSts = pCardData->pfnCardGetChallenge(pCardData, pChallenge,dwcbChallenge);
|
|
if (FAILED(dwSts))
|
|
{
|
|
dwcbChallenge = 0;
|
|
return dwSts;
|
|
}
|
|
ERROUT(dwSts);
|
|
return dwSts;
|
|
}
|
|
|
|
// Perform the PIN unblock, calling down to the card module, and assuming challenge-response
|
|
// administrative authentication.
|
|
//
|
|
// The admin auth data is coming in as a case-unknown string from the user. Convert to binary,
|
|
// and pass the converted blob to pfnCardUnblockPin
|
|
|
|
DWORD DoCardUnblock(BYTE *pAuthData, DWORD dwcbAuthData,
|
|
BYTE *pPinData, DWORD dwcbPinData)
|
|
{
|
|
|
|
WCHAR szName[] = wszCARD_USER_USER;
|
|
//DoConvertWideStringToLowerCase(szName);
|
|
|
|
// Convert the incoming buffer
|
|
|
|
DWORD dwRet = pCardData->pfnCardUnblockPin(
|
|
pCardData,
|
|
szName,
|
|
pAuthData,
|
|
dwcbAuthData,
|
|
pPinData,
|
|
dwcbPinData,
|
|
0,
|
|
CARD_UNBLOCK_PIN_CHALLENGE_RESPONSE);
|
|
|
|
// this call should be unnecessary, as the unblock should deauth the admin
|
|
// I can't reset the card from the card module interface, so I'll ask the user to remove
|
|
// his card from the reader if deauth fails. In the real thing, I'll reset the card.
|
|
|
|
pCardData->pfnCardDeauthenticate(
|
|
pCardData,
|
|
wszCARD_USER_USER,0);
|
|
|
|
// Deallocate the buffer for the converted response
|
|
ERROUT(dwRet);
|
|
if (0 == dwRet) DoInvalidatePinCache();
|
|
return dwRet;
|
|
}
|