windows-nt/Source/XPSP1/NT/net/rras/ras/ppp/eaptls/scard.c
2020-09-26 16:20:57 +08:00

566 lines
12 KiB
C

/*
Copyright (c) 1997, Microsoft Corporation, all rights reserved
Description:
Smart card helper functions.
History:
13 Dec 1997: Amanda Matlosz created original version.
12 May 1998: Vijay Baliga moved things around.
*/
#undef UNICODE
#include <winscard.h>
#include <wincrypt.h>
#include <rtutils.h>
VOID
EapTlsTrace(
IN CHAR* Format,
...
);
typedef
WINSCARDAPI LONG
(WINAPI *GETOPENCARDNAMEA)(
OPENCARDNAMEA*
);
GETOPENCARDNAMEA g_fnGetOpenCardNameA = NULL;
HINSTANCE g_hInstanceScardDlg = NULL;
/*
Returns:
Notes:
*/
DWORD
LoadScardDlgDll(
VOID
)
{
DWORD dwErr = NO_ERROR;
if (NULL == g_hInstanceScardDlg)
{
g_hInstanceScardDlg = LoadLibrary("scarddlg.dll");
}
if (NULL == g_hInstanceScardDlg)
{
dwErr = GetLastError();
EapTlsTrace("LoadLibrary(scarddlg.dll) failed and returned 0x%x",
dwErr);
goto LDone;
}
if (NULL == g_fnGetOpenCardNameA)
{
g_fnGetOpenCardNameA = (GETOPENCARDNAMEA)
GetProcAddress(g_hInstanceScardDlg, "GetOpenCardNameA");
}
if (NULL == g_fnGetOpenCardNameA)
{
dwErr = GetLastError();
EapTlsTrace("GetProcAddress(GetOpenCardNameA) failed and returned 0x%x",
dwErr);
goto LDone;
}
LDone:
return(dwErr);
}
/*
Returns:
Notes:
*/
VOID
FreeScardDlgDll(
VOID
)
{
if (NULL != g_hInstanceScardDlg)
{
FreeLibrary(g_hInstanceScardDlg);
g_hInstanceScardDlg = NULL;
g_fnGetOpenCardNameA = NULL;
}
}
/*
Returns:
Notes:
*/
DWORD
LocalCryptGetProvParamW(
IN HCRYPTPROV hProv,
IN DWORD dwParam,
OUT WCHAR** ppwsz
)
{
CHAR* psz = NULL;
WCHAR* pwszTemp = NULL;
DWORD cb;
int count;
BOOL fSuccess;
DWORD dwErr = NO_ERROR;
fSuccess = CryptGetProvParam(hProv, dwParam, NULL, &cb, 0);
if (!fSuccess)
{
dwErr = GetLastError();
EapTlsTrace("CryptGetProvParam failed and returned 0x%x", dwErr);
goto LDone;
}
psz = (CHAR*)LocalAlloc(LPTR, cb);
if (NULL == psz)
{
dwErr = GetLastError();
EapTlsTrace("LocalAlloc failed and returned %d", dwErr);
goto LDone;
}
fSuccess = CryptGetProvParam(hProv, dwParam, (BYTE*)psz, &cb, 0);
if (!fSuccess)
{
dwErr = GetLastError();
EapTlsTrace("CryptGetProvParam failed and returned 0x%x", dwErr);
goto LDone;
}
count = MultiByteToWideChar(CP_UTF8, 0, psz, -1, NULL, 0);
if (0 == count)
{
dwErr = GetLastError();
EapTlsTrace("MultiByteToWideChar(%s) failed: %d", psz, dwErr);
goto LDone;
}
pwszTemp = (WCHAR*)LocalAlloc(LPTR, count * sizeof(WCHAR));
if (NULL == pwszTemp)
{
dwErr = GetLastError();
EapTlsTrace("LocalAlloc failed and returned %d", dwErr);
goto LDone;
}
count = MultiByteToWideChar(CP_UTF8, 0, psz, -1, pwszTemp, count);
if (0 == count)
{
dwErr = GetLastError();
EapTlsTrace("MultiByteToWideChar(%s) failed: %d", psz, dwErr);
goto LDone;
}
*ppwsz = pwszTemp;
pwszTemp = NULL;
LDone:
LocalFree(pwszTemp);
LocalFree(psz);
return(dwErr);
}
/*
Returns:
Notes:
This internal routine generates a certificate context with (static)
keyprov info suitable for CertStore-based operations.
*/
DWORD
BuildCertContext(
IN HCRYPTPROV hProv,
IN BYTE* pbCert,
IN DWORD dwCertLen,
OUT PCCERT_CONTEXT* ppCertContext
)
{
CRYPT_KEY_PROV_INFO KeyProvInfo;
WCHAR* pwszContainerName = NULL;
WCHAR* pwszProviderName = NULL;
BOOL fCertContextCreated = FALSE;
BOOL fSuccess;
DWORD dwErr = NO_ERROR;
if ( (0 == hProv)
|| (NULL == pbCert)
|| (0 == dwCertLen))
{
dwErr = ERROR_INVALID_PARAMETER;
goto LDone;
}
RTASSERT(NULL != ppCertContext);
// Convert the certificate into a cert context.
*ppCertContext = CertCreateCertificateContext(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
pbCert, dwCertLen);
if (NULL == *ppCertContext)
{
dwErr = GetLastError();
EapTlsTrace("CertCreateCertificateContext failed and returned 0x%x",
dwErr);
goto LDone;
}
fCertContextCreated = TRUE;
// Associate cryptprovider w/ the private key property of this cert
dwErr = LocalCryptGetProvParamW(hProv, PP_CONTAINER, &pwszContainerName);
if (NO_ERROR != dwErr)
{
goto LDone;
}
EapTlsTrace("Container: %ws", pwszContainerName);
dwErr = LocalCryptGetProvParamW(hProv, PP_NAME, &pwszProviderName);
if (NO_ERROR != dwErr)
{
goto LDone;
}
EapTlsTrace("Provider: %ws", pwszProviderName);
// Set the cert context properties to reflect the prov info
KeyProvInfo.pwszContainerName = pwszContainerName;
KeyProvInfo.pwszProvName = pwszProviderName;
KeyProvInfo.dwProvType = PROV_RSA_FULL;
KeyProvInfo.dwFlags = 0;
KeyProvInfo.cProvParam = 0;
KeyProvInfo.rgProvParam = NULL;
KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE;
fSuccess = CertSetCertificateContextProperty(
*ppCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
0,
(void *)&KeyProvInfo);
if (!fSuccess)
{
dwErr = GetLastError();
EapTlsTrace("CertSetCertificateContextProperty failed and returned "
"0x%x", dwErr);
goto LDone;
}
LDone:
if (NO_ERROR != dwErr)
{
if (fCertContextCreated)
{
CertFreeCertificateContext(*ppCertContext);
}
*ppCertContext = NULL;
}
LocalFree(pwszContainerName);
LocalFree(pwszProviderName);
return(dwErr);
}
/*
Returns:
Notes:
The "Select Card" common dialog is raised, then the certificate is read
from the card, a certificate context complete with key prov info is
migrated to the cert store and also returned to the caller.
*/
DWORD
GetCertFromCard(
OUT PCCERT_CONTEXT* ppCertContext
)
{
CHAR* pszReader = NULL;
CHAR* pszCard = NULL;
DWORD cbReaderOrCard = MAX_PATH;
SCARDCONTEXT hContext = 0;
OPENCARDNAMEA OpenCardName;
CHAR* pszProviderName = NULL;
DWORD cchProvider;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
DWORD cbCertLen;
BYTE* pbCert = NULL;
HCERTSTORE hCertStore = NULL;
BOOL fSuccess;
LONG lErr;
DWORD dwErr = NO_ERROR;
EapTlsTrace("GetCertFromCard");
RTASSERT(NULL != ppCertContext);
dwErr = LoadScardDlgDll();
if (NO_ERROR != dwErr)
{
goto LDone;
}
pszReader = (BYTE*)LocalAlloc(LPTR, cbReaderOrCard);
if (NULL == pszReader)
{
dwErr = GetLastError();
EapTlsTrace("LocalAlloc failed and returned %d", dwErr);
goto LDone;
}
pszCard = (BYTE*)LocalAlloc(LPTR, cbReaderOrCard);
if (NULL == pszCard)
{
dwErr = GetLastError();
EapTlsTrace("LocalAlloc failed and returned %d", dwErr);
goto LDone;
}
lErr = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext);
if (SCARD_S_SUCCESS != lErr)
{
dwErr = lErr;
EapTlsTrace("SCardEstablishContext failed and returned 0x%x", dwErr);
goto LDone;
}
ZeroMemory(&OpenCardName, sizeof(OpenCardName));
OpenCardName.dwStructSize = sizeof(OpenCardName);
OpenCardName.hSCardContext = hContext;
OpenCardName.lpstrCardNames = NULL;
OpenCardName.lpstrRdr = pszReader;
OpenCardName.nMaxRdr = cbReaderOrCard;
OpenCardName.lpstrCard = pszCard;
OpenCardName.nMaxCard = cbReaderOrCard;
OpenCardName.lpstrTitle = "Select smartcard";
OpenCardName.dwFlags = SC_DLG_MINIMAL_UI;
OpenCardName.dwShareMode = 0;
OpenCardName.dwPreferredProtocols = 0;
lErr = g_fnGetOpenCardNameA(&OpenCardName);
if (SCARD_S_SUCCESS != lErr)
{
dwErr = lErr;
EapTlsTrace("GetOpenCardNameA failed and returned 0x%x", dwErr);
goto LDone;
}
EapTlsTrace("Reader: %s, Card: %s", pszReader, pszCard);
RTASSERT(0 == OpenCardName.hCardHandle);
pszProviderName = NULL;
cchProvider = SCARD_AUTOALLOCATE;
lErr = SCardGetCardTypeProviderNameA(hContext, OpenCardName.lpstrCard,
SCARD_PROVIDER_CSP, (CHAR*) &pszProviderName, &cchProvider);
if (SCARD_S_SUCCESS != lErr)
{
dwErr = lErr;
EapTlsTrace("SCardGetCardTypeProviderNameA failed and returned 0x%x",
dwErr);
goto LDone;
}
if (NULL != pszProviderName)
{
EapTlsTrace("Provider: %s", pszProviderName);
}
// Load the CSP
fSuccess = CryptAcquireContext(&hProv, NULL /* default container */,
pszProviderName, PROV_RSA_FULL,
CRYPT_SILENT /* or 0, to show CSP UI as needed */);
if (!fSuccess)
{
dwErr = GetLastError();
EapTlsTrace("CryptAcquireContext failed and returned 0x%x", dwErr);
goto LDone;
}
// Get the key handle.
fSuccess = CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKey);
if (!fSuccess)
{
dwErr = GetLastError();
EapTlsTrace("CryptGetUserKey failed and returned 0x%x", dwErr);
goto LDone;
}
// Upload the certificate.
cbCertLen = 0;
fSuccess = CryptGetKeyParam(hKey, KP_CERTIFICATE, NULL, &cbCertLen, 0);
if (!fSuccess)
{
dwErr = GetLastError();
if (ERROR_MORE_DATA != dwErr)
{
EapTlsTrace("CryptGetKeyParam(KP_CERTIFICATE) failed and returned "
"0x%x", dwErr);
goto LDone;
}
dwErr = NO_ERROR;
}
pbCert = (BYTE*)LocalAlloc(LPTR, cbCertLen);
if (NULL == pbCert)
{
dwErr = GetLastError();
EapTlsTrace("LocalAlloc failed and returned %d", dwErr);
goto LDone;
}
fSuccess = CryptGetKeyParam(hKey, KP_CERTIFICATE, pbCert, &cbCertLen, 0);
if (!fSuccess)
{
dwErr = GetLastError();
EapTlsTrace("CryptGetKeyParam(KP_CERTIFICATE) failed and returned "
"0x%x", dwErr);
goto LDone;
}
// Get the cert context...
dwErr = BuildCertContext(hProv, pbCert, cbCertLen, ppCertContext);
if (NO_ERROR != dwErr)
{
goto LDone;
}
// ...and migrate it to the My store
hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, hProv,
CERT_SYSTEM_STORE_CURRENT_USER, "MY");
if (NULL == hCertStore)
{
dwErr = GetLastError();
EapTlsTrace("CertOpenStore failed and returned 0x%x", dwErr);
goto LDone;
}
fSuccess = CertAddCertificateContextToStore(hCertStore, *ppCertContext,
CERT_STORE_ADD_REPLACE_EXISTING, NULL);
if (!fSuccess)
{
// This is OK. Don't return an error.
EapTlsTrace("CertAddCertificateContextToStore failed and returned 0x%x",
GetLastError());
}
LDone:
LocalFree(pszReader);
LocalFree(pszCard);
LocalFree(pbCert);
if (0 != hProv)
{
CryptReleaseContext(hProv, 0);
}
if (NULL != hCertStore)
{
CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
}
if (NULL != pszProviderName)
{
SCardFreeMemory(hContext, pszProviderName);
}
if (0 != hContext)
{
SCardReleaseContext(hContext);
}
if (0 != hKey)
{
CryptDestroyKey(hKey);
}
RTASSERT( (NO_ERROR == dwErr)
|| (NULL == *ppCertContext));
if ( (NULL == *ppCertContext)
&& (NO_ERROR == dwErr))
{
EapTlsTrace("CertContext is NULL. Returning E_FAIL");
dwErr = E_FAIL;
}
if ( (SCARD_W_CANCELLED_BY_USER == dwErr)
|| (SCARD_E_NO_READERS_AVAILABLE == dwErr))
{
dwErr = ERROR_CANCELLED;
}
return(dwErr);
}