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

837 lines
25 KiB

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Microsoft Windows, Copyright (C) Microsoft Corporation, 2000
File: SmartCard.cpp
Content: Implementation of helper routines for accessing certificates in
smart card. Functions in this module require that the Smart Card Base
Component v1.1 to be installed.
History: 12-06-2001 dsie created
------------------------------------------------------------------------------*/
#include "StdAfx.h"
#include "CAPICOM.h"
#include "SmartCard.h"
//
// typedefs for SCardXXX APIs.
//
typedef WINSCARDAPI LONG (WINAPI * PFNSCARDESTABLISHCONTEXT) (
IN DWORD dwScope,
IN LPCVOID pvReserved1,
IN LPCVOID pvReserved2,
OUT LPSCARDCONTEXT phContext);
typedef WINSCARDAPI LONG (WINAPI * PFNSCARDLISTREADERSA) (
IN SCARDCONTEXT hContext,
IN LPCSTR mszGroups,
OUT LPSTR mszReaders,
IN OUT LPDWORD pcchReaders);
typedef WINSCARDAPI LONG (WINAPI * PFNSCARDGETSTATUSCHANGEA) (
IN SCARDCONTEXT hContext,
IN DWORD dwTimeout,
IN OUT LPSCARD_READERSTATE_A rgReaderStates,
IN DWORD cReaders);
typedef WINSCARDAPI LONG (WINAPI * PFNSCARDLISTCARDSA) (
IN SCARDCONTEXT hContext,
IN LPCBYTE pbAtr,
IN LPCGUID rgquidInterfaces,
IN DWORD cguidInterfaceCount,
OUT LPSTR mszCards,
IN OUT LPDWORD pcchCards);
typedef WINSCARDAPI LONG (WINAPI* PFNSCARDGETCARDTYPEPROVIDERNAMEA) (
IN SCARDCONTEXT hContext,
IN LPCSTR szCardName,
IN DWORD dwProviderId,
OUT LPSTR szProvider,
IN OUT LPDWORD pcchProvider);
typedef WINSCARDAPI LONG (WINAPI* PFNSCARDFREEMEMORY) (
IN SCARDCONTEXT hContext,
IN LPVOID pvMem);
typedef WINSCARDAPI LONG (WINAPI * PFNSCARDRELEASECONTEXT) (
IN SCARDCONTEXT hContext);
//
// Function pointer to SCardXXX APIs.
//
static PFNSCARDESTABLISHCONTEXT pfnSCardEstablishContext = NULL;
static PFNSCARDLISTREADERSA pfnSCardListReadersA = NULL;
static PFNSCARDGETSTATUSCHANGEA pfnSCardGetStatusChangeA = NULL;
static PFNSCARDLISTCARDSA pfnSCardListCardsA = NULL;
static PFNSCARDGETCARDTYPEPROVIDERNAMEA pfnSCardGetCardTypeProviderNameA = NULL;
static PFNSCARDFREEMEMORY pfnSCardFreeMemory = NULL;
static PFNSCARDRELEASECONTEXT pfnSCardReleaseContext = NULL;
////////////////////////////////////////////////////////////////////////////////
//
// Local functions.
//
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : AddCert
Synopsis : Add the specified certificate to the specified store.
Parameter: - IN LPCSTR szCSPName
CSP name string.
- IN LPCSTR szContainerName
Key container name string.
- IN DWORD dwKeySpec
AT_KEYEXCHANGZE or AT_SIGNATURE.
- IN LPBYTE pbEncodedCert
Pointer to encoded cert data to be added.
- IN DWORD cbEncodedCert
Length of encoded cert data.
- IN HCERTSTORE hCertStore
Handle of cert store where the cert will be added.
Remarks :
------------------------------------------------------------------------------*/
static HRESULT AddCert (IN LPCSTR szCSPName,
IN LPCSTR szContainerName,
IN DWORD dwKeySpec,
IN LPBYTE pbEncodedCert,
IN DWORD cbEncodedCert,
IN HCERTSTORE hCertStore)
{
HRESULT hr = S_OK;
PCCERT_CONTEXT pCertContext = NULL;
CComBSTR bstrCSPName;
CComBSTR bstrContainerName;
CRYPT_KEY_PROV_INFO KeyProvInfo;
DebugTrace("Entering AddCert().\n");
//
// Sanity check.
//
ATLASSERT(szCSPName);
ATLASSERT(szContainerName);
ATLASSERT(pbEncodedCert);
ATLASSERT(cbEncodedCert);
ATLASSERT(hCertStore);
//
// Create certificate context for the specified certificate.
//
if (!(pCertContext = ::CertCreateCertificateContext(CAPICOM_ASN_ENCODING,
pbEncodedCert,
cbEncodedCert)))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CertCreateCertificateContext() failed.\n", hr);
goto ErrorExit;
}
//
// Convert strings to UNICODE.
//
if (!(bstrCSPName = szCSPName))
{
hr = E_OUTOFMEMORY;
DebugTrace("Error [%#x]: bstrCSPName = szCSPName failed.\n", hr);
goto ErrorExit;
}
if (!(bstrContainerName = szContainerName))
{
hr = E_OUTOFMEMORY;
DebugTrace("Error [%#x]: bstrContainerName = szContainerName failed.\n", hr);
goto ErrorExit;
}
//
// Add the CSP & key container info. This is used by CAPI to load the
// CSP and find the keyset when the user indicates this certificate.
//
::ZeroMemory((LPVOID) &KeyProvInfo, sizeof(CRYPT_KEY_PROV_INFO));
KeyProvInfo.pwszContainerName = (LPWSTR) bstrContainerName;
KeyProvInfo.pwszProvName = (LPWSTR) bstrCSPName;
KeyProvInfo.dwProvType = PROV_RSA_FULL;
KeyProvInfo.dwFlags = 0;
KeyProvInfo.dwKeySpec = dwKeySpec;
if (!::CertSetCertificateContextProperty(pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
0,
(const void *) &KeyProvInfo))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CertSetCertificateContextProperty() failed.\n", hr);
goto ErrorExit;
}
//
// Put the cert in the store!
//
if (!::CertAddCertificateContextToStore(hCertStore,
pCertContext,
CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, // or CERT_STORE_ADD_NEW
NULL))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CertCreateCertificateContext() failed.\n", hr);
goto ErrorExit;
}
CommonExit:
//
// Free resources.
//
if (pCertContext != NULL)
{
CertFreeCertificateContext(pCertContext);
}
DebugTrace("Leaving AddCert().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
goto CommonExit;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : GetCert
Synopsis : Get the cert from the specified CSP for the specified key type.
Parameter: - IN HCRYPTPROV hCryptProv
Crypto context returned by CryptAcquireContext().
- IN DWORD dwKeySpec
AT_KEYEXCHANGZE or AT_SIGNATURE.
- OUT LPBYTE * ppbEncodedCert
Ponter to pointer to encoded cert data. Upon success, the buffer
is automatically allocated and must be later freed by
CoTaskMemFree().
- OUT DWORD * pcbEncodedCert
Pointer to encoded cert data length. Upon success, receive the
length of the encoded cert data.
Remarks :
------------------------------------------------------------------------------*/
static HRESULT GetCert (IN HCRYPTPROV hCryptProv,
IN DWORD dwKeySpec,
OUT LPBYTE * ppbEncodedCert,
OUT DWORD * pcbEncodedCert)
{
HRESULT hr = S_OK;
HCRYPTKEY hCryptKey = NULL;
LPBYTE pbEncodedCert = NULL;
DWORD cbEncodedCert = 0;
DebugTrace("Entering GetCert().\n");
//
// Sanity check.
//
ATLASSERT(hCryptProv);
ATLASSERT(ppbEncodedCert);
ATLASSERT(pcbEncodedCert);
//
// Get key handle.
//
if (!::CryptGetUserKey(hCryptProv, dwKeySpec, &hCryptKey))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptGetUserKey() failed.\n", hr);
goto ErrorExit;
}
//
// Query certificate data length.
//
if (!::CryptGetKeyParam(hCryptKey,
KP_CERTIFICATE,
NULL, // NULL to query certificate data length
&cbEncodedCert,
0))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptGetKeyParam() failed.\n", hr);
goto ErrorExit;
}
//
// Allocate memory for certificate data.
//
if (!(pbEncodedCert = (LPBYTE) ::CoTaskMemAlloc(cbEncodedCert)))
{
hr = E_OUTOFMEMORY;
DebugTrace("Error [%#x]: CoTaskMemAlloc() failed.\n", hr);
goto ErrorExit;
}
//
// Now read the certificate data.
//
if (!::CryptGetKeyParam(hCryptKey,
KP_CERTIFICATE,
pbEncodedCert,
&cbEncodedCert,
0))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptGetKeyParam() failed.\n", hr);
goto ErrorExit;
}
//
// Return encoded cert to caller.
//
*ppbEncodedCert = pbEncodedCert;
*pcbEncodedCert = cbEncodedCert;
CommonExit:
//
// Free resources.
//
if (hCryptKey)
{
::CryptDestroyKey(hCryptKey);
}
DebugTrace("Leaving GetCert().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
//
// Free resources.
//
if (pbEncodedCert)
{
::CoTaskMemFree((LPVOID) pbEncodedCert);
}
goto CommonExit;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : PropCert
Synopsis : Propagate the digital certificate associated with the specified
CSP and container name to the soecified store.
Parameter: - IN LPCSTR szCSPName
Pointer to Crypto Service Provider name string.
- IN LPCTSTR szContainerName
Pointer to key container name string.
- IN HCERTSTORE hCertStore
Handle of store to add the certificate to.
Remarks :
------------------------------------------------------------------------------*/
static HRESULT PropCert (IN LPCSTR szCSPName,
IN LPCSTR szContainerName,
IN HCERTSTORE hCertStore)
{
HRESULT hr = S_OK;
HCRYPTPROV hCryptProv = NULL;
DWORD rgdwKeys[] = {AT_KEYEXCHANGE, AT_SIGNATURE};
DWORD cbEncodedCert = 0;
LPBYTE pbEncodedCert = NULL;
DWORD i;
DebugTrace("Entering PropCert().\n");
//
// Sanity check.
//
ATLASSERT(szCSPName);
ATLASSERT(szContainerName);
ATLASSERT(hCertStore);
//
// Obtain the crypto context.
//
// CRYPT_SILENT forces the CSP to raise no UI. The fully qualified
// container name indicates which reader to connect to, so the
// user should not be prompted to insert or select a card.
//
if (!::CryptAcquireContextA(&hCryptProv,
szContainerName,
szCSPName,
PROV_RSA_FULL,
CRYPT_SILENT))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptAcquireContextA() failed.\n", hr);
goto ErrorExit;
}
//
// For each key pair found in the smart card, store the corresponding
// digital certificate to the specified store.
//
for (i = 0; i < ARRAYSIZE(rgdwKeys); i++)
{
//
// Get the certificate data.
//
if (FAILED(hr = ::GetCert(hCryptProv,
rgdwKeys[i],
&pbEncodedCert,
&cbEncodedCert)))
{
if (HRESULT_FROM_WIN32(NTE_NO_KEY) == hr)
{
//
// We are OK if there is no key of such type.
//
hr = S_OK;
continue;
}
DebugTrace("Error [%#x]: GetCert() failed.\n", hr);
goto ErrorExit;
}
//
// Add the certificate to the specified store.
//
if (FAILED(hr = ::AddCert(szCSPName,
szContainerName,
rgdwKeys[i],
pbEncodedCert,
cbEncodedCert,
hCertStore)))
{
DebugTrace("Error [%#x]: AddCert() failed.\n", hr);
goto ErrorExit;
}
//
// Free resources.
//
if (pbEncodedCert)
{
::CoTaskMemFree((LPVOID) pbEncodedCert), pbEncodedCert = NULL;
}
}
CommonExit:
//
// Free resources.
//
if (pbEncodedCert)
{
::CoTaskMemFree((LPVOID) pbEncodedCert);
}
if (hCryptProv)
{
::CryptReleaseContext(hCryptProv, 0);
}
DebugTrace("Leaving PropCert().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
goto CommonExit;
}
////////////////////////////////////////////////////////////////////////////////
//
// Exported functions.
//
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : LoadFromSmartCard
Synopsis : Load all certificates from all smart card readers.
Parameter: HCERTSTORE hCertStore - Certificate store handle of store to
receive all the certificates.
Remark :
------------------------------------------------------------------------------*/
HRESULT LoadFromSmartCard (HCERTSTORE hCertStore)
{
HRESULT hr = S_OK;
LONG lResult = 0;
DWORD dwNumReaders = 0;
DWORD dwAutoAllocate = SCARD_AUTOALLOCATE;
SCARDCONTEXT hContext = NULL;
LPSTR szReaderName = NULL;
LPSTR mszReaderNames = NULL;
LPSTR szCardName = NULL;
LPSTR szCSPName = NULL;
LPSTR szContainerName = NULL;
HMODULE hWinSCardDll = NULL;
LPSCARD_READERSTATE lpReaderStates = NULL;
DebugTrace("Entering LoadFromSmartCard().\n");
//
// Sanity check.
//
ATLASSERT(hCertStore);
//
// Load WinSCard.dll.
//
if (!(hWinSCardDll = ::LoadLibrary("WinSCard.dll")))
{
hr = CAPICOM_E_NOT_SUPPORTED;
DebugTrace("Error [%#x]: Smart Card Base Component (WinSCard.dll) not installed.\n", hr);
goto ErrorExit;
}
//
// Load all SCard APIs used.
//
if (!(pfnSCardEstablishContext = (PFNSCARDESTABLISHCONTEXT)
::GetProcAddress(hWinSCardDll, "SCardEstablishContext")))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardEstablishContext.\n", hr);
goto ErrorExit;
}
if (!(pfnSCardListReadersA = (PFNSCARDLISTREADERSA)
::GetProcAddress(hWinSCardDll, "SCardListReadersA")))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardListReadersA.\n", hr);
goto ErrorExit;
}
if (!(pfnSCardGetStatusChangeA = (PFNSCARDGETSTATUSCHANGEA)
::GetProcAddress(hWinSCardDll, "SCardGetStatusChangeA")))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardStatusChangeA.\n", hr);
goto ErrorExit;
}
if (!(pfnSCardListCardsA = (PFNSCARDLISTCARDSA)
::GetProcAddress(hWinSCardDll, "SCardListCardsA")))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardListCardsA.\n", hr);
goto ErrorExit;
}
if (!(pfnSCardGetCardTypeProviderNameA = (PFNSCARDGETCARDTYPEPROVIDERNAMEA)
::GetProcAddress(hWinSCardDll, "SCardGetCardTypeProviderNameA")))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardGetCardTypeProviderNameA.\n", hr);
goto ErrorExit;
}
if (!(pfnSCardFreeMemory = (PFNSCARDFREEMEMORY)
::GetProcAddress(hWinSCardDll, "SCardFreeMemory")))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardFreeMemory.\n", hr);
goto ErrorExit;
}
if (!(pfnSCardReleaseContext = (PFNSCARDRELEASECONTEXT)
::GetProcAddress(hWinSCardDll, "SCardReleaseContext")))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardReleaseContext.\n", hr);
goto ErrorExit;
}
//
// Establish context with the resource manager.
//
if (SCARD_S_SUCCESS != (lResult = pfnSCardEstablishContext(SCARD_SCOPE_USER,
NULL,
NULL,
&hContext)))
{
hr = HRESULT_FROM_WIN32(lResult);
DebugTrace("Error [%#x]: SCardEstablishContext() failed.\n", hr);
goto ErrorExit;
}
//
// Get the list of all reader(s).
// Note: The buffer is automatically allocated and must be freed
// by SCardFreeMemory().
//
if (SCARD_S_SUCCESS != (lResult = pfnSCardListReadersA(hContext,
NULL,
(LPSTR) &mszReaderNames,
&dwAutoAllocate)))
{
hr = HRESULT_FROM_WIN32(lResult);
DebugTrace("Error [%#x]: SCardListReadersA() failed.\n", hr);
goto ErrorExit;
}
//
// Count number of readers.
//
for (dwNumReaders = 0, szReaderName = mszReaderNames; *szReaderName; dwNumReaders++)
{
szReaderName += ::strlen(szReaderName) + 1;
}
//
// Nothing to do if no reader.
//
if (0 < dwNumReaders)
{
DWORD i;
//
// Allocate memory for SCARD_READERSTATE array.
//
if (!(lpReaderStates = (LPSCARD_READERSTATE)
::CoTaskMemAlloc(dwNumReaders * sizeof(SCARD_READERSTATE))))
{
hr = E_OUTOFMEMORY;
DebugTrace("Error [%#x]: CoTaskMemAlloc() failed.\n", hr);
goto ErrorExit;
}
//
// Prepare state array.
//
::ZeroMemory((LPVOID) lpReaderStates, dwNumReaders * sizeof(SCARD_READERSTATE));
for (i = 0, szReaderName = mszReaderNames; i < dwNumReaders; i++)
{
lpReaderStates[i].szReader = (LPCSTR) szReaderName;
lpReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE;
szReaderName += ::strlen(szReaderName) + 1;
}
//
// Initialize card status.
//
if (SCARD_S_SUCCESS != (lResult = pfnSCardGetStatusChangeA(hContext,
INFINITE,
lpReaderStates,
dwNumReaders)))
{
hr = HRESULT_FROM_WIN32(lResult);
DebugTrace("Error [%#x]: SCardGetStatusChangeA() failed.\n", hr);
goto ErrorExit;
}
//
// For each card found, find the proper CSP and propagate the
// certificate(s) to the specified store.
//
for (i = 0; i < dwNumReaders; i++)
{
//
// Card in this reader?
//
if (!(lpReaderStates[i].dwEventState & SCARD_STATE_PRESENT))
{
//
// No card in this reader.
//
continue;
}
//
// Get card name.
//
dwAutoAllocate = SCARD_AUTOALLOCATE;
if (SCARD_S_SUCCESS != (lResult = pfnSCardListCardsA(hContext,
lpReaderStates[i].rgbAtr,
NULL,
0,
(LPSTR) &szCardName,
&dwAutoAllocate)))
{
hr = HRESULT_FROM_WIN32(lResult);
DebugTrace("Error [%#x]: SCardListCardsA() failed.\n", hr);
goto ErrorExit;
}
//
// Get card's CSP name.
//
dwAutoAllocate = SCARD_AUTOALLOCATE;
if (SCARD_S_SUCCESS != (lResult = pfnSCardGetCardTypeProviderNameA(hContext,
szCardName,
SCARD_PROVIDER_CSP,
(LPSTR) &szCSPName,
&dwAutoAllocate)))
{
hr = HRESULT_FROM_WIN32(lResult);
DebugTrace("Error [%#x]: SCardGetCardTypeProviderNameA() failed.\n", hr);
goto ErrorExit;
}
//
// Prepare fully qualified container name.
//
if (!(szContainerName = (LPSTR) ::CoTaskMemAlloc(sizeof("\\\\.\\") + 1 +
::strlen(lpReaderStates[i].szReader))))
{
hr = E_OUTOFMEMORY;
DebugTrace("Error [%#x]: CoTaskMemAlloc() failed.\n", hr);
goto ErrorExit;
}
wsprintfA(szContainerName, "\\\\.\\%s\\", lpReaderStates[i].szReader);
//
// Propagate the cert.
//
if (FAILED(hr = ::PropCert(szCSPName, szContainerName, hCertStore)))
{
DebugTrace("Error [%#x]: PropCert() failed.\n", hr);
goto ErrorExit;
}
//
// Free resources.
//
if (szContainerName)
{
::CoTaskMemFree((LPVOID) szContainerName), szContainerName = NULL;
}
if (szCSPName)
{
pfnSCardFreeMemory(hContext, (LPVOID) szCSPName), szCSPName = NULL;
}
if (szCardName)
{
pfnSCardFreeMemory(hContext, (LPVOID) szCardName), szCardName = NULL;
}
}
}
CommonExit:
//
// Free resource.
//
if (szContainerName)
{
::CoTaskMemFree((LPVOID) szContainerName);
}
if (szCSPName)
{
pfnSCardFreeMemory(hContext, (LPVOID) szCSPName);
}
if (szCardName)
{
pfnSCardFreeMemory(hContext, (LPVOID) szCardName);
}
if (lpReaderStates)
{
::CoTaskMemFree((LPVOID) lpReaderStates);
}
if (mszReaderNames)
{
pfnSCardFreeMemory(hContext, (LPVOID) mszReaderNames);
}
if (hContext)
{
pfnSCardReleaseContext(hContext);
}
if (hWinSCardDll)
{
::FreeLibrary(hWinSCardDll);
}
DebugTrace("Leaving LoadFromSmartCard().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
goto CommonExit;
}